From c2571c7faf10b93d14ccbc150849ed498ff7ac02 Mon Sep 17 00:00:00 2001 From: Jason Stoltzfus Date: Mon, 18 Oct 2021 12:20:19 -0400 Subject: [PATCH 001/204] [App Search] Added a History tab to the Automated Curation detail view (#115090) --- .../curation/automated_curation.test.tsx | 37 ++++++++++-- .../curations/curation/automated_curation.tsx | 32 +++++++++-- .../curations/curation/history.test.tsx | 23 ++++++++ .../components/curations/curation/history.tsx | 57 +++++++++++++++++++ 4 files changed, 140 insertions(+), 9 deletions(-) create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/history.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/history.tsx diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/automated_curation.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/automated_curation.test.tsx index 2cee5bbbec80b..944d8315452b0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/automated_curation.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/automated_curation.test.tsx @@ -8,6 +8,7 @@ import '../../../../__mocks__/shallow_useeffect.mock'; import { setMockActions, setMockValues } from '../../../../__mocks__/kea_logic'; import { mockUseParams } from '../../../../__mocks__/react_router'; + import '../../../__mocks__/engine_logic.mock'; import React from 'react'; @@ -27,6 +28,7 @@ import { CurationLogic } from './curation_logic'; import { DeleteCurationButton } from './delete_curation_button'; import { PromotedDocuments, OrganicDocuments } from './documents'; +import { History } from './history'; describe('AutomatedCuration', () => { const values = { @@ -39,6 +41,7 @@ describe('AutomatedCuration', () => { suggestion: { status: 'applied', }, + queries: ['foo'], }, activeQuery: 'query A', isAutomated: true, @@ -61,20 +64,46 @@ describe('AutomatedCuration', () => { expect(wrapper.is(AppSearchPageTemplate)); expect(wrapper.find(PromotedDocuments)).toHaveLength(1); expect(wrapper.find(OrganicDocuments)).toHaveLength(1); + expect(wrapper.find(History)).toHaveLength(0); }); - it('includes a static tab group', () => { + it('includes tabs', () => { const wrapper = shallow(); - const tabs = getPageHeaderTabs(wrapper).find(EuiTab); + let tabs = getPageHeaderTabs(wrapper).find(EuiTab); - expect(tabs).toHaveLength(2); + expect(tabs).toHaveLength(3); - expect(tabs.at(0).prop('onClick')).toBeUndefined(); expect(tabs.at(0).prop('isSelected')).toBe(true); expect(tabs.at(1).prop('onClick')).toBeUndefined(); expect(tabs.at(1).prop('isSelected')).toBe(false); expect(tabs.at(1).prop('disabled')).toBe(true); + + expect(tabs.at(2).prop('isSelected')).toBe(false); + + // Clicking on the History tab shows the history view + tabs.at(2).simulate('click'); + + tabs = getPageHeaderTabs(wrapper).find(EuiTab); + + expect(tabs.at(0).prop('isSelected')).toBe(false); + expect(tabs.at(2).prop('isSelected')).toBe(true); + + expect(wrapper.find(PromotedDocuments)).toHaveLength(0); + expect(wrapper.find(OrganicDocuments)).toHaveLength(0); + expect(wrapper.find(History)).toHaveLength(1); + + // Clicking back to the Promoted tab shows promoted documents + tabs.at(0).simulate('click'); + + tabs = getPageHeaderTabs(wrapper).find(EuiTab); + + expect(tabs.at(0).prop('isSelected')).toBe(true); + expect(tabs.at(2).prop('isSelected')).toBe(false); + + expect(wrapper.find(PromotedDocuments)).toHaveLength(1); + expect(wrapper.find(OrganicDocuments)).toHaveLength(1); + expect(wrapper.find(History)).toHaveLength(0); }); it('initializes CurationLogic with a curationId prop from URL param', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/automated_curation.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/automated_curation.tsx index fa34fa071b855..276b40ba88677 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/automated_curation.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/automated_curation.tsx @@ -5,15 +5,18 @@ * 2.0. */ -import React from 'react'; +import React, { useState } from 'react'; import { useParams } from 'react-router-dom'; import { useValues, useActions } from 'kea'; import { EuiButton, EuiBadge, EuiLoadingSpinner, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { EngineLogic } from '../../engine'; import { AppSearchPageTemplate } from '../../layout'; import { AutomatedIcon } from '../components/automated_icon'; + import { AUTOMATED_LABEL, COVERT_TO_MANUAL_BUTTON_LABEL, @@ -26,19 +29,25 @@ import { HIDDEN_DOCUMENTS_TITLE, PROMOTED_DOCUMENTS_TITLE } from './constants'; import { CurationLogic } from './curation_logic'; import { DeleteCurationButton } from './delete_curation_button'; import { PromotedDocuments, OrganicDocuments } from './documents'; +import { History } from './history'; + +const PROMOTED = 'promoted'; +const HISTORY = 'history'; export const AutomatedCuration: React.FC = () => { const { curationId } = useParams<{ curationId: string }>(); const logic = CurationLogic({ curationId }); const { convertToManual } = useActions(logic); const { activeQuery, dataLoading, queries, curation } = useValues(logic); + const { engineName } = useValues(EngineLogic); + const [selectedPageTab, setSelectedPageTab] = useState(PROMOTED); - // This tab group is meant to visually mirror the dynamic group of tags in the ManualCuration component const pageTabs = [ { label: PROMOTED_DOCUMENTS_TITLE, append: {curation.promoted.length}, - isSelected: true, + isSelected: selectedPageTab === PROMOTED, + onClick: () => setSelectedPageTab(PROMOTED), }, { label: HIDDEN_DOCUMENTS_TITLE, @@ -46,6 +55,16 @@ export const AutomatedCuration: React.FC = () => { isSelected: false, disabled: true, }, + { + label: i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.curation.detail.historyButtonLabel', + { + defaultMessage: 'History', + } + ), + isSelected: selectedPageTab === HISTORY, + onClick: () => setSelectedPageTab(HISTORY), + }, ]; return ( @@ -83,8 +102,11 @@ export const AutomatedCuration: React.FC = () => { }} isLoading={dataLoading} > - - + {selectedPageTab === PROMOTED && } + {selectedPageTab === PROMOTED && } + {selectedPageTab === HISTORY && ( + + )} ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/history.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/history.test.tsx new file mode 100644 index 0000000000000..a7f83fb0c61d9 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/history.test.tsx @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { shallow } from 'enzyme'; + +import { EntSearchLogStream } from '../../../../shared/log_stream'; + +import { History } from './history'; + +describe('History', () => { + it('renders', () => { + const wrapper = shallow(); + expect(wrapper.find(EntSearchLogStream).prop('query')).toEqual( + 'appsearch.search_relevance_suggestions.query: some text and event.kind: event and event.dataset: search-relevance-suggestions and appsearch.search_relevance_suggestions.engine: foo and event.action: curation_suggestion' + ); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/history.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/history.tsx new file mode 100644 index 0000000000000..744141372469c --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/history.tsx @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { i18n } from '@kbn/i18n'; + +import { EntSearchLogStream } from '../../../../shared/log_stream'; +import { DataPanel } from '../../data_panel'; + +interface Props { + query: string; + engineName: string; +} + +export const History: React.FC = ({ query, engineName }) => { + const filters = [ + `appsearch.search_relevance_suggestions.query: ${query}`, + 'event.kind: event', + 'event.dataset: search-relevance-suggestions', + `appsearch.search_relevance_suggestions.engine: ${engineName}`, + 'event.action: curation_suggestion', + ]; + + return ( + + {i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.curation.detail.historyTableTitle', + { + defaultMessage: 'Automated curation changes', + } + )} + + } + subtitle={i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.curation.detail.historyTableDescription', + { + defaultMessage: 'A detailed log of recent changes to your automated curation.', + } + )} + hasBorder + > + + + ); +}; From 3ebfb029a2b7a774bd777602fbdb193748e2a379 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ester=20Mart=C3=AD=20Vilaseca?= Date: Mon, 18 Oct 2021 18:24:01 +0200 Subject: [PATCH 002/204] [Stack monitoring] Remove angular (#115063) * Remove angular * Fix translations * convert insetupmode to boolean * remove license service Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/monitoring/kibana.json | 1 - .../monitoring/public/alerts/badge.tsx | 2 +- .../monitoring/public/angular/app_modules.ts | 246 ------------- .../public/angular/helpers/routes.ts | 39 --- .../public/angular/helpers/utils.ts | 45 --- .../monitoring/public/angular/index.ts | 83 ----- .../public/angular/providers/private.js | 193 ----------- .../application/pages/logstash/pipelines.tsx | 8 +- .../pages/no_data/no_data_page.tsx | 2 +- .../application/pages/page_template.tsx | 2 +- .../public/application/setup_mode/index.ts | 2 +- .../application/setup_mode/setup_mode.tsx | 203 ----------- .../setup_mode/setup_mode_renderer.js | 2 +- .../elasticsearch/ml_job_listing/index.js | 171 ---------- .../public/directives/main/index.html | 323 ------------------ .../public/directives/main/index.js | 275 --------------- .../public/directives/main/index.scss | 3 - .../main/monitoring_main_controller.test.js | 286 ---------------- .../monitoring/public/lib/setup_mode.test.js | 2 +- .../monitoring/public/lib/setup_mode.tsx | 113 +++--- x-pack/plugins/monitoring/public/plugin.ts | 48 +-- .../monitoring/public/services/breadcrumbs.js | 214 ------------ .../public/services/breadcrumbs.test.js | 166 --------- .../monitoring/public/services/clusters.js | 59 ---- .../public/services/enable_alerts_modal.js | 51 --- .../monitoring/public/services/executor.js | 130 ------- .../monitoring/public/services/features.js | 47 --- .../monitoring/public/services/license.js | 52 --- .../monitoring/public/services/title.js | 26 -- .../public/views/access_denied/index.html | 44 --- .../public/views/access_denied/index.js | 44 --- x-pack/plugins/monitoring/public/views/all.js | 39 --- .../public/views/apm/instance/index.html | 8 - .../public/views/apm/instance/index.js | 74 ---- .../public/views/apm/instances/index.html | 7 - .../public/views/apm/instances/index.js | 92 ----- .../public/views/apm/overview/index.html | 7 - .../public/views/apm/overview/index.js | 58 ---- .../public/views/base_controller.js | 271 --------------- .../public/views/base_eui_table_controller.js | 135 -------- .../public/views/base_table_controller.js | 53 --- .../public/views/beats/beat/get_page_data.js | 32 -- .../public/views/beats/beat/index.html | 11 - .../public/views/beats/beat/index.js | 75 ---- .../views/beats/listing/get_page_data.js | 31 -- .../public/views/beats/listing/index.html | 7 - .../public/views/beats/listing/index.js | 89 ----- .../views/beats/overview/get_page_data.js | 31 -- .../public/views/beats/overview/index.html | 7 - .../public/views/beats/overview/index.js | 62 ---- .../public/views/cluster/listing/index.html | 3 - .../public/views/cluster/listing/index.js | 100 ------ .../public/views/cluster/overview/index.html | 3 - .../public/views/cluster/overview/index.js | 96 ------ .../views/elasticsearch/ccr/get_page_data.js | 31 -- .../public/views/elasticsearch/ccr/index.html | 7 - .../public/views/elasticsearch/ccr/index.js | 79 ----- .../elasticsearch/ccr/shard/get_page_data.js | 32 -- .../views/elasticsearch/ccr/shard/index.html | 8 - .../views/elasticsearch/ccr/shard/index.js | 110 ------ .../elasticsearch/index/advanced/index.html | 8 - .../elasticsearch/index/advanced/index.js | 124 ------- .../views/elasticsearch/index/index.html | 9 - .../public/views/elasticsearch/index/index.js | 148 -------- .../views/elasticsearch/indices/index.html | 8 - .../views/elasticsearch/indices/index.js | 120 ------- .../elasticsearch/ml_jobs/get_page_data.js | 30 -- .../views/elasticsearch/ml_jobs/index.html | 9 - .../views/elasticsearch/ml_jobs/index.js | 51 --- .../elasticsearch/node/advanced/index.html | 11 - .../elasticsearch/node/advanced/index.js | 135 -------- .../views/elasticsearch/node/get_page_data.js | 36 -- .../views/elasticsearch/node/index.html | 11 - .../public/views/elasticsearch/node/index.js | 155 --------- .../views/elasticsearch/nodes/index.html | 8 - .../public/views/elasticsearch/nodes/index.js | 149 -------- .../elasticsearch/overview/controller.js | 100 ------ .../views/elasticsearch/overview/index.html | 8 - .../views/elasticsearch/overview/index.js | 24 -- .../plugins/monitoring/public/views/index.js | 10 - .../public/views/kibana/instance/index.html | 8 - .../public/views/kibana/instance/index.js | 168 --------- .../views/kibana/instances/get_page_data.js | 31 -- .../public/views/kibana/instances/index.html | 7 - .../public/views/kibana/instances/index.js | 95 ------ .../public/views/kibana/overview/index.html | 7 - .../public/views/kibana/overview/index.js | 117 ------- .../public/views/license/controller.js | 79 ----- .../public/views/license/index.html | 3 - .../monitoring/public/views/license/index.js | 24 -- .../public/views/loading/index.html | 5 - .../monitoring/public/views/loading/index.js | 78 ----- .../views/logstash/node/advanced/index.html | 9 - .../views/logstash/node/advanced/index.js | 149 -------- .../public/views/logstash/node/index.html | 9 - .../public/views/logstash/node/index.js | 147 -------- .../views/logstash/node/pipelines/index.html | 8 - .../views/logstash/node/pipelines/index.js | 135 -------- .../views/logstash/nodes/get_page_data.js | 31 -- .../public/views/logstash/nodes/index.html | 3 - .../public/views/logstash/nodes/index.js | 89 ----- .../public/views/logstash/overview/index.html | 3 - .../public/views/logstash/overview/index.js | 81 ----- .../public/views/logstash/pipeline/index.html | 12 - .../public/views/logstash/pipeline/index.js | 181 ---------- .../views/logstash/pipelines/index.html | 7 - .../public/views/logstash/pipelines/index.js | 130 ------- .../public/views/no_data/controller.js | 102 ------ .../public/views/no_data/index.html | 5 - .../monitoring/public/views/no_data/index.js | 15 - .../public/views/no_data/model_updater.js | 38 --- .../views/no_data/model_updater.test.js | 55 --- .../translations/translations/ja-JP.json | 21 +- .../translations/translations/zh-CN.json | 21 +- 114 files changed, 68 insertions(+), 7399 deletions(-) delete mode 100644 x-pack/plugins/monitoring/public/angular/app_modules.ts delete mode 100644 x-pack/plugins/monitoring/public/angular/helpers/routes.ts delete mode 100644 x-pack/plugins/monitoring/public/angular/helpers/utils.ts delete mode 100644 x-pack/plugins/monitoring/public/angular/index.ts delete mode 100644 x-pack/plugins/monitoring/public/angular/providers/private.js delete mode 100644 x-pack/plugins/monitoring/public/application/setup_mode/setup_mode.tsx delete mode 100644 x-pack/plugins/monitoring/public/directives/elasticsearch/ml_job_listing/index.js delete mode 100644 x-pack/plugins/monitoring/public/directives/main/index.html delete mode 100644 x-pack/plugins/monitoring/public/directives/main/index.js delete mode 100644 x-pack/plugins/monitoring/public/directives/main/index.scss delete mode 100644 x-pack/plugins/monitoring/public/directives/main/monitoring_main_controller.test.js delete mode 100644 x-pack/plugins/monitoring/public/services/breadcrumbs.js delete mode 100644 x-pack/plugins/monitoring/public/services/breadcrumbs.test.js delete mode 100644 x-pack/plugins/monitoring/public/services/clusters.js delete mode 100644 x-pack/plugins/monitoring/public/services/enable_alerts_modal.js delete mode 100644 x-pack/plugins/monitoring/public/services/executor.js delete mode 100644 x-pack/plugins/monitoring/public/services/features.js delete mode 100644 x-pack/plugins/monitoring/public/services/license.js delete mode 100644 x-pack/plugins/monitoring/public/services/title.js delete mode 100644 x-pack/plugins/monitoring/public/views/access_denied/index.html delete mode 100644 x-pack/plugins/monitoring/public/views/access_denied/index.js delete mode 100644 x-pack/plugins/monitoring/public/views/all.js delete mode 100644 x-pack/plugins/monitoring/public/views/apm/instance/index.html delete mode 100644 x-pack/plugins/monitoring/public/views/apm/instance/index.js delete mode 100644 x-pack/plugins/monitoring/public/views/apm/instances/index.html delete mode 100644 x-pack/plugins/monitoring/public/views/apm/instances/index.js delete mode 100644 x-pack/plugins/monitoring/public/views/apm/overview/index.html delete mode 100644 x-pack/plugins/monitoring/public/views/apm/overview/index.js delete mode 100644 x-pack/plugins/monitoring/public/views/base_controller.js delete mode 100644 x-pack/plugins/monitoring/public/views/base_eui_table_controller.js delete mode 100644 x-pack/plugins/monitoring/public/views/base_table_controller.js delete mode 100644 x-pack/plugins/monitoring/public/views/beats/beat/get_page_data.js delete mode 100644 x-pack/plugins/monitoring/public/views/beats/beat/index.html delete mode 100644 x-pack/plugins/monitoring/public/views/beats/beat/index.js delete mode 100644 x-pack/plugins/monitoring/public/views/beats/listing/get_page_data.js delete mode 100644 x-pack/plugins/monitoring/public/views/beats/listing/index.html delete mode 100644 x-pack/plugins/monitoring/public/views/beats/listing/index.js delete mode 100644 x-pack/plugins/monitoring/public/views/beats/overview/get_page_data.js delete mode 100644 x-pack/plugins/monitoring/public/views/beats/overview/index.html delete mode 100644 x-pack/plugins/monitoring/public/views/beats/overview/index.js delete mode 100644 x-pack/plugins/monitoring/public/views/cluster/listing/index.html delete mode 100644 x-pack/plugins/monitoring/public/views/cluster/listing/index.js delete mode 100644 x-pack/plugins/monitoring/public/views/cluster/overview/index.html delete mode 100644 x-pack/plugins/monitoring/public/views/cluster/overview/index.js delete mode 100644 x-pack/plugins/monitoring/public/views/elasticsearch/ccr/get_page_data.js delete mode 100644 x-pack/plugins/monitoring/public/views/elasticsearch/ccr/index.html delete mode 100644 x-pack/plugins/monitoring/public/views/elasticsearch/ccr/index.js delete mode 100644 x-pack/plugins/monitoring/public/views/elasticsearch/ccr/shard/get_page_data.js delete mode 100644 x-pack/plugins/monitoring/public/views/elasticsearch/ccr/shard/index.html delete mode 100644 x-pack/plugins/monitoring/public/views/elasticsearch/ccr/shard/index.js delete mode 100644 x-pack/plugins/monitoring/public/views/elasticsearch/index/advanced/index.html delete mode 100644 x-pack/plugins/monitoring/public/views/elasticsearch/index/advanced/index.js delete mode 100644 x-pack/plugins/monitoring/public/views/elasticsearch/index/index.html delete mode 100644 x-pack/plugins/monitoring/public/views/elasticsearch/index/index.js delete mode 100644 x-pack/plugins/monitoring/public/views/elasticsearch/indices/index.html delete mode 100644 x-pack/plugins/monitoring/public/views/elasticsearch/indices/index.js delete mode 100644 x-pack/plugins/monitoring/public/views/elasticsearch/ml_jobs/get_page_data.js delete mode 100644 x-pack/plugins/monitoring/public/views/elasticsearch/ml_jobs/index.html delete mode 100644 x-pack/plugins/monitoring/public/views/elasticsearch/ml_jobs/index.js delete mode 100644 x-pack/plugins/monitoring/public/views/elasticsearch/node/advanced/index.html delete mode 100644 x-pack/plugins/monitoring/public/views/elasticsearch/node/advanced/index.js delete mode 100644 x-pack/plugins/monitoring/public/views/elasticsearch/node/get_page_data.js delete mode 100644 x-pack/plugins/monitoring/public/views/elasticsearch/node/index.html delete mode 100644 x-pack/plugins/monitoring/public/views/elasticsearch/node/index.js delete mode 100644 x-pack/plugins/monitoring/public/views/elasticsearch/nodes/index.html delete mode 100644 x-pack/plugins/monitoring/public/views/elasticsearch/nodes/index.js delete mode 100644 x-pack/plugins/monitoring/public/views/elasticsearch/overview/controller.js delete mode 100644 x-pack/plugins/monitoring/public/views/elasticsearch/overview/index.html delete mode 100644 x-pack/plugins/monitoring/public/views/elasticsearch/overview/index.js delete mode 100644 x-pack/plugins/monitoring/public/views/index.js delete mode 100644 x-pack/plugins/monitoring/public/views/kibana/instance/index.html delete mode 100644 x-pack/plugins/monitoring/public/views/kibana/instance/index.js delete mode 100644 x-pack/plugins/monitoring/public/views/kibana/instances/get_page_data.js delete mode 100644 x-pack/plugins/monitoring/public/views/kibana/instances/index.html delete mode 100644 x-pack/plugins/monitoring/public/views/kibana/instances/index.js delete mode 100644 x-pack/plugins/monitoring/public/views/kibana/overview/index.html delete mode 100644 x-pack/plugins/monitoring/public/views/kibana/overview/index.js delete mode 100644 x-pack/plugins/monitoring/public/views/license/controller.js delete mode 100644 x-pack/plugins/monitoring/public/views/license/index.html delete mode 100644 x-pack/plugins/monitoring/public/views/license/index.js delete mode 100644 x-pack/plugins/monitoring/public/views/loading/index.html delete mode 100644 x-pack/plugins/monitoring/public/views/loading/index.js delete mode 100644 x-pack/plugins/monitoring/public/views/logstash/node/advanced/index.html delete mode 100644 x-pack/plugins/monitoring/public/views/logstash/node/advanced/index.js delete mode 100644 x-pack/plugins/monitoring/public/views/logstash/node/index.html delete mode 100644 x-pack/plugins/monitoring/public/views/logstash/node/index.js delete mode 100644 x-pack/plugins/monitoring/public/views/logstash/node/pipelines/index.html delete mode 100644 x-pack/plugins/monitoring/public/views/logstash/node/pipelines/index.js delete mode 100644 x-pack/plugins/monitoring/public/views/logstash/nodes/get_page_data.js delete mode 100644 x-pack/plugins/monitoring/public/views/logstash/nodes/index.html delete mode 100644 x-pack/plugins/monitoring/public/views/logstash/nodes/index.js delete mode 100644 x-pack/plugins/monitoring/public/views/logstash/overview/index.html delete mode 100644 x-pack/plugins/monitoring/public/views/logstash/overview/index.js delete mode 100644 x-pack/plugins/monitoring/public/views/logstash/pipeline/index.html delete mode 100644 x-pack/plugins/monitoring/public/views/logstash/pipeline/index.js delete mode 100644 x-pack/plugins/monitoring/public/views/logstash/pipelines/index.html delete mode 100644 x-pack/plugins/monitoring/public/views/logstash/pipelines/index.js delete mode 100644 x-pack/plugins/monitoring/public/views/no_data/controller.js delete mode 100644 x-pack/plugins/monitoring/public/views/no_data/index.html delete mode 100644 x-pack/plugins/monitoring/public/views/no_data/index.js delete mode 100644 x-pack/plugins/monitoring/public/views/no_data/model_updater.js delete mode 100644 x-pack/plugins/monitoring/public/views/no_data/model_updater.test.js diff --git a/x-pack/plugins/monitoring/kibana.json b/x-pack/plugins/monitoring/kibana.json index 4f8e1c0bdbae4..bc0cf47181585 100644 --- a/x-pack/plugins/monitoring/kibana.json +++ b/x-pack/plugins/monitoring/kibana.json @@ -25,7 +25,6 @@ "home", "alerting", "kibanaReact", - "licenseManagement", "kibanaLegacy" ] } diff --git a/x-pack/plugins/monitoring/public/alerts/badge.tsx b/x-pack/plugins/monitoring/public/alerts/badge.tsx index 6b1c8c5085565..22bffb5d62b19 100644 --- a/x-pack/plugins/monitoring/public/alerts/badge.tsx +++ b/x-pack/plugins/monitoring/public/alerts/badge.tsx @@ -73,7 +73,7 @@ export const AlertsBadge: React.FC = (props: Props) => { const groupByType = GROUP_BY_NODE; const panels = showByNode ? getAlertPanelsByNode(PANEL_TITLE, alerts, stateFilter) - : getAlertPanelsByCategory(PANEL_TITLE, inSetupMode, alerts, stateFilter); + : getAlertPanelsByCategory(PANEL_TITLE, !!inSetupMode, alerts, stateFilter); if (panels.length && !inSetupMode && panels[0].items) { panels[0].items.push( ...[ diff --git a/x-pack/plugins/monitoring/public/angular/app_modules.ts b/x-pack/plugins/monitoring/public/angular/app_modules.ts deleted file mode 100644 index 6ded0bce51d4b..0000000000000 --- a/x-pack/plugins/monitoring/public/angular/app_modules.ts +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import angular, { IWindowService } from 'angular'; -import '../views/all'; -// required for `ngSanitize` angular module -import 'angular-sanitize'; -import 'angular-route'; -import '../index.scss'; -import { upperFirst } from 'lodash'; -import { CoreStart } from 'kibana/public'; -import { i18nDirective, i18nFilter, I18nProvider } from './angular_i18n'; -import { Storage } from '../../../../../src/plugins/kibana_utils/public'; -import { createTopNavDirective, createTopNavHelper } from './top_nav'; -import { MonitoringStartPluginDependencies } from '../types'; -import { GlobalState } from '../url_state'; -import { getSafeForExternalLink } from '../lib/get_safe_for_external_link'; - -// @ts-ignore -import { formatMetric, formatNumber } from '../lib/format_number'; -// @ts-ignore -import { extractIp } from '../lib/extract_ip'; -// @ts-ignore -import { PrivateProvider } from './providers/private'; -// @ts-ignore -import { breadcrumbsProvider } from '../services/breadcrumbs'; -// @ts-ignore -import { monitoringClustersProvider } from '../services/clusters'; -// @ts-ignore -import { executorProvider } from '../services/executor'; -// @ts-ignore -import { featuresProvider } from '../services/features'; -// @ts-ignore -import { licenseProvider } from '../services/license'; -// @ts-ignore -import { titleProvider } from '../services/title'; -// @ts-ignore -import { enableAlertsModalProvider } from '../services/enable_alerts_modal'; -// @ts-ignore -import { monitoringMlListingProvider } from '../directives/elasticsearch/ml_job_listing'; -// @ts-ignore -import { monitoringMainProvider } from '../directives/main'; - -export const appModuleName = 'monitoring'; - -type IPrivate = (provider: (...injectable: unknown[]) => T) => T; - -const thirdPartyAngularDependencies = ['ngSanitize', 'ngRoute', 'react']; - -export const localAppModule = ({ - core, - data: { query }, - navigation, - externalConfig, -}: MonitoringStartPluginDependencies) => { - createLocalI18nModule(); - createLocalPrivateModule(); - createLocalStorage(); - createLocalConfigModule(core); - createLocalStateModule(query, core.notifications.toasts); - createLocalTopNavModule(navigation); - createHrefModule(core); - createMonitoringAppServices(); - createMonitoringAppDirectives(); - createMonitoringAppConfigConstants(externalConfig); - createMonitoringAppFilters(); - - const appModule = angular.module(appModuleName, [ - ...thirdPartyAngularDependencies, - 'monitoring/I18n', - 'monitoring/Private', - 'monitoring/Storage', - 'monitoring/Config', - 'monitoring/State', - 'monitoring/TopNav', - 'monitoring/href', - 'monitoring/constants', - 'monitoring/services', - 'monitoring/filters', - 'monitoring/directives', - ]); - return appModule; -}; - -function createMonitoringAppConfigConstants( - keys: MonitoringStartPluginDependencies['externalConfig'] -) { - let constantsModule = angular.module('monitoring/constants', []); - keys.map(([key, value]) => (constantsModule = constantsModule.constant(key as string, value))); -} - -function createLocalStateModule( - query: MonitoringStartPluginDependencies['data']['query'], - toasts: MonitoringStartPluginDependencies['core']['notifications']['toasts'] -) { - angular - .module('monitoring/State', ['monitoring/Private']) - .service( - 'globalState', - function ( - Private: IPrivate, - $rootScope: ng.IRootScopeService, - $location: ng.ILocationService - ) { - function GlobalStateProvider(this: any) { - const state = new GlobalState(query, toasts, $rootScope, $location, this); - const initialState: any = state.getState(); - for (const key in initialState) { - if (!initialState.hasOwnProperty(key)) { - continue; - } - this[key] = initialState[key]; - } - this.save = () => { - const newState = { ...this }; - delete newState.save; - state.setState(newState); - }; - } - return Private(GlobalStateProvider); - } - ); -} - -function createMonitoringAppServices() { - angular - .module('monitoring/services', ['monitoring/Private']) - .service('breadcrumbs', function (Private: IPrivate) { - return Private(breadcrumbsProvider); - }) - .service('monitoringClusters', function (Private: IPrivate) { - return Private(monitoringClustersProvider); - }) - .service('$executor', function (Private: IPrivate) { - return Private(executorProvider); - }) - .service('features', function (Private: IPrivate) { - return Private(featuresProvider); - }) - .service('enableAlertsModal', function (Private: IPrivate) { - return Private(enableAlertsModalProvider); - }) - .service('license', function (Private: IPrivate) { - return Private(licenseProvider); - }) - .service('title', function (Private: IPrivate) { - return Private(titleProvider); - }); -} - -function createMonitoringAppDirectives() { - angular - .module('monitoring/directives', []) - .directive('monitoringMlListing', monitoringMlListingProvider) - .directive('monitoringMain', monitoringMainProvider); -} - -function createMonitoringAppFilters() { - angular - .module('monitoring/filters', []) - .filter('capitalize', function () { - return function (input: string) { - return upperFirst(input?.toLowerCase()); - }; - }) - .filter('formatNumber', function () { - return formatNumber; - }) - .filter('formatMetric', function () { - return formatMetric; - }) - .filter('extractIp', function () { - return extractIp; - }); -} - -function createLocalConfigModule(core: MonitoringStartPluginDependencies['core']) { - angular.module('monitoring/Config', []).provider('config', function () { - return { - $get: () => ({ - get: (key: string) => core.uiSettings?.get(key), - }), - }; - }); -} - -function createLocalStorage() { - angular - .module('monitoring/Storage', []) - .service('localStorage', function ($window: IWindowService) { - return new Storage($window.localStorage); - }) - .service('sessionStorage', function ($window: IWindowService) { - return new Storage($window.sessionStorage); - }) - .service('sessionTimeout', function () { - return {}; - }); -} - -function createLocalPrivateModule() { - angular.module('monitoring/Private', []).provider('Private', PrivateProvider); -} - -function createLocalTopNavModule({ ui }: MonitoringStartPluginDependencies['navigation']) { - angular - .module('monitoring/TopNav', ['react']) - .directive('kbnTopNav', createTopNavDirective) - .directive('kbnTopNavHelper', createTopNavHelper(ui)); -} - -function createLocalI18nModule() { - angular - .module('monitoring/I18n', []) - .provider('i18n', I18nProvider) - .filter('i18n', i18nFilter) - .directive('i18nId', i18nDirective); -} - -function createHrefModule(core: CoreStart) { - const name: string = 'kbnHref'; - angular.module('monitoring/href', []).directive(name, function () { - return { - restrict: 'A', - link: { - pre: (_$scope, _$el, $attr) => { - $attr.$observe(name, (val) => { - if (val) { - const url = getSafeForExternalLink(val as string); - $attr.$set('href', core.http.basePath.prepend(url)); - } - }); - - _$scope.$on('$locationChangeSuccess', () => { - const url = getSafeForExternalLink($attr.href as string); - $attr.$set('href', core.http.basePath.prepend(url)); - }); - }, - }, - }; - }); -} diff --git a/x-pack/plugins/monitoring/public/angular/helpers/routes.ts b/x-pack/plugins/monitoring/public/angular/helpers/routes.ts deleted file mode 100644 index 2579e522882a2..0000000000000 --- a/x-pack/plugins/monitoring/public/angular/helpers/routes.ts +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -type RouteObject = [string, { reloadOnSearch: boolean }]; -interface Redirect { - redirectTo: string; -} - -class Routes { - private routes: RouteObject[] = []; - public redirect?: Redirect = { redirectTo: '/no-data' }; - - public when = (...args: RouteObject) => { - const [, routeOptions] = args; - routeOptions.reloadOnSearch = false; - this.routes.push(args); - return this; - }; - - public otherwise = (redirect: Redirect) => { - this.redirect = redirect; - return this; - }; - - public addToProvider = ($routeProvider: any) => { - this.routes.forEach((args) => { - $routeProvider.when.apply(this, args); - }); - - if (this.redirect) { - $routeProvider.otherwise(this.redirect); - } - }; -} -export const uiRoutes = new Routes(); diff --git a/x-pack/plugins/monitoring/public/angular/helpers/utils.ts b/x-pack/plugins/monitoring/public/angular/helpers/utils.ts deleted file mode 100644 index 32184ad71ed8d..0000000000000 --- a/x-pack/plugins/monitoring/public/angular/helpers/utils.ts +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { IScope } from 'angular'; -import * as Rx from 'rxjs'; - -/** - * Subscribe to an observable at a $scope, ensuring that the digest cycle - * is run for subscriber hooks and routing errors to fatalError if not handled. - */ -export const subscribeWithScope = ( - $scope: IScope, - observable: Rx.Observable, - observer?: Rx.PartialObserver -) => { - return observable.subscribe({ - next(value) { - if (observer && observer.next) { - $scope.$applyAsync(() => observer.next!(value)); - } - }, - error(error) { - $scope.$applyAsync(() => { - if (observer && observer.error) { - observer.error(error); - } else { - throw new Error( - `Uncaught error in subscribeWithScope(): ${ - error ? error.stack || error.message : error - }` - ); - } - }); - }, - complete() { - if (observer && observer.complete) { - $scope.$applyAsync(() => observer.complete!()); - } - }, - }); -}; diff --git a/x-pack/plugins/monitoring/public/angular/index.ts b/x-pack/plugins/monitoring/public/angular/index.ts deleted file mode 100644 index 1a655fc1ee256..0000000000000 --- a/x-pack/plugins/monitoring/public/angular/index.ts +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import angular, { IModule } from 'angular'; -import { uiRoutes } from './helpers/routes'; -import { Legacy } from '../legacy_shims'; -import { configureAppAngularModule } from '../angular/top_nav'; -import { localAppModule, appModuleName } from './app_modules'; -import { APP_WRAPPER_CLASS } from '../../../../../src/core/public'; - -import { MonitoringStartPluginDependencies } from '../types'; - -export class AngularApp { - private injector?: angular.auto.IInjectorService; - - constructor(deps: MonitoringStartPluginDependencies) { - const { - core, - element, - data, - navigation, - isCloud, - pluginInitializerContext, - externalConfig, - triggersActionsUi, - usageCollection, - appMountParameters, - } = deps; - const app: IModule = localAppModule(deps); - app.run(($injector: angular.auto.IInjectorService) => { - this.injector = $injector; - Legacy.init( - { - core, - element, - data, - navigation, - isCloud, - pluginInitializerContext, - externalConfig, - triggersActionsUi, - usageCollection, - appMountParameters, - }, - this.injector - ); - }); - - app.config(($routeProvider: unknown) => uiRoutes.addToProvider($routeProvider)); - - const np = { core, env: pluginInitializerContext.env }; - configureAppAngularModule(app, np, true); - const appElement = document.createElement('div'); - appElement.setAttribute('style', 'height: 100%'); - appElement.innerHTML = '
'; - - if (!element.classList.contains(APP_WRAPPER_CLASS)) { - element.classList.add(APP_WRAPPER_CLASS); - } - - angular.bootstrap(appElement, [appModuleName]); - angular.element(element).append(appElement); - } - - public destroy = () => { - if (this.injector) { - this.injector.get('$rootScope').$destroy(); - } - }; - - public applyScope = () => { - if (!this.injector) { - return; - } - - const rootScope = this.injector.get('$rootScope'); - rootScope.$applyAsync(); - }; -} diff --git a/x-pack/plugins/monitoring/public/angular/providers/private.js b/x-pack/plugins/monitoring/public/angular/providers/private.js deleted file mode 100644 index 018e2d7d41840..0000000000000 --- a/x-pack/plugins/monitoring/public/angular/providers/private.js +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -/** - * # `Private()` - * Private module loader, used to merge angular and require js dependency styles - * by allowing a require.js module to export a single provider function that will - * create a value used within an angular application. This provider can declare - * angular dependencies by listing them as arguments, and can be require additional - * Private modules. - * - * ## Define a private module provider: - * ```js - * export default function PingProvider($http) { - * this.ping = function () { - * return $http.head('/health-check'); - * }; - * }; - * ``` - * - * ## Require a private module: - * ```js - * export default function ServerHealthProvider(Private, Promise) { - * let ping = Private(require('ui/ping')); - * return { - * check: Promise.method(function () { - * let attempts = 0; - * return (function attempt() { - * attempts += 1; - * return ping.ping() - * .catch(function (err) { - * if (attempts < 3) return attempt(); - * }) - * }()) - * .then(function () { - * return true; - * }) - * .catch(function () { - * return false; - * }); - * }) - * } - * }; - * ``` - * - * # `Private.stub(provider, newInstance)` - * `Private.stub()` replaces the instance of a module with another value. This is all we have needed until now. - * - * ```js - * beforeEach(inject(function ($injector, Private) { - * Private.stub( - * // since this module just exports a function, we need to change - * // what Private returns in order to modify it's behavior - * require('ui/agg_response/hierarchical/_build_split'), - * sinon.stub().returns(fakeSplit) - * ); - * })); - * ``` - * - * # `Private.swap(oldProvider, newProvider)` - * This new method does an 1-for-1 swap of module providers, unlike `stub()` which replaces a modules instance. - * Pass the module you want to swap out, and the one it should be replaced with, then profit. - * - * Note: even though this example shows `swap()` being called in a config - * function, it can be called from anywhere. It is particularly useful - * in this scenario though. - * - * ```js - * beforeEach(module('kibana', function (PrivateProvider) { - * PrivateProvider.swap( - * function StubbedRedirectProvider($decorate) { - * // $decorate is a function that will instantiate the original module when called - * return sinon.spy($decorate()); - * } - * ); - * })); - * ``` - * - * @param {[type]} prov [description] - */ -import { partial, uniqueId, isObject } from 'lodash'; - -const nextId = partial(uniqueId, 'privateProvider#'); - -function name(fn) { - return fn.name || fn.toString().split('\n').shift(); -} - -export function PrivateProvider() { - const provider = this; - - // one cache/swaps per Provider - const cache = {}; - const swaps = {}; - - // return the uniq id for this function - function identify(fn) { - if (typeof fn !== 'function') { - throw new TypeError('Expected private module "' + fn + '" to be a function'); - } - - if (fn.$$id) return fn.$$id; - else return (fn.$$id = nextId()); - } - - provider.stub = function (fn, instance) { - cache[identify(fn)] = instance; - return instance; - }; - - provider.swap = function (fn, prov) { - const id = identify(fn); - swaps[id] = prov; - }; - - provider.$get = [ - '$injector', - function PrivateFactory($injector) { - // prevent circular deps by tracking where we came from - const privPath = []; - const pathToString = function () { - return privPath.map(name).join(' -> '); - }; - - // call a private provider and return the instance it creates - function instantiate(prov, locals) { - if (~privPath.indexOf(prov)) { - throw new Error( - 'Circular reference to "' + - name(prov) + - '"' + - ' found while resolving private deps: ' + - pathToString() - ); - } - - privPath.push(prov); - - const context = {}; - let instance = $injector.invoke(prov, context, locals); - if (!isObject(instance)) instance = context; - - privPath.pop(); - return instance; - } - - // retrieve an instance from cache or create and store on - function get(id, prov, $delegateId, $delegateProv) { - if (cache[id]) return cache[id]; - - let instance; - - if ($delegateId != null && $delegateProv != null) { - instance = instantiate(prov, { - $decorate: partial(get, $delegateId, $delegateProv), - }); - } else { - instance = instantiate(prov); - } - - return (cache[id] = instance); - } - - // main api, get the appropriate instance for a provider - function Private(prov) { - let id = identify(prov); - let $delegateId; - let $delegateProv; - - if (swaps[id]) { - $delegateId = id; - $delegateProv = prov; - - prov = swaps[$delegateId]; - id = identify(prov); - } - - return get(id, prov, $delegateId, $delegateProv); - } - - Private.stub = provider.stub; - Private.swap = provider.swap; - - return Private; - }, - ]; - - return provider; -} diff --git a/x-pack/plugins/monitoring/public/application/pages/logstash/pipelines.tsx b/x-pack/plugins/monitoring/public/application/pages/logstash/pipelines.tsx index c2dfe1c0dae7d..2a2de0a716cea 100644 --- a/x-pack/plugins/monitoring/public/application/pages/logstash/pipelines.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/logstash/pipelines.tsx @@ -34,12 +34,12 @@ export const LogStashPipelinesPage: React.FC = ({ clusters }) => const { getPaginationTableProps, getPaginationRouteOptions, updateTotalItemCount } = useTable('logstash.pipelines'); - const title = i18n.translate('xpack.monitoring.logstash.overview.title', { - defaultMessage: 'Logstash', + const title = i18n.translate('xpack.monitoring.logstash.pipelines.routeTitle', { + defaultMessage: 'Logstash Pipelines', }); - const pageTitle = i18n.translate('xpack.monitoring.logstash.overview.pageTitle', { - defaultMessage: 'Logstash overview', + const pageTitle = i18n.translate('xpack.monitoring.logstash.pipelines.pageTitle', { + defaultMessage: 'Logstash pipelines', }); const getPageData = useCallback(async () => { diff --git a/x-pack/plugins/monitoring/public/application/pages/no_data/no_data_page.tsx b/x-pack/plugins/monitoring/public/application/pages/no_data/no_data_page.tsx index e798e7d74ad38..e767074aea42b 100644 --- a/x-pack/plugins/monitoring/public/application/pages/no_data/no_data_page.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/no_data/no_data_page.tsx @@ -17,7 +17,7 @@ import { CODE_PATH_LICENSE, STANDALONE_CLUSTER_CLUSTER_UUID } from '../../../../ import { Legacy } from '../../../legacy_shims'; import { Enabler } from './enabler'; import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; -import { initSetupModeState } from '../../setup_mode/setup_mode'; +import { initSetupModeState } from '../../../lib/setup_mode'; import { GlobalStateContext } from '../../contexts/global_state_context'; import { useRequestErrorHandler } from '../../hooks/use_request_error_handler'; diff --git a/x-pack/plugins/monitoring/public/application/pages/page_template.tsx b/x-pack/plugins/monitoring/public/application/pages/page_template.tsx index 23eeb2c034a80..c0030cfcfe55c 100644 --- a/x-pack/plugins/monitoring/public/application/pages/page_template.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/page_template.tsx @@ -17,7 +17,7 @@ import { getSetupModeState, isSetupModeFeatureEnabled, updateSetupModeData, -} from '../setup_mode/setup_mode'; +} from '../../lib/setup_mode'; import { SetupModeFeature } from '../../../common/enums'; import { AlertsDropdown } from '../../alerts/alerts_dropdown'; import { ActionMenu } from '../../components/action_menu'; diff --git a/x-pack/plugins/monitoring/public/application/setup_mode/index.ts b/x-pack/plugins/monitoring/public/application/setup_mode/index.ts index 1bcdcdef09c28..57d734fc6d056 100644 --- a/x-pack/plugins/monitoring/public/application/setup_mode/index.ts +++ b/x-pack/plugins/monitoring/public/application/setup_mode/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export * from './setup_mode'; +export * from '../../lib/setup_mode'; diff --git a/x-pack/plugins/monitoring/public/application/setup_mode/setup_mode.tsx b/x-pack/plugins/monitoring/public/application/setup_mode/setup_mode.tsx deleted file mode 100644 index 828d5a2d20ae6..0000000000000 --- a/x-pack/plugins/monitoring/public/application/setup_mode/setup_mode.tsx +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { render } from 'react-dom'; -import { get, includes } from 'lodash'; -import { i18n } from '@kbn/i18n'; -import { HttpStart, IHttpFetchError } from 'kibana/public'; -import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; -import { Legacy } from '../../legacy_shims'; -import { SetupModeEnterButton } from '../../components/setup_mode/enter_button'; -import { SetupModeFeature } from '../../../common/enums'; -import { ISetupModeContext } from '../../components/setup_mode/setup_mode_context'; -import { State as GlobalState } from '../contexts/global_state_context'; - -function isOnPage(hash: string) { - return includes(window.location.hash, hash); -} - -let globalState: GlobalState; -let httpService: HttpStart; -let errorHandler: (error: IHttpFetchError) => void; - -interface ISetupModeState { - enabled: boolean; - data: any; - callback?: (() => void) | null; - hideBottomBar: boolean; -} -const setupModeState: ISetupModeState = { - enabled: false, - data: null, - callback: null, - hideBottomBar: false, -}; - -export const getSetupModeState = () => setupModeState; - -export const setNewlyDiscoveredClusterUuid = (clusterUuid: string) => { - globalState.cluster_uuid = clusterUuid; - globalState.save?.(); -}; - -export const fetchCollectionData = async (uuid?: string, fetchWithoutClusterUuid = false) => { - const clusterUuid = globalState.cluster_uuid; - const ccs = globalState.ccs; - - let url = '../api/monitoring/v1/setup/collection'; - if (uuid) { - url += `/node/${uuid}`; - } else if (!fetchWithoutClusterUuid && clusterUuid) { - url += `/cluster/${clusterUuid}`; - } else { - url += '/cluster'; - } - - try { - const response = await httpService.post(url, { - body: JSON.stringify({ - ccs, - }), - }); - return response; - } catch (err) { - errorHandler(err); - throw err; - } -}; - -const notifySetupModeDataChange = () => setupModeState.callback && setupModeState.callback(); - -export const updateSetupModeData = async (uuid?: string, fetchWithoutClusterUuid = false) => { - const data = await fetchCollectionData(uuid, fetchWithoutClusterUuid); - setupModeState.data = data; - const hasPermissions = get(data, '_meta.hasPermissions', false); - if (!hasPermissions) { - let text: string = ''; - if (!hasPermissions) { - text = i18n.translate('xpack.monitoring.setupMode.notAvailablePermissions', { - defaultMessage: 'You do not have the necessary permissions to do this.', - }); - } - - Legacy.shims.toastNotifications.addDanger({ - title: i18n.translate('xpack.monitoring.setupMode.notAvailableTitle', { - defaultMessage: 'Setup mode is not available', - }), - text, - }); - return toggleSetupMode(false); - } - notifySetupModeDataChange(); - - const clusterUuid = globalState.cluster_uuid; - if (!clusterUuid) { - const liveClusterUuid: string = get(data, '_meta.liveClusterUuid'); - const migratedEsNodes = Object.values(get(data, 'elasticsearch.byUuid', {})).filter( - (node: any) => node.isPartiallyMigrated || node.isFullyMigrated - ); - if (liveClusterUuid && migratedEsNodes.length > 0) { - setNewlyDiscoveredClusterUuid(liveClusterUuid); - } - } -}; - -export const hideBottomBar = () => { - setupModeState.hideBottomBar = true; - notifySetupModeDataChange(); -}; -export const showBottomBar = () => { - setupModeState.hideBottomBar = false; - notifySetupModeDataChange(); -}; - -export const disableElasticsearchInternalCollection = async () => { - const clusterUuid = globalState.cluster_uuid; - const url = `../api/monitoring/v1/setup/collection/${clusterUuid}/disable_internal_collection`; - try { - const response = await httpService.post(url); - return response; - } catch (err) { - errorHandler(err); - throw err; - } -}; - -export const toggleSetupMode = (inSetupMode: boolean) => { - setupModeState.enabled = inSetupMode; - globalState.inSetupMode = inSetupMode; - globalState.save?.(); - setSetupModeMenuItem(); - notifySetupModeDataChange(); - - if (inSetupMode) { - // Intentionally do not await this so we don't block UI operations - updateSetupModeData(); - } -}; - -export const setSetupModeMenuItem = () => { - if (isOnPage('no-data')) { - return; - } - - const enabled = !globalState.inSetupMode; - const I18nContext = Legacy.shims.I18nContext; - - render( - - - - - , - document.getElementById('setupModeNav') - ); -}; - -export const initSetupModeState = async ( - state: GlobalState, - http: HttpStart, - handleErrors: (error: IHttpFetchError) => void, - callback?: () => void -) => { - globalState = state; - httpService = http; - errorHandler = handleErrors; - if (callback) { - setupModeState.callback = callback; - } - - if (globalState.inSetupMode) { - toggleSetupMode(true); - } -}; - -export const isInSetupMode = (context?: ISetupModeContext, gState: GlobalState = globalState) => { - if (context?.setupModeSupported === false) { - return false; - } - if (setupModeState.enabled) { - return true; - } - - return gState.inSetupMode; -}; - -export const isSetupModeFeatureEnabled = (feature: SetupModeFeature) => { - if (!setupModeState.enabled) { - return false; - } - - if (feature === SetupModeFeature.MetricbeatMigration) { - if (Legacy.shims.isCloud) { - return false; - } - } - - return true; -}; diff --git a/x-pack/plugins/monitoring/public/application/setup_mode/setup_mode_renderer.js b/x-pack/plugins/monitoring/public/application/setup_mode/setup_mode_renderer.js index a9ee2464cd423..df524fa99ae53 100644 --- a/x-pack/plugins/monitoring/public/application/setup_mode/setup_mode_renderer.js +++ b/x-pack/plugins/monitoring/public/application/setup_mode/setup_mode_renderer.js @@ -13,7 +13,7 @@ import { disableElasticsearchInternalCollection, toggleSetupMode, setSetupModeMenuItem, -} from './setup_mode'; +} from '../../lib/setup_mode'; import { Flyout } from '../../components/metricbeat_migration/flyout'; import { EuiBottomBar, diff --git a/x-pack/plugins/monitoring/public/directives/elasticsearch/ml_job_listing/index.js b/x-pack/plugins/monitoring/public/directives/elasticsearch/ml_job_listing/index.js deleted file mode 100644 index 69579cb831c06..0000000000000 --- a/x-pack/plugins/monitoring/public/directives/elasticsearch/ml_job_listing/index.js +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { capitalize } from 'lodash'; -import numeral from '@elastic/numeral'; -import React from 'react'; -import { render, unmountComponentAtNode } from 'react-dom'; -import { EuiMonitoringTable } from '../../../components/table'; -import { MachineLearningJobStatusIcon } from '../../../components/elasticsearch/ml_job_listing/status_icon'; -import { LARGE_ABBREVIATED, LARGE_BYTES } from '../../../../common/formatting'; -import { EuiLink, EuiPage, EuiPageContent, EuiPageBody, EuiPanel, EuiSpacer } from '@elastic/eui'; -import { ClusterStatus } from '../../../components/elasticsearch/cluster_status'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { getSafeForExternalLink } from '../../../lib/get_safe_for_external_link'; - -const getColumns = () => [ - { - name: i18n.translate('xpack.monitoring.elasticsearch.mlJobListing.jobIdTitle', { - defaultMessage: 'Job ID', - }), - field: 'job_id', - sortable: true, - }, - { - name: i18n.translate('xpack.monitoring.elasticsearch.mlJobListing.stateTitle', { - defaultMessage: 'State', - }), - field: 'state', - sortable: true, - render: (state) => ( -
- -   - {capitalize(state)} -
- ), - }, - { - name: i18n.translate('xpack.monitoring.elasticsearch.mlJobListing.processedRecordsTitle', { - defaultMessage: 'Processed Records', - }), - field: 'data_counts.processed_record_count', - sortable: true, - render: (value) => {numeral(value).format(LARGE_ABBREVIATED)}, - }, - { - name: i18n.translate('xpack.monitoring.elasticsearch.mlJobListing.modelSizeTitle', { - defaultMessage: 'Model Size', - }), - field: 'model_size_stats.model_bytes', - sortable: true, - render: (value) => {numeral(value).format(LARGE_BYTES)}, - }, - { - name: i18n.translate('xpack.monitoring.elasticsearch.mlJobListing.forecastsTitle', { - defaultMessage: 'Forecasts', - }), - field: 'forecasts_stats.total', - sortable: true, - render: (value) => {numeral(value).format(LARGE_ABBREVIATED)}, - }, - { - name: i18n.translate('xpack.monitoring.elasticsearch.mlJobListing.nodeTitle', { - defaultMessage: 'Node', - }), - field: 'node.name', - sortable: true, - render: (name, node) => { - if (node) { - return ( - - {name} - - ); - } - - return ( - - ); - }, - }, -]; - -//monitoringMlListing -export function monitoringMlListingProvider() { - return { - restrict: 'E', - scope: { - jobs: '=', - paginationSettings: '=', - sorting: '=', - onTableChange: '=', - status: '=', - }, - link(scope, $el) { - scope.$on('$destroy', () => $el && $el[0] && unmountComponentAtNode($el[0])); - const columns = getColumns(); - - const filterJobsPlaceholder = i18n.translate( - 'xpack.monitoring.elasticsearch.mlJobListing.filterJobsPlaceholder', - { - defaultMessage: 'Filter Jobs…', - } - ); - - scope.$watch('jobs', (_jobs = []) => { - const jobs = _jobs.map((job) => { - if (job.ml) { - return { - ...job.ml.job, - node: job.node, - job_id: job.ml.job.id, - }; - } - return job; - }); - const mlTable = ( - - - - - - - - - - - - ); - render(mlTable, $el[0]); - }); - }, - }; -} diff --git a/x-pack/plugins/monitoring/public/directives/main/index.html b/x-pack/plugins/monitoring/public/directives/main/index.html deleted file mode 100644 index fd14120e1db2f..0000000000000 --- a/x-pack/plugins/monitoring/public/directives/main/index.html +++ /dev/null @@ -1,323 +0,0 @@ -
-
-
-
-
-
-
-

{{pageTitle || monitoringMain.instance}}

-
-
-
-
-
- - -
-
-
- - - - - - - - - - - - - - - -
-
-
diff --git a/x-pack/plugins/monitoring/public/directives/main/index.js b/x-pack/plugins/monitoring/public/directives/main/index.js deleted file mode 100644 index 0e464f0a356c4..0000000000000 --- a/x-pack/plugins/monitoring/public/directives/main/index.js +++ /dev/null @@ -1,275 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { render, unmountComponentAtNode } from 'react-dom'; -import { EuiSelect, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { get } from 'lodash'; -import template from './index.html'; -import { Legacy } from '../../legacy_shims'; -import { shortenPipelineHash } from '../../../common/formatting'; -import { - getSetupModeState, - initSetupModeState, - isSetupModeFeatureEnabled, -} from '../../lib/setup_mode'; -import { Subscription } from 'rxjs'; -import { getSafeForExternalLink } from '../../lib/get_safe_for_external_link'; -import { SetupModeFeature } from '../../../common/enums'; -import './index.scss'; - -const setOptions = (controller) => { - if ( - !controller.pipelineVersions || - !controller.pipelineVersions.length || - !controller.pipelineDropdownElement - ) { - return; - } - - render( - - - { - return { - text: i18n.translate( - 'xpack.monitoring.logstashNavigation.pipelineVersionDescription', - { - defaultMessage: - 'Version active {relativeLastSeen} and first seen {relativeFirstSeen}', - values: { - relativeLastSeen: option.relativeLastSeen, - relativeFirstSeen: option.relativeFirstSeen, - }, - } - ), - value: option.hash, - }; - })} - onChange={controller.onChangePipelineHash} - /> - - , - controller.pipelineDropdownElement - ); -}; - -/* - * Manage data and provide helper methods for the "main" directive's template - */ -export class MonitoringMainController { - // called internally by Angular - constructor() { - this.inListing = false; - this.inAlerts = false; - this.inOverview = false; - this.inElasticsearch = false; - this.inKibana = false; - this.inLogstash = false; - this.inBeats = false; - this.inApm = false; - } - - addTimerangeObservers = () => { - const timefilter = Legacy.shims.timefilter; - this.subscriptions = new Subscription(); - - const refreshIntervalUpdated = () => { - const { value: refreshInterval, pause: isPaused } = timefilter.getRefreshInterval(); - this.datePicker.onRefreshChange({ refreshInterval, isPaused }, true); - }; - - const timeUpdated = () => { - this.datePicker.onTimeUpdate({ dateRange: timefilter.getTime() }, true); - }; - - this.subscriptions.add( - timefilter.getRefreshIntervalUpdate$().subscribe(refreshIntervalUpdated) - ); - this.subscriptions.add(timefilter.getTimeUpdate$().subscribe(timeUpdated)); - }; - - dropdownLoadedHandler() { - this.pipelineDropdownElement = document.querySelector('#dropdown-elm'); - setOptions(this); - } - - // kick things off from the directive link function - setup(options) { - const timefilter = Legacy.shims.timefilter; - this._licenseService = options.licenseService; - this._breadcrumbsService = options.breadcrumbsService; - this._executorService = options.executorService; - - Object.assign(this, options.attributes); - - this.navName = `${this.name}-nav`; - - // set the section we're navigated in - if (this.product) { - this.inElasticsearch = this.product === 'elasticsearch'; - this.inKibana = this.product === 'kibana'; - this.inLogstash = this.product === 'logstash'; - this.inBeats = this.product === 'beats'; - this.inApm = this.product === 'apm'; - } else { - this.inOverview = this.name === 'overview'; - this.inAlerts = this.name === 'alerts'; - this.inListing = this.name === 'listing'; // || this.name === 'no-data'; - } - - if (!this.inListing) { - // no breadcrumbs in cluster listing page - this.breadcrumbs = this._breadcrumbsService(options.clusterName, this); - } - - if (this.pipelineHash) { - this.pipelineHashShort = shortenPipelineHash(this.pipelineHash); - this.onChangePipelineHash = () => { - window.location.hash = getSafeForExternalLink( - `#/logstash/pipelines/${this.pipelineId}/${this.pipelineHash}` - ); - }; - } - - this.datePicker = { - enableTimeFilter: timefilter.isTimeRangeSelectorEnabled(), - timeRange: timefilter.getTime(), - refreshInterval: timefilter.getRefreshInterval(), - onRefreshChange: ({ isPaused, refreshInterval }, skipSet = false) => { - this.datePicker.refreshInterval = { - pause: isPaused, - value: refreshInterval, - }; - if (!skipSet) { - timefilter.setRefreshInterval({ - pause: isPaused, - value: refreshInterval ? refreshInterval : this.datePicker.refreshInterval.value, - }); - } - }, - onTimeUpdate: ({ dateRange }, skipSet = false) => { - this.datePicker.timeRange = { - ...dateRange, - }; - if (!skipSet) { - timefilter.setTime(dateRange); - } - this._executorService.cancel(); - this._executorService.run(); - }, - }; - } - - // check whether to "highlight" a tab - isActiveTab(testPath) { - return this.name === testPath; - } - - // check whether to show ML tab - isMlSupported() { - return this._licenseService.mlIsSupported(); - } - - isDisabledTab(product) { - const setupMode = getSetupModeState(); - if (!isSetupModeFeatureEnabled(SetupModeFeature.MetricbeatMigration)) { - return false; - } - - if (!setupMode.data) { - return false; - } - - const data = setupMode.data[product] || {}; - if (data.totalUniqueInstanceCount === 0) { - return true; - } - if ( - data.totalUniqueInternallyCollectedCount === 0 && - data.totalUniqueFullyMigratedCount === 0 && - data.totalUniquePartiallyMigratedCount === 0 - ) { - return true; - } - return false; - } -} - -export function monitoringMainProvider(breadcrumbs, license, $injector) { - const $executor = $injector.get('$executor'); - const $parse = $injector.get('$parse'); - - return { - restrict: 'E', - transclude: true, - template, - controller: MonitoringMainController, - controllerAs: 'monitoringMain', - bindToController: true, - link(scope, _element, attributes, controller) { - scope.$applyAsync(() => { - controller.addTimerangeObservers(); - const setupObj = getSetupObj(); - controller.setup(setupObj); - Object.keys(setupObj.attributes).forEach((key) => { - attributes.$observe(key, () => controller.setup(getSetupObj())); - }); - if (attributes.onLoaded) { - const onLoaded = $parse(attributes.onLoaded)(scope); - onLoaded(); - } - }); - - initSetupModeState(scope, $injector, () => { - controller.setup(getSetupObj()); - }); - if (!scope.cluster) { - const $route = $injector.get('$route'); - const globalState = $injector.get('globalState'); - scope.cluster = ($route.current.locals.clusters || []).find( - (cluster) => cluster.cluster_uuid === globalState.cluster_uuid - ); - } - - function getSetupObj() { - return { - licenseService: license, - breadcrumbsService: breadcrumbs, - executorService: $executor, - attributes: { - name: attributes.name, - product: attributes.product, - instance: attributes.instance, - resolver: attributes.resolver, - page: attributes.page, - tabIconClass: attributes.tabIconClass, - tabIconLabel: attributes.tabIconLabel, - pipelineId: attributes.pipelineId, - pipelineHash: attributes.pipelineHash, - pipelineVersions: get(scope, 'pageData.versions'), - isCcrEnabled: attributes.isCcrEnabled === 'true' || attributes.isCcrEnabled === true, - }, - clusterName: get(scope, 'cluster.cluster_name'), - }; - } - - scope.$on('$destroy', () => { - controller.pipelineDropdownElement && - unmountComponentAtNode(controller.pipelineDropdownElement); - controller.subscriptions && controller.subscriptions.unsubscribe(); - }); - scope.$watch('pageData.versions', (versions) => { - controller.pipelineVersions = versions; - setOptions(controller); - }); - }, - }; -} diff --git a/x-pack/plugins/monitoring/public/directives/main/index.scss b/x-pack/plugins/monitoring/public/directives/main/index.scss deleted file mode 100644 index db5d2b72ab07b..0000000000000 --- a/x-pack/plugins/monitoring/public/directives/main/index.scss +++ /dev/null @@ -1,3 +0,0 @@ -.monTopNavSecondItem { - padding-left: $euiSizeM; -} diff --git a/x-pack/plugins/monitoring/public/directives/main/monitoring_main_controller.test.js b/x-pack/plugins/monitoring/public/directives/main/monitoring_main_controller.test.js deleted file mode 100644 index 195e11cee6112..0000000000000 --- a/x-pack/plugins/monitoring/public/directives/main/monitoring_main_controller.test.js +++ /dev/null @@ -1,286 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { noop } from 'lodash'; -import expect from '@kbn/expect'; -import { Legacy } from '../../legacy_shims'; -import { MonitoringMainController } from './'; - -const getMockLicenseService = (options) => ({ mlIsSupported: () => options.mlIsSupported }); -const getMockBreadcrumbsService = () => noop; // breadcrumb service has its own test - -describe('Monitoring Main Directive Controller', () => { - const core = { - notifications: {}, - application: {}, - i18n: {}, - chrome: {}, - }; - const data = { - query: { - timefilter: { - timefilter: { - isTimeRangeSelectorEnabled: () => true, - getTime: () => 1, - getRefreshInterval: () => 1, - }, - }, - }, - }; - const isCloud = false; - const triggersActionsUi = {}; - - beforeAll(() => { - Legacy.init({ - core, - data, - isCloud, - triggersActionsUi, - }); - }); - - /* - * Simulates calling the monitoringMain directive the way Cluster Listing - * does: - * - * ... - */ - it('in Cluster Listing', () => { - const controller = new MonitoringMainController(); - controller.setup({ - clusterName: 'test-cluster-foo', - licenseService: getMockLicenseService(), - breadcrumbsService: getMockBreadcrumbsService(), - attributes: { - name: 'listing', - }, - }); - - // derived properties - expect(controller.inListing).to.be(true); - expect(controller.inAlerts).to.be(false); - expect(controller.inOverview).to.be(false); - - // attributes - expect(controller.name).to.be('listing'); - expect(controller.product).to.be(undefined); - expect(controller.instance).to.be(undefined); - expect(controller.resolver).to.be(undefined); - expect(controller.page).to.be(undefined); - expect(controller.tabIconClass).to.be(undefined); - expect(controller.tabIconLabel).to.be(undefined); - }); - - /* - * Simulates calling the monitoringMain directive the way Cluster Alerts - * Listing does: - * - * ... - */ - it('in Cluster Alerts', () => { - const controller = new MonitoringMainController(); - controller.setup({ - clusterName: 'test-cluster-foo', - licenseService: getMockLicenseService(), - breadcrumbsService: getMockBreadcrumbsService(), - attributes: { - name: 'alerts', - }, - }); - - // derived properties - expect(controller.inListing).to.be(false); - expect(controller.inAlerts).to.be(true); - expect(controller.inOverview).to.be(false); - - // attributes - expect(controller.name).to.be('alerts'); - expect(controller.product).to.be(undefined); - expect(controller.instance).to.be(undefined); - expect(controller.resolver).to.be(undefined); - expect(controller.page).to.be(undefined); - expect(controller.tabIconClass).to.be(undefined); - expect(controller.tabIconLabel).to.be(undefined); - }); - - /* - * Simulates calling the monitoringMain directive the way Cluster Overview - * does: - * - * ... - */ - it('in Cluster Overview', () => { - const controller = new MonitoringMainController(); - controller.setup({ - clusterName: 'test-cluster-foo', - licenseService: getMockLicenseService(), - breadcrumbsService: getMockBreadcrumbsService(), - attributes: { - name: 'overview', - }, - }); - - // derived properties - expect(controller.inListing).to.be(false); - expect(controller.inAlerts).to.be(false); - expect(controller.inOverview).to.be(true); - - // attributes - expect(controller.name).to.be('overview'); - expect(controller.product).to.be(undefined); - expect(controller.instance).to.be(undefined); - expect(controller.resolver).to.be(undefined); - expect(controller.page).to.be(undefined); - expect(controller.tabIconClass).to.be(undefined); - expect(controller.tabIconLabel).to.be(undefined); - }); - - /* - * Simulates calling the monitoringMain directive the way that Elasticsearch - * Node / Advanced does: - * - * ... - */ - it('in ES Node - Advanced', () => { - const controller = new MonitoringMainController(); - controller.setup({ - clusterName: 'test-cluster-foo', - licenseService: getMockLicenseService(), - breadcrumbsService: getMockBreadcrumbsService(), - attributes: { - product: 'elasticsearch', - name: 'nodes', - instance: 'es-node-name-01', - resolver: 'es-node-resolver-01', - page: 'advanced', - tabIconClass: 'fa star', - tabIconLabel: 'Master Node', - }, - }); - - // derived properties - expect(controller.inListing).to.be(false); - expect(controller.inAlerts).to.be(false); - expect(controller.inOverview).to.be(false); - - // attributes - expect(controller.name).to.be('nodes'); - expect(controller.product).to.be('elasticsearch'); - expect(controller.instance).to.be('es-node-name-01'); - expect(controller.resolver).to.be('es-node-resolver-01'); - expect(controller.page).to.be('advanced'); - expect(controller.tabIconClass).to.be('fa star'); - expect(controller.tabIconLabel).to.be('Master Node'); - }); - - /** - * - */ - it('in Kibana Overview', () => { - const controller = new MonitoringMainController(); - controller.setup({ - clusterName: 'test-cluster-foo', - licenseService: getMockLicenseService(), - breadcrumbsService: getMockBreadcrumbsService(), - attributes: { - product: 'kibana', - name: 'overview', - }, - }); - - // derived properties - expect(controller.inListing).to.be(false); - expect(controller.inAlerts).to.be(false); - expect(controller.inOverview).to.be(false); - - // attributes - expect(controller.name).to.be('overview'); - expect(controller.product).to.be('kibana'); - expect(controller.instance).to.be(undefined); - expect(controller.resolver).to.be(undefined); - expect(controller.page).to.be(undefined); - expect(controller.tabIconClass).to.be(undefined); - expect(controller.tabIconLabel).to.be(undefined); - }); - - /** - * - */ - it('in Logstash Listing', () => { - const controller = new MonitoringMainController(); - controller.setup({ - clusterName: 'test-cluster-foo', - licenseService: getMockLicenseService(), - breadcrumbsService: getMockBreadcrumbsService(), - attributes: { - product: 'logstash', - name: 'listing', - }, - }); - - // derived properties - expect(controller.inListing).to.be(false); - expect(controller.inAlerts).to.be(false); - expect(controller.inOverview).to.be(false); - - // attributes - expect(controller.name).to.be('listing'); - expect(controller.product).to.be('logstash'); - expect(controller.instance).to.be(undefined); - expect(controller.resolver).to.be(undefined); - expect(controller.page).to.be(undefined); - expect(controller.tabIconClass).to.be(undefined); - expect(controller.tabIconLabel).to.be(undefined); - }); - - /* - * Test `controller.isMlSupported` function - */ - describe('Checking support for ML', () => { - it('license supports ML', () => { - const controller = new MonitoringMainController(); - controller.setup({ - clusterName: 'test-cluster-foo', - licenseService: getMockLicenseService({ mlIsSupported: true }), - breadcrumbsService: getMockBreadcrumbsService(), - attributes: { - name: 'listing', - }, - }); - - expect(controller.isMlSupported()).to.be(true); - }); - it('license does not support ML', () => { - getMockLicenseService({ mlIsSupported: false }); - const controller = new MonitoringMainController(); - controller.setup({ - clusterName: 'test-cluster-foo', - licenseService: getMockLicenseService({ mlIsSupported: false }), - breadcrumbsService: getMockBreadcrumbsService(), - attributes: { - name: 'listing', - }, - }); - - expect(controller.isMlSupported()).to.be(false); - }); - }); -}); diff --git a/x-pack/plugins/monitoring/public/lib/setup_mode.test.js b/x-pack/plugins/monitoring/public/lib/setup_mode.test.js index 6dad6effeecc1..47cae9c4f0851 100644 --- a/x-pack/plugins/monitoring/public/lib/setup_mode.test.js +++ b/x-pack/plugins/monitoring/public/lib/setup_mode.test.js @@ -83,7 +83,7 @@ function waitForSetupModeData() { return new Promise((resolve) => process.nextTick(resolve)); } -describe('setup_mode', () => { +xdescribe('setup_mode', () => { beforeEach(async () => { setModulesAndMocks(); }); diff --git a/x-pack/plugins/monitoring/public/lib/setup_mode.tsx b/x-pack/plugins/monitoring/public/lib/setup_mode.tsx index fca7f94731bc5..e582f4aa40812 100644 --- a/x-pack/plugins/monitoring/public/lib/setup_mode.tsx +++ b/x-pack/plugins/monitoring/public/lib/setup_mode.tsx @@ -9,37 +9,21 @@ import React from 'react'; import { render } from 'react-dom'; import { get, includes } from 'lodash'; import { i18n } from '@kbn/i18n'; +import { HttpStart, IHttpFetchError } from 'kibana/public'; import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public'; import { Legacy } from '../legacy_shims'; -import { ajaxErrorHandlersProvider } from './ajax_error_handler'; import { SetupModeEnterButton } from '../components/setup_mode/enter_button'; import { SetupModeFeature } from '../../common/enums'; import { ISetupModeContext } from '../components/setup_mode/setup_mode_context'; -import * as setupModeReact from '../application/setup_mode/setup_mode'; -import { isReactMigrationEnabled } from '../external_config'; +import { State as GlobalState } from '../application/contexts/global_state_context'; function isOnPage(hash: string) { return includes(window.location.hash, hash); } -interface IAngularState { - injector: any; - scope: any; -} - -const angularState: IAngularState = { - injector: null, - scope: null, -}; - -const checkAngularState = () => { - if (!angularState.injector || !angularState.scope) { - throw new Error( - 'Unable to interact with setup mode because the angular injector was not previously set.' + - ' This needs to be set by calling `initSetupModeState`.' - ); - } -}; +let globalState: GlobalState; +let httpService: HttpStart; +let errorHandler: (error: IHttpFetchError) => void; interface ISetupModeState { enabled: boolean; @@ -57,20 +41,11 @@ const setupModeState: ISetupModeState = { export const getSetupModeState = () => setupModeState; export const setNewlyDiscoveredClusterUuid = (clusterUuid: string) => { - const globalState = angularState.injector.get('globalState'); - const executor = angularState.injector.get('$executor'); - angularState.scope.$apply(() => { - globalState.cluster_uuid = clusterUuid; - globalState.save(); - }); - executor.run(); + globalState.cluster_uuid = clusterUuid; + globalState.save?.(); }; export const fetchCollectionData = async (uuid?: string, fetchWithoutClusterUuid = false) => { - checkAngularState(); - - const http = angularState.injector.get('$http'); - const globalState = angularState.injector.get('globalState'); const clusterUuid = globalState.cluster_uuid; const ccs = globalState.ccs; @@ -84,12 +59,15 @@ export const fetchCollectionData = async (uuid?: string, fetchWithoutClusterUuid } try { - const response = await http.post(url, { ccs }); - return response.data; + const response = await httpService.post(url, { + body: JSON.stringify({ + ccs, + }), + }); + return response; } catch (err) { - const Private = angularState.injector.get('Private'); - const ajaxErrorHandlers = Private(ajaxErrorHandlersProvider); - return ajaxErrorHandlers(err); + errorHandler(err); + throw err; } }; @@ -107,19 +85,16 @@ export const updateSetupModeData = async (uuid?: string, fetchWithoutClusterUuid }); } - angularState.scope.$evalAsync(() => { - Legacy.shims.toastNotifications.addDanger({ - title: i18n.translate('xpack.monitoring.setupMode.notAvailableTitle', { - defaultMessage: 'Setup mode is not available', - }), - text, - }); + Legacy.shims.toastNotifications.addDanger({ + title: i18n.translate('xpack.monitoring.setupMode.notAvailableTitle', { + defaultMessage: 'Setup mode is not available', + }), + text, }); return toggleSetupMode(false); } notifySetupModeDataChange(); - const globalState = angularState.injector.get('globalState'); const clusterUuid = globalState.cluster_uuid; if (!clusterUuid) { const liveClusterUuid: string = get(data, '_meta.liveClusterUuid'); @@ -142,31 +117,21 @@ export const showBottomBar = () => { }; export const disableElasticsearchInternalCollection = async () => { - checkAngularState(); - - const http = angularState.injector.get('$http'); - const globalState = angularState.injector.get('globalState'); const clusterUuid = globalState.cluster_uuid; const url = `../api/monitoring/v1/setup/collection/${clusterUuid}/disable_internal_collection`; try { - const response = await http.post(url); - return response.data; + const response = await httpService.post(url); + return response; } catch (err) { - const Private = angularState.injector.get('Private'); - const ajaxErrorHandlers = Private(ajaxErrorHandlersProvider); - return ajaxErrorHandlers(err); + errorHandler(err); + throw err; } }; export const toggleSetupMode = (inSetupMode: boolean) => { - if (isReactMigrationEnabled()) return setupModeReact.toggleSetupMode(inSetupMode); - - checkAngularState(); - - const globalState = angularState.injector.get('globalState'); setupModeState.enabled = inSetupMode; globalState.inSetupMode = inSetupMode; - globalState.save(); + globalState.save?.(); setSetupModeMenuItem(); notifySetupModeDataChange(); @@ -177,13 +142,10 @@ export const toggleSetupMode = (inSetupMode: boolean) => { }; export const setSetupModeMenuItem = () => { - checkAngularState(); - if (isOnPage('no-data')) { return; } - const globalState = angularState.injector.get('globalState'); const enabled = !globalState.inSetupMode; const I18nContext = Legacy.shims.I18nContext; @@ -197,23 +159,25 @@ export const setSetupModeMenuItem = () => { ); }; -export const addSetupModeCallback = (callback: () => void) => (setupModeState.callback = callback); - -export const initSetupModeState = async ($scope: any, $injector: any, callback?: () => void) => { - angularState.scope = $scope; - angularState.injector = $injector; +export const initSetupModeState = async ( + state: GlobalState, + http: HttpStart, + handleErrors: (error: IHttpFetchError) => void, + callback?: () => void +) => { + globalState = state; + httpService = http; + errorHandler = handleErrors; if (callback) { setupModeState.callback = callback; } - const globalState = $injector.get('globalState'); if (globalState.inSetupMode) { toggleSetupMode(true); } }; -export const isInSetupMode = (context?: ISetupModeContext) => { - if (isReactMigrationEnabled()) return setupModeReact.isInSetupMode(context); +export const isInSetupMode = (context?: ISetupModeContext, gState: GlobalState = globalState) => { if (context?.setupModeSupported === false) { return false; } @@ -221,20 +185,19 @@ export const isInSetupMode = (context?: ISetupModeContext) => { return true; } - const $injector = angularState.injector || Legacy.shims.getAngularInjector(); - const globalState = $injector.get('globalState'); - return globalState.inSetupMode; + return gState.inSetupMode; }; export const isSetupModeFeatureEnabled = (feature: SetupModeFeature) => { - if (isReactMigrationEnabled()) return setupModeReact.isSetupModeFeatureEnabled(feature); if (!setupModeState.enabled) { return false; } + if (feature === SetupModeFeature.MetricbeatMigration) { if (Legacy.shims.isCloud) { return false; } } + return true; }; diff --git a/x-pack/plugins/monitoring/public/plugin.ts b/x-pack/plugins/monitoring/public/plugin.ts index 82e49fec5a8d4..f75b76871f58c 100644 --- a/x-pack/plugins/monitoring/public/plugin.ts +++ b/x-pack/plugins/monitoring/public/plugin.ts @@ -36,9 +36,6 @@ interface MonitoringSetupPluginDependencies { triggersActionsUi: TriggersAndActionsUIPublicPluginSetup; usageCollection: UsageCollectionSetup; } - -const HASH_CHANGE = 'hashchange'; - export class MonitoringPlugin implements Plugin @@ -88,7 +85,6 @@ export class MonitoringPlugin category: DEFAULT_APP_CATEGORIES.management, mount: async (params: AppMountParameters) => { const [coreStart, pluginsStart] = await core.getStartServices(); - const { AngularApp } = await import('./angular'); const externalConfig = this.getExternalConfig(); const deps: MonitoringStartPluginDependencies = { navigation: pluginsStart.navigation, @@ -118,26 +114,8 @@ export class MonitoringPlugin const config = Object.fromEntries(externalConfig); setConfig(config); - if (config.renderReactApp) { - const { renderApp } = await import('./application'); - return renderApp(coreStart, pluginsStart, params, config); - } else { - const monitoringApp = new AngularApp(deps); - const removeHistoryListener = params.history.listen((location) => { - if (location.pathname === '' && location.hash === '') { - monitoringApp.applyScope(); - } - }); - - const removeHashChange = this.setInitialTimefilter(deps); - return () => { - if (removeHashChange) { - removeHashChange(); - } - removeHistoryListener(); - monitoringApp.destroy(); - }; - } + const { renderApp } = await import('./application'); + return renderApp(coreStart, pluginsStart, params, config); }, }; @@ -148,28 +126,6 @@ export class MonitoringPlugin public stop() {} - private setInitialTimefilter({ data }: MonitoringStartPluginDependencies) { - const { timefilter } = data.query.timefilter; - const { pause: pauseByDefault } = timefilter.getRefreshIntervalDefaults(); - if (pauseByDefault) { - return; - } - /** - * We can't use timefilter.getRefreshIntervalUpdate$ last value, - * since it's not a BehaviorSubject. This means we need to wait for - * hash change because of angular's applyAsync - */ - const onHashChange = () => { - const { value, pause } = timefilter.getRefreshInterval(); - if (!value && pause) { - window.removeEventListener(HASH_CHANGE, onHashChange); - timefilter.setRefreshInterval({ value: 10000, pause: false }); - } - }; - window.addEventListener(HASH_CHANGE, onHashChange, false); - return () => window.removeEventListener(HASH_CHANGE, onHashChange); - } - private getExternalConfig() { const monitoring = this.initializerContext.config.get(); return [ diff --git a/x-pack/plugins/monitoring/public/services/breadcrumbs.js b/x-pack/plugins/monitoring/public/services/breadcrumbs.js deleted file mode 100644 index 54ff46f4bf0ab..0000000000000 --- a/x-pack/plugins/monitoring/public/services/breadcrumbs.js +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { Legacy } from '../legacy_shims'; -import { i18n } from '@kbn/i18n'; - -// Helper for making objects to use in a link element -const createCrumb = (url, label, testSubj, ignoreGlobalState = false) => { - const crumb = { url, label, ignoreGlobalState }; - if (testSubj) { - crumb.testSubj = testSubj; - } - return crumb; -}; - -// generate Elasticsearch breadcrumbs -function getElasticsearchBreadcrumbs(mainInstance) { - const breadcrumbs = []; - if (mainInstance.instance) { - breadcrumbs.push(createCrumb('#/elasticsearch', 'Elasticsearch')); - if (mainInstance.name === 'indices') { - breadcrumbs.push( - createCrumb( - '#/elasticsearch/indices', - i18n.translate('xpack.monitoring.breadcrumbs.es.indicesLabel', { - defaultMessage: 'Indices', - }), - 'breadcrumbEsIndices' - ) - ); - } else if (mainInstance.name === 'nodes') { - breadcrumbs.push( - createCrumb( - '#/elasticsearch/nodes', - i18n.translate('xpack.monitoring.breadcrumbs.es.nodesLabel', { defaultMessage: 'Nodes' }), - 'breadcrumbEsNodes' - ) - ); - } else if (mainInstance.name === 'ml') { - // ML Instance (for user later) - breadcrumbs.push( - createCrumb( - '#/elasticsearch/ml_jobs', - i18n.translate('xpack.monitoring.breadcrumbs.es.jobsLabel', { - defaultMessage: 'Machine learning jobs', - }) - ) - ); - } else if (mainInstance.name === 'ccr_shard') { - breadcrumbs.push( - createCrumb( - '#/elasticsearch/ccr', - i18n.translate('xpack.monitoring.breadcrumbs.es.ccrLabel', { defaultMessage: 'CCR' }) - ) - ); - } - breadcrumbs.push(createCrumb(null, mainInstance.instance)); - } else { - // don't link to Overview when we're possibly on Overview or its sibling tabs - breadcrumbs.push(createCrumb(null, 'Elasticsearch')); - } - return breadcrumbs; -} - -// generate Kibana breadcrumbs -function getKibanaBreadcrumbs(mainInstance) { - const breadcrumbs = []; - if (mainInstance.instance) { - breadcrumbs.push(createCrumb('#/kibana', 'Kibana')); - breadcrumbs.push( - createCrumb( - '#/kibana/instances', - i18n.translate('xpack.monitoring.breadcrumbs.kibana.instancesLabel', { - defaultMessage: 'Instances', - }) - ) - ); - breadcrumbs.push(createCrumb(null, mainInstance.instance)); - } else { - // don't link to Overview when we're possibly on Overview or its sibling tabs - breadcrumbs.push(createCrumb(null, 'Kibana')); - } - return breadcrumbs; -} - -// generate Logstash breadcrumbs -function getLogstashBreadcrumbs(mainInstance) { - const logstashLabel = i18n.translate('xpack.monitoring.breadcrumbs.logstashLabel', { - defaultMessage: 'Logstash', - }); - const breadcrumbs = []; - if (mainInstance.instance) { - breadcrumbs.push(createCrumb('#/logstash', logstashLabel)); - if (mainInstance.name === 'nodes') { - breadcrumbs.push( - createCrumb( - '#/logstash/nodes', - i18n.translate('xpack.monitoring.breadcrumbs.logstash.nodesLabel', { - defaultMessage: 'Nodes', - }) - ) - ); - } - breadcrumbs.push(createCrumb(null, mainInstance.instance)); - } else if (mainInstance.page === 'pipeline') { - breadcrumbs.push(createCrumb('#/logstash', logstashLabel)); - breadcrumbs.push( - createCrumb( - '#/logstash/pipelines', - i18n.translate('xpack.monitoring.breadcrumbs.logstash.pipelinesLabel', { - defaultMessage: 'Pipelines', - }) - ) - ); - } else { - // don't link to Overview when we're possibly on Overview or its sibling tabs - breadcrumbs.push(createCrumb(null, logstashLabel)); - } - - return breadcrumbs; -} - -// generate Beats breadcrumbs -function getBeatsBreadcrumbs(mainInstance) { - const beatsLabel = i18n.translate('xpack.monitoring.breadcrumbs.beatsLabel', { - defaultMessage: 'Beats', - }); - const breadcrumbs = []; - if (mainInstance.instance) { - breadcrumbs.push(createCrumb('#/beats', beatsLabel)); - breadcrumbs.push( - createCrumb( - '#/beats/beats', - i18n.translate('xpack.monitoring.breadcrumbs.beats.instancesLabel', { - defaultMessage: 'Instances', - }) - ) - ); - breadcrumbs.push(createCrumb(null, mainInstance.instance)); - } else { - breadcrumbs.push(createCrumb(null, beatsLabel)); - } - - return breadcrumbs; -} - -// generate Apm breadcrumbs -function getApmBreadcrumbs(mainInstance) { - const apmLabel = i18n.translate('xpack.monitoring.breadcrumbs.apmLabel', { - defaultMessage: 'APM server', - }); - const breadcrumbs = []; - if (mainInstance.instance) { - breadcrumbs.push(createCrumb('#/apm', apmLabel)); - breadcrumbs.push( - createCrumb( - '#/apm/instances', - i18n.translate('xpack.monitoring.breadcrumbs.apm.instancesLabel', { - defaultMessage: 'Instances', - }) - ) - ); - breadcrumbs.push(createCrumb(null, mainInstance.instance)); - } else { - // don't link to Overview when we're possibly on Overview or its sibling tabs - breadcrumbs.push(createCrumb(null, apmLabel)); - } - return breadcrumbs; -} - -export function breadcrumbsProvider() { - return function createBreadcrumbs(clusterName, mainInstance) { - const homeCrumb = i18n.translate('xpack.monitoring.breadcrumbs.clustersLabel', { - defaultMessage: 'Clusters', - }); - - let breadcrumbs = [createCrumb('#/home', homeCrumb, 'breadcrumbClusters', true)]; - - if (!mainInstance.inOverview && clusterName) { - breadcrumbs.push(createCrumb('#/overview', clusterName)); - } - - if (mainInstance.inElasticsearch) { - breadcrumbs = breadcrumbs.concat(getElasticsearchBreadcrumbs(mainInstance)); - } - if (mainInstance.inKibana) { - breadcrumbs = breadcrumbs.concat(getKibanaBreadcrumbs(mainInstance)); - } - if (mainInstance.inLogstash) { - breadcrumbs = breadcrumbs.concat(getLogstashBreadcrumbs(mainInstance)); - } - if (mainInstance.inBeats) { - breadcrumbs = breadcrumbs.concat(getBeatsBreadcrumbs(mainInstance)); - } - if (mainInstance.inApm) { - breadcrumbs = breadcrumbs.concat(getApmBreadcrumbs(mainInstance)); - } - - Legacy.shims.breadcrumbs.set( - breadcrumbs.map((b) => ({ - text: b.label, - href: b.url, - 'data-test-subj': b.testSubj, - ignoreGlobalState: b.ignoreGlobalState, - })) - ); - - return breadcrumbs; - }; -} diff --git a/x-pack/plugins/monitoring/public/services/breadcrumbs.test.js b/x-pack/plugins/monitoring/public/services/breadcrumbs.test.js deleted file mode 100644 index 0af5d59e54555..0000000000000 --- a/x-pack/plugins/monitoring/public/services/breadcrumbs.test.js +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import expect from '@kbn/expect'; -import { breadcrumbsProvider } from './breadcrumbs'; -import { MonitoringMainController } from '../directives/main'; -import { Legacy } from '../legacy_shims'; - -describe('Monitoring Breadcrumbs Service', () => { - const core = { - notifications: {}, - application: {}, - i18n: {}, - chrome: {}, - }; - const data = { - query: { - timefilter: { - timefilter: { - isTimeRangeSelectorEnabled: () => true, - getTime: () => 1, - getRefreshInterval: () => 1, - }, - }, - }, - }; - const isCloud = false; - const triggersActionsUi = {}; - - beforeAll(() => { - Legacy.init({ - core, - data, - isCloud, - triggersActionsUi, - }); - }); - - it('in Cluster Alerts', () => { - const controller = new MonitoringMainController(); - controller.setup({ - clusterName: 'test-cluster-foo', - licenseService: {}, - breadcrumbsService: breadcrumbsProvider(), - attributes: { - name: 'alerts', - }, - }); - expect(controller.breadcrumbs).to.eql([ - { url: '#/home', label: 'Clusters', testSubj: 'breadcrumbClusters', ignoreGlobalState: true }, - { url: '#/overview', label: 'test-cluster-foo', ignoreGlobalState: false }, - ]); - }); - - it('in Cluster Overview', () => { - const controller = new MonitoringMainController(); - controller.setup({ - clusterName: 'test-cluster-foo', - licenseService: {}, - breadcrumbsService: breadcrumbsProvider(), - attributes: { - name: 'overview', - }, - }); - expect(controller.breadcrumbs).to.eql([ - { url: '#/home', label: 'Clusters', testSubj: 'breadcrumbClusters', ignoreGlobalState: true }, - ]); - }); - - it('in ES Node - Advanced', () => { - const controller = new MonitoringMainController(); - controller.setup({ - clusterName: 'test-cluster-foo', - licenseService: {}, - breadcrumbsService: breadcrumbsProvider(), - attributes: { - product: 'elasticsearch', - name: 'nodes', - instance: 'es-node-name-01', - resolver: 'es-node-resolver-01', - page: 'advanced', - tabIconClass: 'fa star', - tabIconLabel: 'Master Node', - }, - }); - expect(controller.breadcrumbs).to.eql([ - { url: '#/home', label: 'Clusters', testSubj: 'breadcrumbClusters', ignoreGlobalState: true }, - { url: '#/overview', label: 'test-cluster-foo', ignoreGlobalState: false }, - { url: '#/elasticsearch', label: 'Elasticsearch', ignoreGlobalState: false }, - { - url: '#/elasticsearch/nodes', - label: 'Nodes', - testSubj: 'breadcrumbEsNodes', - ignoreGlobalState: false, - }, - { url: null, label: 'es-node-name-01', ignoreGlobalState: false }, - ]); - }); - - it('in Kibana Overview', () => { - const controller = new MonitoringMainController(); - controller.setup({ - clusterName: 'test-cluster-foo', - licenseService: {}, - breadcrumbsService: breadcrumbsProvider(), - attributes: { - product: 'kibana', - name: 'overview', - }, - }); - expect(controller.breadcrumbs).to.eql([ - { url: '#/home', label: 'Clusters', testSubj: 'breadcrumbClusters', ignoreGlobalState: true }, - { url: '#/overview', label: 'test-cluster-foo', ignoreGlobalState: false }, - { url: null, label: 'Kibana', ignoreGlobalState: false }, - ]); - }); - - /** - * - */ - it('in Logstash Listing', () => { - const controller = new MonitoringMainController(); - controller.setup({ - clusterName: 'test-cluster-foo', - licenseService: {}, - breadcrumbsService: breadcrumbsProvider(), - attributes: { - product: 'logstash', - name: 'listing', - }, - }); - expect(controller.breadcrumbs).to.eql([ - { url: '#/home', label: 'Clusters', testSubj: 'breadcrumbClusters', ignoreGlobalState: true }, - { url: '#/overview', label: 'test-cluster-foo', ignoreGlobalState: false }, - { url: null, label: 'Logstash', ignoreGlobalState: false }, - ]); - }); - - /** - * - */ - it('in Logstash Pipeline Viewer', () => { - const controller = new MonitoringMainController(); - controller.setup({ - clusterName: 'test-cluster-foo', - licenseService: {}, - breadcrumbsService: breadcrumbsProvider(), - attributes: { - product: 'logstash', - page: 'pipeline', - pipelineId: 'main', - pipelineHash: '42ee890af9...', - }, - }); - expect(controller.breadcrumbs).to.eql([ - { url: '#/home', label: 'Clusters', testSubj: 'breadcrumbClusters', ignoreGlobalState: true }, - { url: '#/overview', label: 'test-cluster-foo', ignoreGlobalState: false }, - { url: '#/logstash', label: 'Logstash', ignoreGlobalState: false }, - { url: '#/logstash/pipelines', label: 'Pipelines', ignoreGlobalState: false }, - ]); - }); -}); diff --git a/x-pack/plugins/monitoring/public/services/clusters.js b/x-pack/plugins/monitoring/public/services/clusters.js deleted file mode 100644 index b19d0ea56765f..0000000000000 --- a/x-pack/plugins/monitoring/public/services/clusters.js +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { ajaxErrorHandlersProvider } from '../lib/ajax_error_handler'; -import { Legacy } from '../legacy_shims'; -import { STANDALONE_CLUSTER_CLUSTER_UUID } from '../../common/constants'; - -function formatClusters(clusters) { - return clusters.map(formatCluster); -} - -function formatCluster(cluster) { - if (cluster.cluster_uuid === STANDALONE_CLUSTER_CLUSTER_UUID) { - cluster.cluster_name = 'Standalone Cluster'; - } - return cluster; -} - -export function monitoringClustersProvider($injector) { - return async (clusterUuid, ccs, codePaths) => { - const { min, max } = Legacy.shims.timefilter.getBounds(); - - // append clusterUuid if the parameter is given - let url = '../api/monitoring/v1/clusters'; - if (clusterUuid) { - url += `/${clusterUuid}`; - } - - const $http = $injector.get('$http'); - - async function getClusters() { - try { - const response = await $http.post( - url, - { - ccs, - timeRange: { - min: min.toISOString(), - max: max.toISOString(), - }, - codePaths, - }, - { headers: { 'kbn-system-request': 'true' } } - ); - return formatClusters(response.data); - } catch (err) { - const Private = $injector.get('Private'); - const ajaxErrorHandlers = Private(ajaxErrorHandlersProvider); - return ajaxErrorHandlers(err); - } - } - - return await getClusters(); - }; -} diff --git a/x-pack/plugins/monitoring/public/services/enable_alerts_modal.js b/x-pack/plugins/monitoring/public/services/enable_alerts_modal.js deleted file mode 100644 index 438c5ab83f5e3..0000000000000 --- a/x-pack/plugins/monitoring/public/services/enable_alerts_modal.js +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { ajaxErrorHandlersProvider } from '../lib/ajax_error_handler'; -import { showAlertsToast } from '../alerts/lib/alerts_toast'; - -export function enableAlertsModalProvider($http, $window, $injector) { - function shouldShowAlertsModal(alerts) { - const modalHasBeenShown = $window.sessionStorage.getItem('ALERTS_MODAL_HAS_BEEN_SHOWN'); - const decisionMade = $window.localStorage.getItem('ALERTS_MODAL_DECISION_MADE'); - - if (Object.keys(alerts).length > 0) { - $window.localStorage.setItem('ALERTS_MODAL_DECISION_MADE', true); - return false; - } else if (!modalHasBeenShown && !decisionMade) { - return true; - } - - return false; - } - - async function enableAlerts() { - try { - const { data } = await $http.post('../api/monitoring/v1/alerts/enable', {}); - $window.localStorage.setItem('ALERTS_MODAL_DECISION_MADE', true); - showAlertsToast(data); - } catch (err) { - const Private = $injector.get('Private'); - const ajaxErrorHandlers = Private(ajaxErrorHandlersProvider); - return ajaxErrorHandlers(err); - } - } - - function notAskAgain() { - $window.localStorage.setItem('ALERTS_MODAL_DECISION_MADE', true); - } - - function hideModalForSession() { - $window.sessionStorage.setItem('ALERTS_MODAL_HAS_BEEN_SHOWN', true); - } - - return { - shouldShowAlertsModal, - enableAlerts, - notAskAgain, - hideModalForSession, - }; -} diff --git a/x-pack/plugins/monitoring/public/services/executor.js b/x-pack/plugins/monitoring/public/services/executor.js deleted file mode 100644 index 60b2c171eac32..0000000000000 --- a/x-pack/plugins/monitoring/public/services/executor.js +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { Legacy } from '../legacy_shims'; -import { subscribeWithScope } from '../angular/helpers/utils'; -import { Subscription } from 'rxjs'; - -export function executorProvider($timeout, $q) { - const queue = []; - const subscriptions = new Subscription(); - let executionTimer; - let ignorePaused = false; - - /** - * Resets the timer to start again - * @returns {void} - */ - function reset() { - cancel(); - start(); - } - - function killTimer() { - if (executionTimer) { - $timeout.cancel(executionTimer); - } - } - - /** - * Cancels the execution timer - * @returns {void} - */ - function cancel() { - killTimer(); - } - - /** - * Registers a service with the executor - * @param {object} service The service to register - * @returns {void} - */ - function register(service) { - queue.push(service); - } - - /** - * Stops the executor and empties the service queue - * @returns {void} - */ - function destroy() { - subscriptions.unsubscribe(); - cancel(); - ignorePaused = false; - queue.splice(0, queue.length); - } - - /** - * Runs the queue (all at once) - * @returns {Promise} a promise of all the services - */ - function run() { - const noop = () => $q.resolve(); - return $q - .all( - queue.map((service) => { - return service - .execute() - .then(service.handleResponse || noop) - .catch(service.handleError || noop); - }) - ) - .finally(reset); - } - - function reFetch() { - cancel(); - run(); - } - - function killIfPaused() { - if (Legacy.shims.timefilter.getRefreshInterval().pause) { - killTimer(); - } - } - - /** - * Starts the executor service if the timefilter is not paused - * @returns {void} - */ - function start() { - const timefilter = Legacy.shims.timefilter; - if ( - (ignorePaused || timefilter.getRefreshInterval().pause === false) && - timefilter.getRefreshInterval().value > 0 - ) { - executionTimer = $timeout(run, timefilter.getRefreshInterval().value); - } - } - - /** - * Expose the methods - */ - return { - register, - start($scope) { - $scope.$applyAsync(() => { - const timefilter = Legacy.shims.timefilter; - subscriptions.add( - subscribeWithScope($scope, timefilter.getFetch$(), { - next: reFetch, - }) - ); - subscriptions.add( - subscribeWithScope($scope, timefilter.getRefreshIntervalUpdate$(), { - next: killIfPaused, - }) - ); - start(); - }); - }, - run, - destroy, - reset, - cancel, - }; -} diff --git a/x-pack/plugins/monitoring/public/services/features.js b/x-pack/plugins/monitoring/public/services/features.js deleted file mode 100644 index 34564f79c9247..0000000000000 --- a/x-pack/plugins/monitoring/public/services/features.js +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { has, isUndefined } from 'lodash'; - -export function featuresProvider($window) { - function getData() { - let returnData = {}; - const monitoringData = $window.localStorage.getItem('xpack.monitoring.data'); - - try { - returnData = (monitoringData && JSON.parse(monitoringData)) || {}; - } catch (e) { - console.error('Monitoring UI: error parsing locally stored monitoring data', e); - } - - return returnData; - } - - function update(featureName, value) { - const monitoringDataObj = getData(); - monitoringDataObj[featureName] = value; - $window.localStorage.setItem('xpack.monitoring.data', JSON.stringify(monitoringDataObj)); - } - - function isEnabled(featureName, defaultSetting) { - const monitoringDataObj = getData(); - if (has(monitoringDataObj, featureName)) { - return monitoringDataObj[featureName]; - } - - if (isUndefined(defaultSetting)) { - return false; - } - - return defaultSetting; - } - - return { - isEnabled, - update, - }; -} diff --git a/x-pack/plugins/monitoring/public/services/license.js b/x-pack/plugins/monitoring/public/services/license.js deleted file mode 100644 index cab5ad01cf58a..0000000000000 --- a/x-pack/plugins/monitoring/public/services/license.js +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { includes } from 'lodash'; -import { ML_SUPPORTED_LICENSES } from '../../common/constants'; - -export function licenseProvider() { - return new (class LicenseService { - constructor() { - // do not initialize with usable state - this.license = { - type: null, - expiry_date_in_millis: -Infinity, - }; - } - - // we're required to call this initially - setLicense(license) { - this.license = license; - } - - isBasic() { - return this.license.type === 'basic'; - } - - mlIsSupported() { - return includes(ML_SUPPORTED_LICENSES, this.license.type); - } - - doesExpire() { - const { expiry_date_in_millis: expiryDateInMillis } = this.license; - return expiryDateInMillis !== undefined; - } - - isActive() { - const { expiry_date_in_millis: expiryDateInMillis } = this.license; - return new Date().getTime() < expiryDateInMillis; - } - - isExpired() { - if (this.doesExpire()) { - const { expiry_date_in_millis: expiryDateInMillis } = this.license; - return new Date().getTime() >= expiryDateInMillis; - } - return false; - } - })(); -} diff --git a/x-pack/plugins/monitoring/public/services/title.js b/x-pack/plugins/monitoring/public/services/title.js deleted file mode 100644 index e12d4936584fa..0000000000000 --- a/x-pack/plugins/monitoring/public/services/title.js +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { get } from 'lodash'; -import { i18n } from '@kbn/i18n'; -import { Legacy } from '../legacy_shims'; - -export function titleProvider($rootScope) { - return function changeTitle(cluster, suffix) { - let clusterName = get(cluster, 'cluster_name'); - clusterName = clusterName ? `- ${clusterName}` : ''; - suffix = suffix ? `- ${suffix}` : ''; - $rootScope.$applyAsync(() => { - Legacy.shims.docTitle.change( - i18n.translate('xpack.monitoring.stackMonitoringDocTitle', { - defaultMessage: 'Stack Monitoring {clusterName} {suffix}', - values: { clusterName, suffix }, - }) - ); - }); - }; -} diff --git a/x-pack/plugins/monitoring/public/views/access_denied/index.html b/x-pack/plugins/monitoring/public/views/access_denied/index.html deleted file mode 100644 index 24863559212f7..0000000000000 --- a/x-pack/plugins/monitoring/public/views/access_denied/index.html +++ /dev/null @@ -1,44 +0,0 @@ -
-
-
- - -
- -
-
- -
- -
-
- - - -
-
-
-
-
diff --git a/x-pack/plugins/monitoring/public/views/access_denied/index.js b/x-pack/plugins/monitoring/public/views/access_denied/index.js deleted file mode 100644 index e52df61dd8966..0000000000000 --- a/x-pack/plugins/monitoring/public/views/access_denied/index.js +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { uiRoutes } from '../../angular/helpers/routes'; -import template from './index.html'; - -const tryPrivilege = ($http) => { - return $http - .get('../api/monitoring/v1/check_access') - .then(() => window.history.replaceState(null, null, '#/home')) - .catch(() => true); -}; - -uiRoutes.when('/access-denied', { - template, - resolve: { - /* - * The user may have been granted privileges in between leaving Monitoring - * and before coming back to Monitoring. That means, they just be on this - * page because Kibana remembers the "last app URL". We check for the - * privilege one time up front (doing it in the resolve makes it happen - * before the template renders), and then keep retrying every 5 seconds. - */ - initialCheck($http) { - return tryPrivilege($http); - }, - }, - controllerAs: 'accessDenied', - controller: function ($scope, $injector) { - const $http = $injector.get('$http'); - const $interval = $injector.get('$interval'); - - // The template's "Back to Kibana" button click handler - this.goToKibanaURL = '/app/home'; - - // keep trying to load data in the background - const accessPoller = $interval(() => tryPrivilege($http), 5 * 1000); // every 5 seconds - $scope.$on('$destroy', () => $interval.cancel(accessPoller)); - }, -}); diff --git a/x-pack/plugins/monitoring/public/views/all.js b/x-pack/plugins/monitoring/public/views/all.js deleted file mode 100644 index 3af0c85d95687..0000000000000 --- a/x-pack/plugins/monitoring/public/views/all.js +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import './no_data'; -import './access_denied'; -import './license'; -import './cluster/listing'; -import './cluster/overview'; -import './elasticsearch/overview'; -import './elasticsearch/indices'; -import './elasticsearch/index'; -import './elasticsearch/index/advanced'; -import './elasticsearch/nodes'; -import './elasticsearch/node'; -import './elasticsearch/node/advanced'; -import './elasticsearch/ccr'; -import './elasticsearch/ccr/shard'; -import './elasticsearch/ml_jobs'; -import './kibana/overview'; -import './kibana/instances'; -import './kibana/instance'; -import './logstash/overview'; -import './logstash/nodes'; -import './logstash/node'; -import './logstash/node/advanced'; -import './logstash/node/pipelines'; -import './logstash/pipelines'; -import './logstash/pipeline'; -import './beats/overview'; -import './beats/listing'; -import './beats/beat'; -import './apm/overview'; -import './apm/instances'; -import './apm/instance'; -import './loading'; diff --git a/x-pack/plugins/monitoring/public/views/apm/instance/index.html b/x-pack/plugins/monitoring/public/views/apm/instance/index.html deleted file mode 100644 index 79579990eb649..0000000000000 --- a/x-pack/plugins/monitoring/public/views/apm/instance/index.html +++ /dev/null @@ -1,8 +0,0 @@ - -
-
diff --git a/x-pack/plugins/monitoring/public/views/apm/instance/index.js b/x-pack/plugins/monitoring/public/views/apm/instance/index.js deleted file mode 100644 index 0d733036bb266..0000000000000 --- a/x-pack/plugins/monitoring/public/views/apm/instance/index.js +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { i18n } from '@kbn/i18n'; -import { find, get } from 'lodash'; -import { uiRoutes } from '../../../angular/helpers/routes'; -import { routeInitProvider } from '../../../lib/route_init'; -import template from './index.html'; -import { MonitoringViewBaseController } from '../../base_controller'; -import { ApmServerInstance } from '../../../components/apm/instance'; -import { CODE_PATH_APM } from '../../../../common/constants'; - -uiRoutes.when('/apm/instances/:uuid', { - template, - resolve: { - clusters: function (Private) { - const routeInit = Private(routeInitProvider); - return routeInit({ codePaths: [CODE_PATH_APM] }); - }, - }, - - controller: class extends MonitoringViewBaseController { - constructor($injector, $scope) { - const $route = $injector.get('$route'); - const title = $injector.get('title'); - const globalState = $injector.get('globalState'); - $scope.cluster = find($route.current.locals.clusters, { - cluster_uuid: globalState.cluster_uuid, - }); - super({ - title: i18n.translate('xpack.monitoring.apm.instance.routeTitle', { - defaultMessage: '{apm} - Instance', - values: { - apm: 'APM server', - }, - }), - telemetryPageViewTitle: 'apm_server_instance', - api: `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/apm/${$route.current.params.uuid}`, - defaultData: {}, - reactNodeId: 'apmInstanceReact', - $scope, - $injector, - }); - - $scope.$watch( - () => this.data, - (data) => { - this.setPageTitle( - i18n.translate('xpack.monitoring.apm.instance.pageTitle', { - defaultMessage: 'APM server instance: {instanceName}', - values: { - instanceName: get(data, 'apmSummary.name'), - }, - }) - ); - title($scope.cluster, `APM server - ${get(data, 'apmSummary.name')}`); - this.renderReact( - - ); - } - ); - } - }, -}); diff --git a/x-pack/plugins/monitoring/public/views/apm/instances/index.html b/x-pack/plugins/monitoring/public/views/apm/instances/index.html deleted file mode 100644 index fd8029e277d78..0000000000000 --- a/x-pack/plugins/monitoring/public/views/apm/instances/index.html +++ /dev/null @@ -1,7 +0,0 @@ - -
-
diff --git a/x-pack/plugins/monitoring/public/views/apm/instances/index.js b/x-pack/plugins/monitoring/public/views/apm/instances/index.js deleted file mode 100644 index f9747ec176e86..0000000000000 --- a/x-pack/plugins/monitoring/public/views/apm/instances/index.js +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { i18n } from '@kbn/i18n'; -import { find } from 'lodash'; -import { uiRoutes } from '../../../angular/helpers/routes'; -import { routeInitProvider } from '../../../lib/route_init'; -import template from './index.html'; -import { ApmServerInstances } from '../../../components/apm/instances'; -import { MonitoringViewBaseEuiTableController } from '../..'; -import { SetupModeRenderer } from '../../../components/renderers'; -import { SetupModeContext } from '../../../components/setup_mode/setup_mode_context'; -import { APM_SYSTEM_ID, CODE_PATH_APM } from '../../../../common/constants'; - -uiRoutes.when('/apm/instances', { - template, - resolve: { - clusters: function (Private) { - const routeInit = Private(routeInitProvider); - return routeInit({ codePaths: [CODE_PATH_APM] }); - }, - }, - controller: class extends MonitoringViewBaseEuiTableController { - constructor($injector, $scope) { - const $route = $injector.get('$route'); - const globalState = $injector.get('globalState'); - $scope.cluster = find($route.current.locals.clusters, { - cluster_uuid: globalState.cluster_uuid, - }); - - super({ - title: i18n.translate('xpack.monitoring.apm.instances.routeTitle', { - defaultMessage: '{apm} - Instances', - values: { - apm: 'APM server', - }, - }), - pageTitle: i18n.translate('xpack.monitoring.apm.instances.pageTitle', { - defaultMessage: 'APM server instances', - }), - storageKey: 'apm.instances', - api: `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/apm/instances`, - defaultData: {}, - reactNodeId: 'apmInstancesReact', - $scope, - $injector, - }); - - this.scope = $scope; - this.injector = $injector; - this.onTableChangeRender = this.renderComponent; - - $scope.$watch( - () => this.data, - () => this.renderComponent() - ); - } - - renderComponent() { - const { pagination, sorting, onTableChange } = this; - - const component = ( - ( - - {flyoutComponent} - - {bottomBarComponent} - - )} - /> - ); - this.renderReact(component); - } - }, -}); diff --git a/x-pack/plugins/monitoring/public/views/apm/overview/index.html b/x-pack/plugins/monitoring/public/views/apm/overview/index.html deleted file mode 100644 index 0cf804e377476..0000000000000 --- a/x-pack/plugins/monitoring/public/views/apm/overview/index.html +++ /dev/null @@ -1,7 +0,0 @@ - -
-
diff --git a/x-pack/plugins/monitoring/public/views/apm/overview/index.js b/x-pack/plugins/monitoring/public/views/apm/overview/index.js deleted file mode 100644 index bef17bf4a2fad..0000000000000 --- a/x-pack/plugins/monitoring/public/views/apm/overview/index.js +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { i18n } from '@kbn/i18n'; -import { find } from 'lodash'; -import { uiRoutes } from '../../../angular/helpers/routes'; -import { routeInitProvider } from '../../../lib/route_init'; -import template from './index.html'; -import { MonitoringViewBaseController } from '../../base_controller'; -import { ApmOverview } from '../../../components/apm/overview'; -import { CODE_PATH_APM } from '../../../../common/constants'; - -uiRoutes.when('/apm', { - template, - resolve: { - clusters: function (Private) { - const routeInit = Private(routeInitProvider); - return routeInit({ codePaths: [CODE_PATH_APM] }); - }, - }, - controller: class extends MonitoringViewBaseController { - constructor($injector, $scope) { - const $route = $injector.get('$route'); - const globalState = $injector.get('globalState'); - $scope.cluster = find($route.current.locals.clusters, { - cluster_uuid: globalState.cluster_uuid, - }); - - super({ - title: i18n.translate('xpack.monitoring.apm.overview.routeTitle', { - defaultMessage: 'APM server', - }), - pageTitle: i18n.translate('xpack.monitoring.apm.overview.pageTitle', { - defaultMessage: 'APM server overview', - }), - api: `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/apm`, - defaultData: {}, - reactNodeId: 'apmOverviewReact', - $scope, - $injector, - }); - - $scope.$watch( - () => this.data, - (data) => { - this.renderReact( - - ); - } - ); - } - }, -}); diff --git a/x-pack/plugins/monitoring/public/views/base_controller.js b/x-pack/plugins/monitoring/public/views/base_controller.js deleted file mode 100644 index dd9898a6e195c..0000000000000 --- a/x-pack/plugins/monitoring/public/views/base_controller.js +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import moment from 'moment'; -import { render, unmountComponentAtNode } from 'react-dom'; -import { getPageData } from '../lib/get_page_data'; -import { PageLoading } from '../components'; -import { Legacy } from '../legacy_shims'; -import { PromiseWithCancel } from '../../common/cancel_promise'; -import { SetupModeFeature } from '../../common/enums'; -import { updateSetupModeData, isSetupModeFeatureEnabled } from '../lib/setup_mode'; -import { AlertsContext } from '../alerts/context'; -import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public'; -import { AlertsDropdown } from '../alerts/alerts_dropdown'; -import { HeaderMenuPortal } from '../../../observability/public'; - -/** - * Given a timezone, this function will calculate the offset in milliseconds - * from UTC time. - * - * @param {string} timezone - */ -const getOffsetInMS = (timezone) => { - if (timezone === 'Browser') { - return 0; - } - const offsetInMinutes = moment.tz(timezone).utcOffset(); - const offsetInMS = offsetInMinutes * 1 * 60 * 1000; - return offsetInMS; -}; - -/** - * Class to manage common instantiation behaviors in a view controller - * - * This is expected to be extended, and behavior enabled using super(); - * - * Example: - * uiRoutes.when('/myRoute', { - * template: importedTemplate, - * controllerAs: 'myView', - * controller: class MyView extends MonitoringViewBaseController { - * constructor($injector, $scope) { - * super({ - * title: 'Hello World', - * api: '../api/v1/monitoring/foo/bar', - * defaultData, - * reactNodeId, - * $scope, - * $injector, - * options: { - * enableTimeFilter: false // this will have just the page auto-refresh control show - * } - * }); - * } - * } - * }); - */ -export class MonitoringViewBaseController { - /** - * Create a view controller - * @param {String} title - Title of the page - * @param {String} api - Back-end API endpoint to poll for getting the page - * data using POST and time range data in the body. Whenever possible, use - * this method for data polling rather than supply the getPageData param. - * @param {Function} apiUrlFn - Function that returns a string for the back-end - * API endpoint, in case the string has dynamic query parameters (e.g. - * show_system_indices) rather than supply the getPageData param. - * @param {Function} getPageData - (Optional) Function to fetch page data, if - * simply passing the API string isn't workable. - * @param {Object} defaultData - Initial model data to populate - * @param {String} reactNodeId - DOM element ID of the element for mounting - * the view's main React component - * @param {Service} $injector - Angular dependency injection service - * @param {Service} $scope - Angular view data binding service - * @param {Boolean} options.enableTimeFilter - Whether to show the time filter - * @param {Boolean} options.enableAutoRefresh - Whether to show the auto - * refresh control - */ - constructor({ - title = '', - pageTitle = '', - api = '', - apiUrlFn, - getPageData: _getPageData = getPageData, - defaultData, - reactNodeId = null, // WIP: https://github.com/elastic/x-pack-kibana/issues/5198 - $scope, - $injector, - options = {}, - alerts = { shouldFetch: false, options: {} }, - fetchDataImmediately = true, - telemetryPageViewTitle = '', - }) { - const titleService = $injector.get('title'); - const $executor = $injector.get('$executor'); - const $window = $injector.get('$window'); - const config = $injector.get('config'); - - titleService($scope.cluster, title); - - $scope.pageTitle = pageTitle; - this.setPageTitle = (title) => ($scope.pageTitle = title); - $scope.pageData = this.data = { ...defaultData }; - this._isDataInitialized = false; - this.reactNodeId = reactNodeId; - this.telemetryPageViewTitle = telemetryPageViewTitle || title; - - let deferTimer; - let zoomInLevel = 0; - - const popstateHandler = () => zoomInLevel > 0 && --zoomInLevel; - const removePopstateHandler = () => $window.removeEventListener('popstate', popstateHandler); - const addPopstateHandler = () => $window.addEventListener('popstate', popstateHandler); - - this.zoomInfo = { - zoomOutHandler: () => $window.history.back(), - showZoomOutBtn: () => zoomInLevel > 0, - }; - - const { enableTimeFilter = true, enableAutoRefresh = true } = options; - - async function fetchAlerts() { - const globalState = $injector.get('globalState'); - const bounds = Legacy.shims.timefilter.getBounds(); - const min = bounds.min?.valueOf(); - const max = bounds.max?.valueOf(); - const options = alerts.options || {}; - try { - return await Legacy.shims.http.post( - `/api/monitoring/v1/alert/${globalState.cluster_uuid}/status`, - { - body: JSON.stringify({ - alertTypeIds: options.alertTypeIds, - filters: options.filters, - timeRange: { - min, - max, - }, - }), - } - ); - } catch (err) { - Legacy.shims.toastNotifications.addDanger({ - title: 'Error fetching alert status', - text: err.message, - }); - } - } - - this.updateData = () => { - if (this.updateDataPromise) { - // Do not sent another request if one is inflight - // See https://github.com/elastic/kibana/issues/24082 - this.updateDataPromise.cancel(); - this.updateDataPromise = null; - } - const _api = apiUrlFn ? apiUrlFn() : api; - const promises = [_getPageData($injector, _api, this.getPaginationRouteOptions())]; - if (alerts.shouldFetch) { - promises.push(fetchAlerts()); - } - if (isSetupModeFeatureEnabled(SetupModeFeature.MetricbeatMigration)) { - promises.push(updateSetupModeData()); - } - this.updateDataPromise = new PromiseWithCancel(Promise.allSettled(promises)); - return this.updateDataPromise.promise().then(([pageData, alerts]) => { - $scope.$apply(() => { - this._isDataInitialized = true; // render will replace loading screen with the react component - $scope.pageData = this.data = pageData.value; // update the view's data with the fetch result - $scope.alerts = this.alerts = alerts && alerts.value ? alerts.value : {}; - }); - }); - }; - - $scope.$applyAsync(() => { - const timefilter = Legacy.shims.timefilter; - - if (enableTimeFilter === false) { - timefilter.disableTimeRangeSelector(); - } else { - timefilter.enableTimeRangeSelector(); - } - - if (enableAutoRefresh === false) { - timefilter.disableAutoRefreshSelector(); - } else { - timefilter.enableAutoRefreshSelector(); - } - - // needed for chart pages - this.onBrush = ({ xaxis }) => { - removePopstateHandler(); - const { to, from } = xaxis; - const timezone = config.get('dateFormat:tz'); - const offset = getOffsetInMS(timezone); - timefilter.setTime({ - from: moment(from - offset), - to: moment(to - offset), - mode: 'absolute', - }); - $executor.cancel(); - $executor.run(); - ++zoomInLevel; - clearTimeout(deferTimer); - /* - Needed to defer 'popstate' event, so it does not fire immediately after it's added. - 10ms is to make sure the event is not added with the same code digest - */ - deferTimer = setTimeout(() => addPopstateHandler(), 10); - }; - - // Render loading state - this.renderReact(null, true); - fetchDataImmediately && this.updateData(); - }); - - $executor.register({ - execute: () => this.updateData(), - }); - $executor.start($scope); - $scope.$on('$destroy', () => { - clearTimeout(deferTimer); - removePopstateHandler(); - const targetElement = document.getElementById(this.reactNodeId); - if (targetElement) { - // WIP https://github.com/elastic/x-pack-kibana/issues/5198 - unmountComponentAtNode(targetElement); - } - $executor.destroy(); - }); - - this.setTitle = (title) => titleService($scope.cluster, title); - } - - renderReact(component, trackPageView = false) { - const renderElement = document.getElementById(this.reactNodeId); - if (!renderElement) { - console.warn(`"#${this.reactNodeId}" element has not been added to the DOM yet`); - return; - } - const I18nContext = Legacy.shims.I18nContext; - const wrappedComponent = ( - - - - - - - {!this._isDataInitialized ? ( - - ) : ( - component - )} - - - - ); - render(wrappedComponent, renderElement); - } - - getPaginationRouteOptions() { - return {}; - } -} diff --git a/x-pack/plugins/monitoring/public/views/base_eui_table_controller.js b/x-pack/plugins/monitoring/public/views/base_eui_table_controller.js deleted file mode 100644 index 0520ce3f10de5..0000000000000 --- a/x-pack/plugins/monitoring/public/views/base_eui_table_controller.js +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { MonitoringViewBaseController } from './'; -import { euiTableStorageGetter, euiTableStorageSetter } from '../components/table'; -import { EUI_SORT_ASCENDING } from '../../common/constants'; - -const PAGE_SIZE_OPTIONS = [5, 10, 20, 50]; - -/** - * Class to manage common instantiation behaviors in a view controller - * And add persistent state to a table: - * - page index: in table pagination, which page are we looking at - * - filter text: what filter was entered in the table's filter bar - * - sortKey: which column field of table data is used for sorting - * - sortOrder: is sorting ordered ascending or descending - * - * This is expected to be extended, and behavior enabled using super(); - */ -export class MonitoringViewBaseEuiTableController extends MonitoringViewBaseController { - /** - * Create a table view controller - * - used by parent class: - * @param {String} title - Title of the page - * @param {Function} getPageData - Function to fetch page data - * @param {Service} $injector - Angular dependency injection service - * @param {Service} $scope - Angular view data binding service - * @param {Boolean} options.enableTimeFilter - Whether to show the time filter - * @param {Boolean} options.enableAutoRefresh - Whether to show the auto refresh control - * - specific to this class: - * @param {String} storageKey - the namespace that will be used to keep the state data in the Monitoring localStorage object - * - */ - constructor(args) { - super(args); - const { storageKey, $injector } = args; - const storage = $injector.get('localStorage'); - - const getLocalStorageData = euiTableStorageGetter(storageKey); - const setLocalStorageData = euiTableStorageSetter(storageKey); - const { page, sort } = getLocalStorageData(storage); - - this.pagination = { - pageSize: 20, - initialPageSize: 20, - pageIndex: 0, - initialPageIndex: 0, - pageSizeOptions: PAGE_SIZE_OPTIONS, - }; - - if (page) { - if (!PAGE_SIZE_OPTIONS.includes(page.size)) { - page.size = 20; - } - this.setPagination(page); - } - - this.setSorting(sort); - - this.onTableChange = ({ page, sort }) => { - this.setPagination(page); - this.setSorting({ sort }); - setLocalStorageData(storage, { - page, - sort: { - sort, - }, - }); - if (this.onTableChangeRender) { - this.onTableChangeRender(); - } - }; - - // For pages where we do not fetch immediately, we want to fetch after pagination is applied - args.fetchDataImmediately === false && this.updateData(); - } - - setPagination(page) { - this.pagination = { - initialPageSize: page.size, - pageSize: page.size, - initialPageIndex: page.index, - pageIndex: page.index, - pageSizeOptions: PAGE_SIZE_OPTIONS, - }; - } - - setSorting(sort) { - this.sorting = sort || { sort: {} }; - - if (!this.sorting.sort.field) { - this.sorting.sort.field = 'name'; - } - if (!this.sorting.sort.direction) { - this.sorting.sort.direction = EUI_SORT_ASCENDING; - } - } - - setQueryText(queryText) { - this.queryText = queryText; - } - - getPaginationRouteOptions() { - if (!this.pagination || !this.sorting) { - return {}; - } - - return { - pagination: { - size: this.pagination.pageSize, - index: this.pagination.pageIndex, - }, - ...this.sorting, - queryText: this.queryText, - }; - } - - getPaginationTableProps(pagination) { - return { - sorting: this.sorting, - pagination: pagination, - onTableChange: this.onTableChange, - fetchMoreData: async ({ page, sort, queryText }) => { - this.setPagination(page); - this.setSorting(sort); - this.setQueryText(queryText); - await this.updateData(); - }, - }; - } -} diff --git a/x-pack/plugins/monitoring/public/views/base_table_controller.js b/x-pack/plugins/monitoring/public/views/base_table_controller.js deleted file mode 100644 index a066a91e48c8b..0000000000000 --- a/x-pack/plugins/monitoring/public/views/base_table_controller.js +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { MonitoringViewBaseController } from './'; -import { tableStorageGetter, tableStorageSetter } from '../components/table'; - -/** - * Class to manage common instantiation behaviors in a view controller - * And add persistent state to a table: - * - page index: in table pagination, which page are we looking at - * - filter text: what filter was entered in the table's filter bar - * - sortKey: which column field of table data is used for sorting - * - sortOrder: is sorting ordered ascending or descending - * - * This is expected to be extended, and behavior enabled using super(); - */ -export class MonitoringViewBaseTableController extends MonitoringViewBaseController { - /** - * Create a table view controller - * - used by parent class: - * @param {String} title - Title of the page - * @param {Function} getPageData - Function to fetch page data - * @param {Service} $injector - Angular dependency injection service - * @param {Service} $scope - Angular view data binding service - * @param {Boolean} options.enableTimeFilter - Whether to show the time filter - * @param {Boolean} options.enableAutoRefresh - Whether to show the auto refresh control - * - specific to this class: - * @param {String} storageKey - the namespace that will be used to keep the state data in the Monitoring localStorage object - * - */ - constructor(args) { - super(args); - const { storageKey, $injector } = args; - const storage = $injector.get('localStorage'); - - const getLocalStorageData = tableStorageGetter(storageKey); - const setLocalStorageData = tableStorageSetter(storageKey); - const { pageIndex, filterText, sortKey, sortOrder } = getLocalStorageData(storage); - - this.pageIndex = pageIndex; - this.filterText = filterText; - this.sortKey = sortKey; - this.sortOrder = sortOrder; - - this.onNewState = (newState) => { - setLocalStorageData(storage, newState); - }; - } -} diff --git a/x-pack/plugins/monitoring/public/views/beats/beat/get_page_data.js b/x-pack/plugins/monitoring/public/views/beats/beat/get_page_data.js deleted file mode 100644 index 7f87fa413d8ca..0000000000000 --- a/x-pack/plugins/monitoring/public/views/beats/beat/get_page_data.js +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { ajaxErrorHandlersProvider } from '../../../lib/ajax_error_handler'; -import { Legacy } from '../../../legacy_shims'; - -export function getPageData($injector) { - const $http = $injector.get('$http'); - const $route = $injector.get('$route'); - const globalState = $injector.get('globalState'); - const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/beats/beat/${$route.current.params.beatUuid}`; - const timeBounds = Legacy.shims.timefilter.getBounds(); - - return $http - .post(url, { - ccs: globalState.ccs, - timeRange: { - min: timeBounds.min.toISOString(), - max: timeBounds.max.toISOString(), - }, - }) - .then((response) => response.data) - .catch((err) => { - const Private = $injector.get('Private'); - const ajaxErrorHandlers = Private(ajaxErrorHandlersProvider); - return ajaxErrorHandlers(err); - }); -} diff --git a/x-pack/plugins/monitoring/public/views/beats/beat/index.html b/x-pack/plugins/monitoring/public/views/beats/beat/index.html deleted file mode 100644 index 6ae727e31cbeb..0000000000000 --- a/x-pack/plugins/monitoring/public/views/beats/beat/index.html +++ /dev/null @@ -1,11 +0,0 @@ - -
- -
diff --git a/x-pack/plugins/monitoring/public/views/beats/beat/index.js b/x-pack/plugins/monitoring/public/views/beats/beat/index.js deleted file mode 100644 index f1a171a19cd89..0000000000000 --- a/x-pack/plugins/monitoring/public/views/beats/beat/index.js +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { find } from 'lodash'; -import { i18n } from '@kbn/i18n'; -import { uiRoutes } from '../../../angular/helpers/routes'; -import { routeInitProvider } from '../../../lib/route_init'; -import { MonitoringViewBaseController } from '../../'; -import { getPageData } from './get_page_data'; -import template from './index.html'; -import { CODE_PATH_BEATS } from '../../../../common/constants'; -import { Beat } from '../../../components/beats/beat'; - -uiRoutes.when('/beats/beat/:beatUuid', { - template, - resolve: { - clusters: function (Private) { - const routeInit = Private(routeInitProvider); - return routeInit({ codePaths: [CODE_PATH_BEATS] }); - }, - pageData: getPageData, - }, - controllerAs: 'beat', - controller: class BeatDetail extends MonitoringViewBaseController { - constructor($injector, $scope) { - // breadcrumbs + page title - const $route = $injector.get('$route'); - const globalState = $injector.get('globalState'); - $scope.cluster = find($route.current.locals.clusters, { - cluster_uuid: globalState.cluster_uuid, - }); - - const pageData = $route.current.locals.pageData; - super({ - title: i18n.translate('xpack.monitoring.beats.instance.routeTitle', { - defaultMessage: 'Beats - {instanceName} - Overview', - values: { - instanceName: pageData.summary.name, - }, - }), - pageTitle: i18n.translate('xpack.monitoring.beats.instance.pageTitle', { - defaultMessage: 'Beat instance: {beatName}', - values: { - beatName: pageData.summary.name, - }, - }), - telemetryPageViewTitle: 'beats_instance', - getPageData, - $scope, - $injector, - reactNodeId: 'monitoringBeatsInstanceApp', - }); - - this.data = pageData; - $scope.$watch( - () => this.data, - (data) => { - this.renderReact( - - ); - } - ); - } - }, -}); diff --git a/x-pack/plugins/monitoring/public/views/beats/listing/get_page_data.js b/x-pack/plugins/monitoring/public/views/beats/listing/get_page_data.js deleted file mode 100644 index 99366f05f3ad4..0000000000000 --- a/x-pack/plugins/monitoring/public/views/beats/listing/get_page_data.js +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { ajaxErrorHandlersProvider } from '../../../lib/ajax_error_handler'; -import { Legacy } from '../../../legacy_shims'; - -export function getPageData($injector) { - const $http = $injector.get('$http'); - const globalState = $injector.get('globalState'); - const timeBounds = Legacy.shims.timefilter.getBounds(); - const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/beats/beats`; - - return $http - .post(url, { - ccs: globalState.ccs, - timeRange: { - min: timeBounds.min.toISOString(), - max: timeBounds.max.toISOString(), - }, - }) - .then((response) => response.data) - .catch((err) => { - const Private = $injector.get('Private'); - const ajaxErrorHandlers = Private(ajaxErrorHandlersProvider); - return ajaxErrorHandlers(err); - }); -} diff --git a/x-pack/plugins/monitoring/public/views/beats/listing/index.html b/x-pack/plugins/monitoring/public/views/beats/listing/index.html deleted file mode 100644 index 0ce66a6848dfd..0000000000000 --- a/x-pack/plugins/monitoring/public/views/beats/listing/index.html +++ /dev/null @@ -1,7 +0,0 @@ - -
-
\ No newline at end of file diff --git a/x-pack/plugins/monitoring/public/views/beats/listing/index.js b/x-pack/plugins/monitoring/public/views/beats/listing/index.js deleted file mode 100644 index eae74d8a08b9e..0000000000000 --- a/x-pack/plugins/monitoring/public/views/beats/listing/index.js +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { find } from 'lodash'; -import { i18n } from '@kbn/i18n'; -import { uiRoutes } from '../../../angular/helpers/routes'; -import { routeInitProvider } from '../../../lib/route_init'; -import { MonitoringViewBaseEuiTableController } from '../../'; -import { getPageData } from './get_page_data'; -import template from './index.html'; -import React from 'react'; -import { Listing } from '../../../components/beats/listing/listing'; -import { SetupModeRenderer } from '../../../components/renderers'; -import { SetupModeContext } from '../../../components/setup_mode/setup_mode_context'; -import { CODE_PATH_BEATS, BEATS_SYSTEM_ID } from '../../../../common/constants'; - -uiRoutes.when('/beats/beats', { - template, - resolve: { - clusters: function (Private) { - const routeInit = Private(routeInitProvider); - return routeInit({ codePaths: [CODE_PATH_BEATS] }); - }, - pageData: getPageData, - }, - controllerAs: 'beats', - controller: class BeatsListing extends MonitoringViewBaseEuiTableController { - constructor($injector, $scope) { - // breadcrumbs + page title - const $route = $injector.get('$route'); - const globalState = $injector.get('globalState'); - $scope.cluster = find($route.current.locals.clusters, { - cluster_uuid: globalState.cluster_uuid, - }); - - super({ - title: i18n.translate('xpack.monitoring.beats.routeTitle', { defaultMessage: 'Beats' }), - pageTitle: i18n.translate('xpack.monitoring.beats.listing.pageTitle', { - defaultMessage: 'Beats listing', - }), - telemetryPageViewTitle: 'beats_listing', - storageKey: 'beats.beats', - getPageData, - reactNodeId: 'monitoringBeatsInstancesApp', - $scope, - $injector, - }); - - this.data = $route.current.locals.pageData; - this.scope = $scope; - this.injector = $injector; - this.onTableChangeRender = this.renderComponent; - - $scope.$watch( - () => this.data, - () => this.renderComponent() - ); - } - - renderComponent() { - const { sorting, pagination, onTableChange } = this.scope.beats; - this.renderReact( - ( - - {flyoutComponent} - - {bottomBarComponent} - - )} - /> - ); - } - }, -}); diff --git a/x-pack/plugins/monitoring/public/views/beats/overview/get_page_data.js b/x-pack/plugins/monitoring/public/views/beats/overview/get_page_data.js deleted file mode 100644 index 497ed8cdb0e74..0000000000000 --- a/x-pack/plugins/monitoring/public/views/beats/overview/get_page_data.js +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { ajaxErrorHandlersProvider } from '../../../lib/ajax_error_handler'; -import { Legacy } from '../../../legacy_shims'; - -export function getPageData($injector) { - const $http = $injector.get('$http'); - const globalState = $injector.get('globalState'); - const timeBounds = Legacy.shims.timefilter.getBounds(); - const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/beats`; - - return $http - .post(url, { - ccs: globalState.ccs, - timeRange: { - min: timeBounds.min.toISOString(), - max: timeBounds.max.toISOString(), - }, - }) - .then((response) => response.data) - .catch((err) => { - const Private = $injector.get('Private'); - const ajaxErrorHandlers = Private(ajaxErrorHandlersProvider); - return ajaxErrorHandlers(err); - }); -} diff --git a/x-pack/plugins/monitoring/public/views/beats/overview/index.html b/x-pack/plugins/monitoring/public/views/beats/overview/index.html deleted file mode 100644 index 0b827c96f68fd..0000000000000 --- a/x-pack/plugins/monitoring/public/views/beats/overview/index.html +++ /dev/null @@ -1,7 +0,0 @@ - -
-
diff --git a/x-pack/plugins/monitoring/public/views/beats/overview/index.js b/x-pack/plugins/monitoring/public/views/beats/overview/index.js deleted file mode 100644 index 475a63d440c76..0000000000000 --- a/x-pack/plugins/monitoring/public/views/beats/overview/index.js +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { find } from 'lodash'; -import { i18n } from '@kbn/i18n'; -import { uiRoutes } from '../../../angular/helpers/routes'; -import { routeInitProvider } from '../../../lib/route_init'; -import { MonitoringViewBaseController } from '../../'; -import { getPageData } from './get_page_data'; -import template from './index.html'; -import { CODE_PATH_BEATS } from '../../../../common/constants'; -import { BeatsOverview } from '../../../components/beats/overview'; - -uiRoutes.when('/beats', { - template, - resolve: { - clusters: function (Private) { - const routeInit = Private(routeInitProvider); - return routeInit({ codePaths: [CODE_PATH_BEATS] }); - }, - pageData: getPageData, - }, - controllerAs: 'beats', - controller: class extends MonitoringViewBaseController { - constructor($injector, $scope) { - // breadcrumbs + page title - const $route = $injector.get('$route'); - const globalState = $injector.get('globalState'); - $scope.cluster = find($route.current.locals.clusters, { - cluster_uuid: globalState.cluster_uuid, - }); - - super({ - title: i18n.translate('xpack.monitoring.beats.overview.routeTitle', { - defaultMessage: 'Beats - Overview', - }), - pageTitle: i18n.translate('xpack.monitoring.beats.overview.pageTitle', { - defaultMessage: 'Beats overview', - }), - getPageData, - $scope, - $injector, - reactNodeId: 'monitoringBeatsOverviewApp', - }); - - this.data = $route.current.locals.pageData; - $scope.$watch( - () => this.data, - (data) => { - this.renderReact( - - ); - } - ); - } - }, -}); diff --git a/x-pack/plugins/monitoring/public/views/cluster/listing/index.html b/x-pack/plugins/monitoring/public/views/cluster/listing/index.html deleted file mode 100644 index 713ca8fb1ffc9..0000000000000 --- a/x-pack/plugins/monitoring/public/views/cluster/listing/index.html +++ /dev/null @@ -1,3 +0,0 @@ - -
-
diff --git a/x-pack/plugins/monitoring/public/views/cluster/listing/index.js b/x-pack/plugins/monitoring/public/views/cluster/listing/index.js deleted file mode 100644 index 8b365292aeb13..0000000000000 --- a/x-pack/plugins/monitoring/public/views/cluster/listing/index.js +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { i18n } from '@kbn/i18n'; -import { uiRoutes } from '../../../angular/helpers/routes'; -import { routeInitProvider } from '../../../lib/route_init'; -import { MonitoringViewBaseEuiTableController } from '../../'; -import template from './index.html'; -import { Listing } from '../../../components/cluster/listing'; -import { CODE_PATH_ALL } from '../../../../common/constants'; -import { EnableAlertsModal } from '../../../alerts/enable_alerts_modal.tsx'; - -const CODE_PATHS = [CODE_PATH_ALL]; - -const getPageData = ($injector) => { - const monitoringClusters = $injector.get('monitoringClusters'); - return monitoringClusters(undefined, undefined, CODE_PATHS); -}; - -const getAlerts = (clusters) => { - return clusters.reduce((alerts, cluster) => ({ ...alerts, ...cluster.alerts.list }), {}); -}; - -uiRoutes - .when('/home', { - template, - resolve: { - clusters: (Private) => { - const routeInit = Private(routeInitProvider); - return routeInit({ - codePaths: CODE_PATHS, - fetchAllClusters: true, - unsetGlobalState: true, - }).then((clusters) => { - if (!clusters || !clusters.length) { - window.location.hash = '#/no-data'; - return Promise.reject(); - } - if (clusters.length === 1) { - // Bypass the cluster listing if there is just 1 cluster - window.history.replaceState(null, null, '#/overview'); - return Promise.reject(); - } - return clusters; - }); - }, - }, - controllerAs: 'clusters', - controller: class ClustersList extends MonitoringViewBaseEuiTableController { - constructor($injector, $scope) { - super({ - storageKey: 'clusters', - pageTitle: i18n.translate('xpack.monitoring.cluster.listing.pageTitle', { - defaultMessage: 'Cluster listing', - }), - getPageData, - $scope, - $injector, - reactNodeId: 'monitoringClusterListingApp', - telemetryPageViewTitle: 'cluster_listing', - }); - - const $route = $injector.get('$route'); - const globalState = $injector.get('globalState'); - const storage = $injector.get('localStorage'); - const showLicenseExpiration = $injector.get('showLicenseExpiration'); - - this.data = $route.current.locals.clusters; - - $scope.$watch( - () => this.data, - (data) => { - this.renderReact( - <> - - - - ); - } - ); - } - }, - }) - .otherwise({ redirectTo: '/loading' }); diff --git a/x-pack/plugins/monitoring/public/views/cluster/overview/index.html b/x-pack/plugins/monitoring/public/views/cluster/overview/index.html deleted file mode 100644 index 1762ee1c2a282..0000000000000 --- a/x-pack/plugins/monitoring/public/views/cluster/overview/index.html +++ /dev/null @@ -1,3 +0,0 @@ - -
-
diff --git a/x-pack/plugins/monitoring/public/views/cluster/overview/index.js b/x-pack/plugins/monitoring/public/views/cluster/overview/index.js deleted file mode 100644 index 20e694ad8548f..0000000000000 --- a/x-pack/plugins/monitoring/public/views/cluster/overview/index.js +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { isEmpty } from 'lodash'; -import { i18n } from '@kbn/i18n'; -import { uiRoutes } from '../../../angular/helpers/routes'; -import { routeInitProvider } from '../../../lib/route_init'; -import template from './index.html'; -import { MonitoringViewBaseController } from '../../'; -import { Overview } from '../../../components/cluster/overview'; -import { SetupModeRenderer } from '../../../components/renderers'; -import { SetupModeContext } from '../../../components/setup_mode/setup_mode_context'; -import { CODE_PATH_ALL } from '../../../../common/constants'; -import { EnableAlertsModal } from '../../../alerts/enable_alerts_modal.tsx'; - -const CODE_PATHS = [CODE_PATH_ALL]; - -uiRoutes.when('/overview', { - template, - resolve: { - clusters(Private) { - // checks license info of all monitored clusters for multi-cluster monitoring usage and capability - const routeInit = Private(routeInitProvider); - return routeInit({ codePaths: CODE_PATHS }); - }, - }, - controllerAs: 'monitoringClusterOverview', - controller: class extends MonitoringViewBaseController { - constructor($injector, $scope) { - const monitoringClusters = $injector.get('monitoringClusters'); - const globalState = $injector.get('globalState'); - const showLicenseExpiration = $injector.get('showLicenseExpiration'); - - super({ - title: i18n.translate('xpack.monitoring.cluster.overviewTitle', { - defaultMessage: 'Overview', - }), - pageTitle: i18n.translate('xpack.monitoring.cluster.overview.pageTitle', { - defaultMessage: 'Cluster overview', - }), - defaultData: {}, - getPageData: async () => { - const clusters = await monitoringClusters( - globalState.cluster_uuid, - globalState.ccs, - CODE_PATHS - ); - return clusters[0]; - }, - reactNodeId: 'monitoringClusterOverviewApp', - $scope, - $injector, - alerts: { - shouldFetch: true, - }, - telemetryPageViewTitle: 'cluster_overview', - }); - - this.init = () => this.renderReact(null); - - $scope.$watch( - () => this.data, - async (data) => { - if (isEmpty(data)) { - return; - } - - this.renderReact( - ( - - {flyoutComponent} - - - {bottomBarComponent} - - )} - /> - ); - } - ); - } - }, -}); diff --git a/x-pack/plugins/monitoring/public/views/elasticsearch/ccr/get_page_data.js b/x-pack/plugins/monitoring/public/views/elasticsearch/ccr/get_page_data.js deleted file mode 100644 index 4f45038986332..0000000000000 --- a/x-pack/plugins/monitoring/public/views/elasticsearch/ccr/get_page_data.js +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { ajaxErrorHandlersProvider } from '../../../lib/ajax_error_handler'; -import { Legacy } from '../../../legacy_shims'; - -export function getPageData($injector) { - const $http = $injector.get('$http'); - const globalState = $injector.get('globalState'); - const timeBounds = Legacy.shims.timefilter.getBounds(); - const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/elasticsearch/ccr`; - - return $http - .post(url, { - ccs: globalState.ccs, - timeRange: { - min: timeBounds.min.toISOString(), - max: timeBounds.max.toISOString(), - }, - }) - .then((response) => response.data) - .catch((err) => { - const Private = $injector.get('Private'); - const ajaxErrorHandlers = Private(ajaxErrorHandlersProvider); - return ajaxErrorHandlers(err); - }); -} diff --git a/x-pack/plugins/monitoring/public/views/elasticsearch/ccr/index.html b/x-pack/plugins/monitoring/public/views/elasticsearch/ccr/index.html deleted file mode 100644 index ca0b036ae39e1..0000000000000 --- a/x-pack/plugins/monitoring/public/views/elasticsearch/ccr/index.html +++ /dev/null @@ -1,7 +0,0 @@ - -
-
diff --git a/x-pack/plugins/monitoring/public/views/elasticsearch/ccr/index.js b/x-pack/plugins/monitoring/public/views/elasticsearch/ccr/index.js deleted file mode 100644 index 91cc9c8782b22..0000000000000 --- a/x-pack/plugins/monitoring/public/views/elasticsearch/ccr/index.js +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { i18n } from '@kbn/i18n'; -import { uiRoutes } from '../../../angular/helpers/routes'; -import { getPageData } from './get_page_data'; -import { routeInitProvider } from '../../../lib/route_init'; -import template from './index.html'; -import { Ccr } from '../../../components/elasticsearch/ccr'; -import { MonitoringViewBaseController } from '../../base_controller'; -import { - CODE_PATH_ELASTICSEARCH, - RULE_CCR_READ_EXCEPTIONS, - ELASTICSEARCH_SYSTEM_ID, -} from '../../../../common/constants'; -import { SetupModeRenderer } from '../../../components/renderers'; -import { SetupModeContext } from '../../../components/setup_mode/setup_mode_context'; - -uiRoutes.when('/elasticsearch/ccr', { - template, - resolve: { - clusters: function (Private) { - const routeInit = Private(routeInitProvider); - return routeInit({ codePaths: [CODE_PATH_ELASTICSEARCH] }); - }, - pageData: getPageData, - }, - controllerAs: 'elasticsearchCcr', - controller: class ElasticsearchCcrController extends MonitoringViewBaseController { - constructor($injector, $scope) { - super({ - title: i18n.translate('xpack.monitoring.elasticsearch.ccr.routeTitle', { - defaultMessage: 'Elasticsearch - Ccr', - }), - pageTitle: i18n.translate('xpack.monitoring.elasticsearch.ccr.pageTitle', { - defaultMessage: 'Elasticsearch Ccr', - }), - reactNodeId: 'elasticsearchCcrReact', - getPageData, - $scope, - $injector, - alerts: { - shouldFetch: true, - options: { - alertTypeIds: [RULE_CCR_READ_EXCEPTIONS], - }, - }, - }); - - $scope.$watch( - () => this.data, - (data) => { - if (!data) { - return; - } - this.renderReact( - ( - - {flyoutComponent} - - {bottomBarComponent} - - )} - /> - ); - } - ); - } - }, -}); diff --git a/x-pack/plugins/monitoring/public/views/elasticsearch/ccr/shard/get_page_data.js b/x-pack/plugins/monitoring/public/views/elasticsearch/ccr/shard/get_page_data.js deleted file mode 100644 index ca1aad39e3610..0000000000000 --- a/x-pack/plugins/monitoring/public/views/elasticsearch/ccr/shard/get_page_data.js +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { ajaxErrorHandlersProvider } from '../../../../lib/ajax_error_handler'; -import { Legacy } from '../../../../legacy_shims'; - -export function getPageData($injector) { - const $http = $injector.get('$http'); - const $route = $injector.get('$route'); - const globalState = $injector.get('globalState'); - const timeBounds = Legacy.shims.timefilter.getBounds(); - const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/elasticsearch/ccr/${$route.current.params.index}/shard/${$route.current.params.shardId}`; - - return $http - .post(url, { - ccs: globalState.ccs, - timeRange: { - min: timeBounds.min.toISOString(), - max: timeBounds.max.toISOString(), - }, - }) - .then((response) => response.data) - .catch((err) => { - const Private = $injector.get('Private'); - const ajaxErrorHandlers = Private(ajaxErrorHandlersProvider); - return ajaxErrorHandlers(err); - }); -} diff --git a/x-pack/plugins/monitoring/public/views/elasticsearch/ccr/shard/index.html b/x-pack/plugins/monitoring/public/views/elasticsearch/ccr/shard/index.html deleted file mode 100644 index 76469e5d9add5..0000000000000 --- a/x-pack/plugins/monitoring/public/views/elasticsearch/ccr/shard/index.html +++ /dev/null @@ -1,8 +0,0 @@ - -
-
diff --git a/x-pack/plugins/monitoring/public/views/elasticsearch/ccr/shard/index.js b/x-pack/plugins/monitoring/public/views/elasticsearch/ccr/shard/index.js deleted file mode 100644 index 767fb18685633..0000000000000 --- a/x-pack/plugins/monitoring/public/views/elasticsearch/ccr/shard/index.js +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { i18n } from '@kbn/i18n'; -import { get } from 'lodash'; -import { uiRoutes } from '../../../../angular/helpers/routes'; -import { getPageData } from './get_page_data'; -import { routeInitProvider } from '../../../../lib/route_init'; -import template from './index.html'; -import { MonitoringViewBaseController } from '../../../base_controller'; -import { CcrShard } from '../../../../components/elasticsearch/ccr_shard'; -import { - CODE_PATH_ELASTICSEARCH, - RULE_CCR_READ_EXCEPTIONS, - ELASTICSEARCH_SYSTEM_ID, -} from '../../../../../common/constants'; -import { SetupModeRenderer } from '../../../../components/renderers'; -import { SetupModeContext } from '../../../../components/setup_mode/setup_mode_context'; - -uiRoutes.when('/elasticsearch/ccr/:index/shard/:shardId', { - template, - resolve: { - clusters: function (Private) { - const routeInit = Private(routeInitProvider); - return routeInit({ codePaths: [CODE_PATH_ELASTICSEARCH] }); - }, - pageData: getPageData, - }, - controllerAs: 'elasticsearchCcr', - controller: class ElasticsearchCcrController extends MonitoringViewBaseController { - constructor($injector, $scope, pageData) { - const $route = $injector.get('$route'); - super({ - title: i18n.translate('xpack.monitoring.elasticsearch.ccr.shard.routeTitle', { - defaultMessage: 'Elasticsearch - Ccr - Shard', - }), - reactNodeId: 'elasticsearchCcrShardReact', - getPageData, - $scope, - $injector, - alerts: { - shouldFetch: true, - options: { - alertTypeIds: [RULE_CCR_READ_EXCEPTIONS], - filters: [ - { - shardId: $route.current.pathParams.shardId, - }, - ], - }, - }, - }); - - $scope.instance = i18n.translate('xpack.monitoring.elasticsearch.ccr.shard.instanceTitle', { - defaultMessage: 'Index: {followerIndex} Shard: {shardId}', - values: { - followerIndex: get(pageData, 'stat.follower_index'), - shardId: get(pageData, 'stat.shard_id'), - }, - }); - - $scope.$watch( - () => this.data, - (data) => { - if (!data) { - return; - } - - this.setPageTitle( - i18n.translate('xpack.monitoring.elasticsearch.ccr.shard.pageTitle', { - defaultMessage: 'Elasticsearch Ccr Shard - Index: {followerIndex} Shard: {shardId}', - values: { - followerIndex: get( - pageData, - 'stat.follower.index', - get(pageData, 'stat.follower_index') - ), - shardId: get( - pageData, - 'stat.follower.shard.number', - get(pageData, 'stat.shard_id') - ), - }, - }) - ); - - this.renderReact( - ( - - {flyoutComponent} - - {bottomBarComponent} - - )} - /> - ); - } - ); - } - }, -}); diff --git a/x-pack/plugins/monitoring/public/views/elasticsearch/index/advanced/index.html b/x-pack/plugins/monitoring/public/views/elasticsearch/index/advanced/index.html deleted file mode 100644 index 159376148d173..0000000000000 --- a/x-pack/plugins/monitoring/public/views/elasticsearch/index/advanced/index.html +++ /dev/null @@ -1,8 +0,0 @@ - -
-
diff --git a/x-pack/plugins/monitoring/public/views/elasticsearch/index/advanced/index.js b/x-pack/plugins/monitoring/public/views/elasticsearch/index/advanced/index.js deleted file mode 100644 index 9276527951612..0000000000000 --- a/x-pack/plugins/monitoring/public/views/elasticsearch/index/advanced/index.js +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -/** - * Controller for Advanced Index Detail - */ -import React from 'react'; -import { i18n } from '@kbn/i18n'; -import { uiRoutes } from '../../../../angular/helpers/routes'; -import { ajaxErrorHandlersProvider } from '../../../../lib/ajax_error_handler'; -import { routeInitProvider } from '../../../../lib/route_init'; -import template from './index.html'; -import { Legacy } from '../../../../legacy_shims'; -import { AdvancedIndex } from '../../../../components/elasticsearch/index/advanced'; -import { MonitoringViewBaseController } from '../../../base_controller'; -import { - CODE_PATH_ELASTICSEARCH, - RULE_LARGE_SHARD_SIZE, - ELASTICSEARCH_SYSTEM_ID, -} from '../../../../../common/constants'; -import { SetupModeContext } from '../../../../components/setup_mode/setup_mode_context'; -import { SetupModeRenderer } from '../../../../components/renderers'; - -function getPageData($injector) { - const globalState = $injector.get('globalState'); - const $route = $injector.get('$route'); - const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/elasticsearch/indices/${$route.current.params.index}`; - const $http = $injector.get('$http'); - const timeBounds = Legacy.shims.timefilter.getBounds(); - - return $http - .post(url, { - ccs: globalState.ccs, - timeRange: { - min: timeBounds.min.toISOString(), - max: timeBounds.max.toISOString(), - }, - is_advanced: true, - }) - .then((response) => response.data) - .catch((err) => { - const Private = $injector.get('Private'); - const ajaxErrorHandlers = Private(ajaxErrorHandlersProvider); - return ajaxErrorHandlers(err); - }); -} - -uiRoutes.when('/elasticsearch/indices/:index/advanced', { - template, - resolve: { - clusters: function (Private) { - const routeInit = Private(routeInitProvider); - return routeInit({ codePaths: [CODE_PATH_ELASTICSEARCH] }); - }, - pageData: getPageData, - }, - controllerAs: 'monitoringElasticsearchAdvancedIndexApp', - controller: class extends MonitoringViewBaseController { - constructor($injector, $scope) { - const $route = $injector.get('$route'); - const indexName = $route.current.params.index; - - super({ - title: i18n.translate('xpack.monitoring.elasticsearch.indices.advanced.routeTitle', { - defaultMessage: 'Elasticsearch - Indices - {indexName} - Advanced', - values: { - indexName, - }, - }), - telemetryPageViewTitle: 'elasticsearch_index_advanced', - defaultData: {}, - getPageData, - reactNodeId: 'monitoringElasticsearchAdvancedIndexApp', - $scope, - $injector, - alerts: { - shouldFetch: true, - options: { - alertTypeIds: [RULE_LARGE_SHARD_SIZE], - filters: [ - { - shardIndex: $route.current.pathParams.index, - }, - ], - }, - }, - }); - - this.indexName = indexName; - - $scope.$watch( - () => this.data, - (data) => { - this.renderReact( - ( - - {flyoutComponent} - - {bottomBarComponent} - - )} - /> - ); - } - ); - } - }, -}); diff --git a/x-pack/plugins/monitoring/public/views/elasticsearch/index/index.html b/x-pack/plugins/monitoring/public/views/elasticsearch/index/index.html deleted file mode 100644 index 84d90f184358d..0000000000000 --- a/x-pack/plugins/monitoring/public/views/elasticsearch/index/index.html +++ /dev/null @@ -1,9 +0,0 @@ - -
-
diff --git a/x-pack/plugins/monitoring/public/views/elasticsearch/index/index.js b/x-pack/plugins/monitoring/public/views/elasticsearch/index/index.js deleted file mode 100644 index c9efb622ff9d1..0000000000000 --- a/x-pack/plugins/monitoring/public/views/elasticsearch/index/index.js +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -/** - * Controller for single index detail - */ -import React from 'react'; -import { i18n } from '@kbn/i18n'; -import { uiRoutes } from '../../../angular/helpers/routes'; -import { routeInitProvider } from '../../../lib/route_init'; -import { ajaxErrorHandlersProvider } from '../../../lib/ajax_error_handler'; -import template from './index.html'; -import { Legacy } from '../../../legacy_shims'; -import { labels } from '../../../components/elasticsearch/shard_allocation/lib/labels'; -import { indicesByNodes } from '../../../components/elasticsearch/shard_allocation/transformers/indices_by_nodes'; -import { Index } from '../../../components/elasticsearch/index/index'; -import { MonitoringViewBaseController } from '../../base_controller'; -import { - CODE_PATH_ELASTICSEARCH, - RULE_LARGE_SHARD_SIZE, - ELASTICSEARCH_SYSTEM_ID, -} from '../../../../common/constants'; -import { SetupModeContext } from '../../../components/setup_mode/setup_mode_context'; -import { SetupModeRenderer } from '../../../components/renderers'; - -function getPageData($injector) { - const $http = $injector.get('$http'); - const $route = $injector.get('$route'); - const globalState = $injector.get('globalState'); - const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/elasticsearch/indices/${$route.current.params.index}`; - const timeBounds = Legacy.shims.timefilter.getBounds(); - - return $http - .post(url, { - ccs: globalState.ccs, - timeRange: { - min: timeBounds.min.toISOString(), - max: timeBounds.max.toISOString(), - }, - is_advanced: false, - }) - .then((response) => response.data) - .catch((err) => { - const Private = $injector.get('Private'); - const ajaxErrorHandlers = Private(ajaxErrorHandlersProvider); - return ajaxErrorHandlers(err); - }); -} - -uiRoutes.when('/elasticsearch/indices/:index', { - template, - resolve: { - clusters(Private) { - const routeInit = Private(routeInitProvider); - return routeInit({ codePaths: [CODE_PATH_ELASTICSEARCH] }); - }, - pageData: getPageData, - }, - controllerAs: 'monitoringElasticsearchIndexApp', - controller: class extends MonitoringViewBaseController { - constructor($injector, $scope) { - const $route = $injector.get('$route'); - const indexName = $route.current.params.index; - - super({ - title: i18n.translate('xpack.monitoring.elasticsearch.indices.overview.routeTitle', { - defaultMessage: 'Elasticsearch - Indices - {indexName} - Overview', - values: { - indexName, - }, - }), - telemetryPageViewTitle: 'elasticsearch_index', - pageTitle: i18n.translate('xpack.monitoring.elasticsearch.indices.overview.pageTitle', { - defaultMessage: 'Index: {indexName}', - values: { - indexName, - }, - }), - defaultData: {}, - getPageData, - reactNodeId: 'monitoringElasticsearchIndexApp', - $scope, - $injector, - alerts: { - shouldFetch: true, - options: { - alertTypeIds: [RULE_LARGE_SHARD_SIZE], - filters: [ - { - shardIndex: $route.current.pathParams.index, - }, - ], - }, - }, - }); - - this.indexName = indexName; - const transformer = indicesByNodes(); - - $scope.$watch( - () => this.data, - (data) => { - if (!data || !data.shards) { - return; - } - - const shards = data.shards; - $scope.totalCount = shards.length; - $scope.showing = transformer(shards, data.nodes); - $scope.labels = labels.node; - if (shards.some((shard) => shard.state === 'UNASSIGNED')) { - $scope.labels = labels.indexWithUnassigned; - } else { - $scope.labels = labels.index; - } - - this.renderReact( - ( - - {flyoutComponent} - - {bottomBarComponent} - - )} - /> - ); - } - ); - } - }, -}); diff --git a/x-pack/plugins/monitoring/public/views/elasticsearch/indices/index.html b/x-pack/plugins/monitoring/public/views/elasticsearch/indices/index.html deleted file mode 100644 index 84013078e0ef1..0000000000000 --- a/x-pack/plugins/monitoring/public/views/elasticsearch/indices/index.html +++ /dev/null @@ -1,8 +0,0 @@ - -
-
diff --git a/x-pack/plugins/monitoring/public/views/elasticsearch/indices/index.js b/x-pack/plugins/monitoring/public/views/elasticsearch/indices/index.js deleted file mode 100644 index 5acff8be20dcf..0000000000000 --- a/x-pack/plugins/monitoring/public/views/elasticsearch/indices/index.js +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { i18n } from '@kbn/i18n'; -import { find } from 'lodash'; -import { uiRoutes } from '../../../angular/helpers/routes'; -import { routeInitProvider } from '../../../lib/route_init'; -import { MonitoringViewBaseEuiTableController } from '../../'; -import { ElasticsearchIndices } from '../../../components'; -import template from './index.html'; -import { - CODE_PATH_ELASTICSEARCH, - ELASTICSEARCH_SYSTEM_ID, - RULE_LARGE_SHARD_SIZE, -} from '../../../../common/constants'; -import { SetupModeRenderer } from '../../../components/renderers'; -import { SetupModeContext } from '../../../components/setup_mode/setup_mode_context'; - -uiRoutes.when('/elasticsearch/indices', { - template, - resolve: { - clusters(Private) { - const routeInit = Private(routeInitProvider); - return routeInit({ codePaths: [CODE_PATH_ELASTICSEARCH] }); - }, - }, - controllerAs: 'elasticsearchIndices', - controller: class ElasticsearchIndicesController extends MonitoringViewBaseEuiTableController { - constructor($injector, $scope) { - const $route = $injector.get('$route'); - const globalState = $injector.get('globalState'); - const features = $injector.get('features'); - - const { cluster_uuid: clusterUuid } = globalState; - $scope.cluster = find($route.current.locals.clusters, { cluster_uuid: clusterUuid }); - - let showSystemIndices = features.isEnabled('showSystemIndices', false); - - super({ - title: i18n.translate('xpack.monitoring.elasticsearch.indices.routeTitle', { - defaultMessage: 'Elasticsearch - Indices', - }), - pageTitle: i18n.translate('xpack.monitoring.elasticsearch.indices.pageTitle', { - defaultMessage: 'Elasticsearch indices', - }), - storageKey: 'elasticsearch.indices', - apiUrlFn: () => - `../api/monitoring/v1/clusters/${clusterUuid}/elasticsearch/indices?show_system_indices=${showSystemIndices}`, - reactNodeId: 'elasticsearchIndicesReact', - defaultData: {}, - $scope, - $injector, - $scope, - $injector, - alerts: { - shouldFetch: true, - options: { - alertTypeIds: [RULE_LARGE_SHARD_SIZE], - }, - }, - }); - - this.isCcrEnabled = $scope.cluster.isCcrEnabled; - - // for binding - const toggleShowSystemIndices = (isChecked) => { - // flip the boolean - showSystemIndices = isChecked; - // preserve setting in localStorage - features.update('showSystemIndices', isChecked); - // update the page (resets pagination and sorting) - this.updateData(); - }; - - const renderComponent = () => { - const { clusterStatus, indices } = this.data; - this.renderReact( - ( - - {flyoutComponent} - - {bottomBarComponent} - - )} - /> - ); - }; - - this.onTableChangeRender = renderComponent; - - $scope.$watch( - () => this.data, - (data) => { - if (!data) { - return; - } - renderComponent(); - } - ); - } - }, -}); diff --git a/x-pack/plugins/monitoring/public/views/elasticsearch/ml_jobs/get_page_data.js b/x-pack/plugins/monitoring/public/views/elasticsearch/ml_jobs/get_page_data.js deleted file mode 100644 index 39bd2686069de..0000000000000 --- a/x-pack/plugins/monitoring/public/views/elasticsearch/ml_jobs/get_page_data.js +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { ajaxErrorHandlersProvider } from '../../../lib/ajax_error_handler'; -import { Legacy } from '../../../legacy_shims'; - -export function getPageData($injector) { - const $http = $injector.get('$http'); - const globalState = $injector.get('globalState'); - const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/elasticsearch/ml_jobs`; - const timeBounds = Legacy.shims.timefilter.getBounds(); - return $http - .post(url, { - ccs: globalState.ccs, - timeRange: { - min: timeBounds.min.toISOString(), - max: timeBounds.max.toISOString(), - }, - }) - .then((response) => response.data) - .catch((err) => { - const Private = $injector.get('Private'); - const ajaxErrorHandlers = Private(ajaxErrorHandlersProvider); - return ajaxErrorHandlers(err); - }); -} diff --git a/x-pack/plugins/monitoring/public/views/elasticsearch/ml_jobs/index.html b/x-pack/plugins/monitoring/public/views/elasticsearch/ml_jobs/index.html deleted file mode 100644 index 6fdae46b6b6ed..0000000000000 --- a/x-pack/plugins/monitoring/public/views/elasticsearch/ml_jobs/index.html +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/x-pack/plugins/monitoring/public/views/elasticsearch/ml_jobs/index.js b/x-pack/plugins/monitoring/public/views/elasticsearch/ml_jobs/index.js deleted file mode 100644 index d44b782f3994b..0000000000000 --- a/x-pack/plugins/monitoring/public/views/elasticsearch/ml_jobs/index.js +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { find } from 'lodash'; -import { i18n } from '@kbn/i18n'; -import { uiRoutes } from '../../../angular/helpers/routes'; -import { routeInitProvider } from '../../../lib/route_init'; -import { MonitoringViewBaseEuiTableController } from '../../'; -import { getPageData } from './get_page_data'; -import template from './index.html'; -import { CODE_PATH_ELASTICSEARCH, CODE_PATH_ML } from '../../../../common/constants'; - -uiRoutes.when('/elasticsearch/ml_jobs', { - template, - resolve: { - clusters: function (Private) { - const routeInit = Private(routeInitProvider); - return routeInit({ codePaths: [CODE_PATH_ELASTICSEARCH, CODE_PATH_ML] }); - }, - pageData: getPageData, - }, - controllerAs: 'mlJobs', - controller: class MlJobsList extends MonitoringViewBaseEuiTableController { - constructor($injector, $scope) { - super({ - title: i18n.translate('xpack.monitoring.elasticsearch.mlJobs.routeTitle', { - defaultMessage: 'Elasticsearch - Machine Learning Jobs', - }), - pageTitle: i18n.translate('xpack.monitoring.elasticsearch.mlJobs.pageTitle', { - defaultMessage: 'Elasticsearch machine learning jobs', - }), - storageKey: 'elasticsearch.mlJobs', - getPageData, - $scope, - $injector, - }); - - const $route = $injector.get('$route'); - this.data = $route.current.locals.pageData; - const globalState = $injector.get('globalState'); - $scope.cluster = find($route.current.locals.clusters, { - cluster_uuid: globalState.cluster_uuid, - }); - this.isCcrEnabled = Boolean($scope.cluster && $scope.cluster.isCcrEnabled); - } - }, -}); diff --git a/x-pack/plugins/monitoring/public/views/elasticsearch/node/advanced/index.html b/x-pack/plugins/monitoring/public/views/elasticsearch/node/advanced/index.html deleted file mode 100644 index c79c4eed46bb7..0000000000000 --- a/x-pack/plugins/monitoring/public/views/elasticsearch/node/advanced/index.html +++ /dev/null @@ -1,11 +0,0 @@ - -
-
diff --git a/x-pack/plugins/monitoring/public/views/elasticsearch/node/advanced/index.js b/x-pack/plugins/monitoring/public/views/elasticsearch/node/advanced/index.js deleted file mode 100644 index dc0456178fbff..0000000000000 --- a/x-pack/plugins/monitoring/public/views/elasticsearch/node/advanced/index.js +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -/** - * Controller for Advanced Node Detail - */ -import React from 'react'; -import { i18n } from '@kbn/i18n'; -import { get } from 'lodash'; -import { uiRoutes } from '../../../../angular/helpers/routes'; -import { ajaxErrorHandlersProvider } from '../../../../lib/ajax_error_handler'; -import { routeInitProvider } from '../../../../lib/route_init'; -import template from './index.html'; -import { Legacy } from '../../../../legacy_shims'; -import { AdvancedNode } from '../../../../components/elasticsearch/node/advanced'; -import { MonitoringViewBaseController } from '../../../base_controller'; -import { - CODE_PATH_ELASTICSEARCH, - RULE_CPU_USAGE, - RULE_THREAD_POOL_SEARCH_REJECTIONS, - RULE_THREAD_POOL_WRITE_REJECTIONS, - RULE_MISSING_MONITORING_DATA, - RULE_DISK_USAGE, - RULE_MEMORY_USAGE, -} from '../../../../../common/constants'; - -function getPageData($injector) { - const $http = $injector.get('$http'); - const globalState = $injector.get('globalState'); - const $route = $injector.get('$route'); - const timeBounds = Legacy.shims.timefilter.getBounds(); - const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/elasticsearch/nodes/${$route.current.params.node}`; - - return $http - .post(url, { - ccs: globalState.ccs, - timeRange: { - min: timeBounds.min.toISOString(), - max: timeBounds.max.toISOString(), - }, - is_advanced: true, - }) - .then((response) => response.data) - .catch((err) => { - const Private = $injector.get('Private'); - const ajaxErrorHandlers = Private(ajaxErrorHandlersProvider); - return ajaxErrorHandlers(err); - }); -} - -uiRoutes.when('/elasticsearch/nodes/:node/advanced', { - template, - resolve: { - clusters: function (Private) { - const routeInit = Private(routeInitProvider); - return routeInit({ codePaths: [CODE_PATH_ELASTICSEARCH] }); - }, - pageData: getPageData, - }, - controller: class extends MonitoringViewBaseController { - constructor($injector, $scope) { - const $route = $injector.get('$route'); - const nodeName = $route.current.params.node; - - super({ - defaultData: {}, - getPageData, - reactNodeId: 'monitoringElasticsearchAdvancedNodeApp', - telemetryPageViewTitle: 'elasticsearch_node_advanced', - $scope, - $injector, - alerts: { - shouldFetch: true, - options: { - alertTypeIds: [ - RULE_CPU_USAGE, - RULE_DISK_USAGE, - RULE_THREAD_POOL_SEARCH_REJECTIONS, - RULE_THREAD_POOL_WRITE_REJECTIONS, - RULE_MEMORY_USAGE, - RULE_MISSING_MONITORING_DATA, - ], - filters: [ - { - nodeUuid: nodeName, - }, - ], - }, - }, - }); - - $scope.$watch( - () => this.data, - (data) => { - if (!data || !data.nodeSummary) { - return; - } - - this.setTitle( - i18n.translate('xpack.monitoring.elasticsearch.node.advanced.routeTitle', { - defaultMessage: 'Elasticsearch - Nodes - {nodeSummaryName} - Advanced', - values: { - nodeSummaryName: get(data, 'nodeSummary.name'), - }, - }) - ); - - this.setPageTitle( - i18n.translate('xpack.monitoring.elasticsearch.node.overview.pageTitle', { - defaultMessage: 'Elasticsearch node: {node}', - values: { - node: get(data, 'nodeSummary.name'), - }, - }) - ); - - this.renderReact( - - ); - } - ); - } - }, -}); diff --git a/x-pack/plugins/monitoring/public/views/elasticsearch/node/get_page_data.js b/x-pack/plugins/monitoring/public/views/elasticsearch/node/get_page_data.js deleted file mode 100644 index 1d8bc3f3efa32..0000000000000 --- a/x-pack/plugins/monitoring/public/views/elasticsearch/node/get_page_data.js +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { ajaxErrorHandlersProvider } from '../../../lib/ajax_error_handler'; -import { Legacy } from '../../../legacy_shims'; - -export function getPageData($injector) { - const $http = $injector.get('$http'); - const globalState = $injector.get('globalState'); - const $route = $injector.get('$route'); - const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/elasticsearch/nodes/${$route.current.params.node}`; - const features = $injector.get('features'); - const showSystemIndices = features.isEnabled('showSystemIndices', false); - const timeBounds = Legacy.shims.timefilter.getBounds(); - - return $http - .post(url, { - showSystemIndices, - ccs: globalState.ccs, - timeRange: { - min: timeBounds.min.toISOString(), - max: timeBounds.max.toISOString(), - }, - is_advanced: false, - }) - .then((response) => response.data) - .catch((err) => { - const Private = $injector.get('Private'); - const ajaxErrorHandlers = Private(ajaxErrorHandlersProvider); - return ajaxErrorHandlers(err); - }); -} diff --git a/x-pack/plugins/monitoring/public/views/elasticsearch/node/index.html b/x-pack/plugins/monitoring/public/views/elasticsearch/node/index.html deleted file mode 100644 index 1c3b32728cecd..0000000000000 --- a/x-pack/plugins/monitoring/public/views/elasticsearch/node/index.html +++ /dev/null @@ -1,11 +0,0 @@ - -
-
diff --git a/x-pack/plugins/monitoring/public/views/elasticsearch/node/index.js b/x-pack/plugins/monitoring/public/views/elasticsearch/node/index.js deleted file mode 100644 index 3ec10aa9d4a4c..0000000000000 --- a/x-pack/plugins/monitoring/public/views/elasticsearch/node/index.js +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -/** - * Controller for Node Detail - */ -import React from 'react'; -import { i18n } from '@kbn/i18n'; -import { get, partial } from 'lodash'; -import { uiRoutes } from '../../../angular/helpers/routes'; -import { routeInitProvider } from '../../../lib/route_init'; -import { getPageData } from './get_page_data'; -import template from './index.html'; -import { SetupModeRenderer } from '../../../components/renderers'; -import { Node } from '../../../components/elasticsearch/node/node'; -import { labels } from '../../../components/elasticsearch/shard_allocation/lib/labels'; -import { nodesByIndices } from '../../../components/elasticsearch/shard_allocation/transformers/nodes_by_indices'; -import { MonitoringViewBaseController } from '../../base_controller'; -import { - CODE_PATH_ELASTICSEARCH, - RULE_CPU_USAGE, - RULE_THREAD_POOL_SEARCH_REJECTIONS, - RULE_THREAD_POOL_WRITE_REJECTIONS, - RULE_MISSING_MONITORING_DATA, - RULE_DISK_USAGE, - RULE_MEMORY_USAGE, - ELASTICSEARCH_SYSTEM_ID, -} from '../../../../common/constants'; -import { SetupModeContext } from '../../../components/setup_mode/setup_mode_context'; - -uiRoutes.when('/elasticsearch/nodes/:node', { - template, - resolve: { - clusters: function (Private) { - const routeInit = Private(routeInitProvider); - return routeInit({ codePaths: [CODE_PATH_ELASTICSEARCH] }); - }, - pageData: getPageData, - }, - controllerAs: 'monitoringElasticsearchNodeApp', - controller: class extends MonitoringViewBaseController { - constructor($injector, $scope) { - const $route = $injector.get('$route'); - const nodeName = $route.current.params.node; - - super({ - title: i18n.translate('xpack.monitoring.elasticsearch.node.overview.routeTitle', { - defaultMessage: 'Elasticsearch - Nodes - {nodeName} - Overview', - values: { - nodeName, - }, - }), - telemetryPageViewTitle: 'elasticsearch_node', - defaultData: {}, - getPageData, - reactNodeId: 'monitoringElasticsearchNodeApp', - $scope, - $injector, - alerts: { - shouldFetch: true, - options: { - alertTypeIds: [ - RULE_CPU_USAGE, - RULE_DISK_USAGE, - RULE_THREAD_POOL_SEARCH_REJECTIONS, - RULE_THREAD_POOL_WRITE_REJECTIONS, - RULE_MEMORY_USAGE, - RULE_MISSING_MONITORING_DATA, - ], - filters: [ - { - nodeUuid: nodeName, - }, - ], - }, - }, - }); - - this.nodeName = nodeName; - - const features = $injector.get('features'); - const callPageData = partial(getPageData, $injector); - // show/hide system indices in shard allocation view - $scope.showSystemIndices = features.isEnabled('showSystemIndices', false); - $scope.toggleShowSystemIndices = (isChecked) => { - $scope.showSystemIndices = isChecked; - // preserve setting in localStorage - features.update('showSystemIndices', isChecked); - // update the page - callPageData().then((data) => (this.data = data)); - }; - - const transformer = nodesByIndices(); - $scope.$watch( - () => this.data, - (data) => { - if (!data || !data.shards) { - return; - } - - this.setTitle( - i18n.translate('xpack.monitoring.elasticsearch.node.overview.routeTitle', { - defaultMessage: 'Elasticsearch - Nodes - {nodeName} - Overview', - values: { - nodeName: get(data, 'nodeSummary.name'), - }, - }) - ); - - this.setPageTitle( - i18n.translate('xpack.monitoring.elasticsearch.node.overview.pageTitle', { - defaultMessage: 'Elasticsearch node: {node}', - values: { - node: get(data, 'nodeSummary.name'), - }, - }) - ); - - const shards = data.shards; - $scope.totalCount = shards.length; - $scope.showing = transformer(shards, data.nodes); - $scope.labels = labels.node; - - this.renderReact( - ( - - {flyoutComponent} - - {bottomBarComponent} - - )} - /> - ); - } - ); - } - }, -}); diff --git a/x-pack/plugins/monitoring/public/views/elasticsearch/nodes/index.html b/x-pack/plugins/monitoring/public/views/elasticsearch/nodes/index.html deleted file mode 100644 index 95a483a59f20c..0000000000000 --- a/x-pack/plugins/monitoring/public/views/elasticsearch/nodes/index.html +++ /dev/null @@ -1,8 +0,0 @@ - -
-
diff --git a/x-pack/plugins/monitoring/public/views/elasticsearch/nodes/index.js b/x-pack/plugins/monitoring/public/views/elasticsearch/nodes/index.js deleted file mode 100644 index 5bc546e8590ad..0000000000000 --- a/x-pack/plugins/monitoring/public/views/elasticsearch/nodes/index.js +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { i18n } from '@kbn/i18n'; -import { find } from 'lodash'; -import { uiRoutes } from '../../../angular/helpers/routes'; -import { Legacy } from '../../../legacy_shims'; -import template from './index.html'; -import { routeInitProvider } from '../../../lib/route_init'; -import { MonitoringViewBaseEuiTableController } from '../../'; -import { ElasticsearchNodes } from '../../../components'; -import { ajaxErrorHandlersProvider } from '../../../lib/ajax_error_handler'; -import { SetupModeRenderer } from '../../../components/renderers'; -import { - ELASTICSEARCH_SYSTEM_ID, - CODE_PATH_ELASTICSEARCH, - RULE_CPU_USAGE, - RULE_THREAD_POOL_SEARCH_REJECTIONS, - RULE_THREAD_POOL_WRITE_REJECTIONS, - RULE_MISSING_MONITORING_DATA, - RULE_DISK_USAGE, - RULE_MEMORY_USAGE, -} from '../../../../common/constants'; -import { SetupModeContext } from '../../../components/setup_mode/setup_mode_context'; - -uiRoutes.when('/elasticsearch/nodes', { - template, - resolve: { - clusters(Private) { - const routeInit = Private(routeInitProvider); - return routeInit({ codePaths: [CODE_PATH_ELASTICSEARCH] }); - }, - }, - controllerAs: 'elasticsearchNodes', - controller: class ElasticsearchNodesController extends MonitoringViewBaseEuiTableController { - constructor($injector, $scope) { - const $route = $injector.get('$route'); - const globalState = $injector.get('globalState'); - const showCgroupMetricsElasticsearch = $injector.get('showCgroupMetricsElasticsearch'); - - $scope.cluster = - find($route.current.locals.clusters, { - cluster_uuid: globalState.cluster_uuid, - }) || {}; - - const getPageData = ($injector, _api = undefined, routeOptions = {}) => { - _api; // to fix eslint - const $http = $injector.get('$http'); - const globalState = $injector.get('globalState'); - const timeBounds = Legacy.shims.timefilter.getBounds(); - - const getNodes = (clusterUuid = globalState.cluster_uuid) => - $http.post(`../api/monitoring/v1/clusters/${clusterUuid}/elasticsearch/nodes`, { - ccs: globalState.ccs, - timeRange: { - min: timeBounds.min.toISOString(), - max: timeBounds.max.toISOString(), - }, - ...routeOptions, - }); - - const promise = globalState.cluster_uuid - ? getNodes() - : new Promise((resolve) => resolve({ data: {} })); - return promise - .then((response) => response.data) - .catch((err) => { - const Private = $injector.get('Private'); - const ajaxErrorHandlers = Private(ajaxErrorHandlersProvider); - return ajaxErrorHandlers(err); - }); - }; - - super({ - title: i18n.translate('xpack.monitoring.elasticsearch.nodes.routeTitle', { - defaultMessage: 'Elasticsearch - Nodes', - }), - pageTitle: i18n.translate('xpack.monitoring.elasticsearch.nodes.pageTitle', { - defaultMessage: 'Elasticsearch nodes', - }), - storageKey: 'elasticsearch.nodes', - reactNodeId: 'elasticsearchNodesReact', - defaultData: {}, - getPageData, - $scope, - $injector, - fetchDataImmediately: false, // We want to apply pagination before sending the first request, - alerts: { - shouldFetch: true, - options: { - alertTypeIds: [ - RULE_CPU_USAGE, - RULE_DISK_USAGE, - RULE_THREAD_POOL_SEARCH_REJECTIONS, - RULE_THREAD_POOL_WRITE_REJECTIONS, - RULE_MEMORY_USAGE, - RULE_MISSING_MONITORING_DATA, - ], - }, - }, - }); - - this.isCcrEnabled = $scope.cluster.isCcrEnabled; - - $scope.$watch( - () => this.data, - (data) => { - if (!data) { - return; - } - - const { clusterStatus, nodes, totalNodeCount } = data; - const pagination = { - ...this.pagination, - totalItemCount: totalNodeCount, - }; - - this.renderReact( - ( - - {flyoutComponent} - - {bottomBarComponent} - - )} - /> - ); - } - ); - } - }, -}); diff --git a/x-pack/plugins/monitoring/public/views/elasticsearch/overview/controller.js b/x-pack/plugins/monitoring/public/views/elasticsearch/overview/controller.js deleted file mode 100644 index f39033fe7014d..0000000000000 --- a/x-pack/plugins/monitoring/public/views/elasticsearch/overview/controller.js +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { i18n } from '@kbn/i18n'; -import { find } from 'lodash'; -import { MonitoringViewBaseController } from '../../'; -import { ElasticsearchOverview } from '../../../components'; - -export class ElasticsearchOverviewController extends MonitoringViewBaseController { - constructor($injector, $scope) { - // breadcrumbs + page title - const $route = $injector.get('$route'); - const globalState = $injector.get('globalState'); - $scope.cluster = find($route.current.locals.clusters, { - cluster_uuid: globalState.cluster_uuid, - }); - - super({ - title: 'Elasticsearch', - pageTitle: i18n.translate('xpack.monitoring.elasticsearch.overview.pageTitle', { - defaultMessage: 'Elasticsearch overview', - }), - api: `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/elasticsearch`, - defaultData: { - clusterStatus: { status: '' }, - metrics: null, - shardActivity: null, - }, - reactNodeId: 'elasticsearchOverviewReact', - $scope, - $injector, - }); - - this.isCcrEnabled = $scope.cluster.isCcrEnabled; - this.showShardActivityHistory = false; - this.toggleShardActivityHistory = () => { - this.showShardActivityHistory = !this.showShardActivityHistory; - $scope.$evalAsync(() => { - this.renderReact(this.data, $scope.cluster); - }); - }; - - this.initScope($scope); - } - - initScope($scope) { - $scope.$watch( - () => this.data, - (data) => { - this.renderReact(data, $scope.cluster); - } - ); - - // HACK to force table to re-render even if data hasn't changed. This - // happens when the data remains empty after turning on showHistory. The - // button toggle needs to update the "no data" message based on the value of showHistory - $scope.$watch( - () => this.showShardActivityHistory, - () => { - const { data } = this; - const dataWithShardActivityLoading = { ...data, shardActivity: null }; - // force shard activity to rerender by manipulating and then re-setting its data prop - this.renderReact(dataWithShardActivityLoading, $scope.cluster); - this.renderReact(data, $scope.cluster); - } - ); - } - - filterShardActivityData(shardActivity) { - return shardActivity.filter((row) => { - return this.showShardActivityHistory || row.stage !== 'DONE'; - }); - } - - renderReact(data, cluster) { - // All data needs to originate in this view, and get passed as a prop to the components, for statelessness - const { clusterStatus, metrics, shardActivity, logs } = data || {}; - const shardActivityData = shardActivity && this.filterShardActivityData(shardActivity); // no filter on data = null - const component = ( - - ); - - super.renderReact(component); - } -} diff --git a/x-pack/plugins/monitoring/public/views/elasticsearch/overview/index.html b/x-pack/plugins/monitoring/public/views/elasticsearch/overview/index.html deleted file mode 100644 index 127c48add5e8d..0000000000000 --- a/x-pack/plugins/monitoring/public/views/elasticsearch/overview/index.html +++ /dev/null @@ -1,8 +0,0 @@ - -
-
diff --git a/x-pack/plugins/monitoring/public/views/elasticsearch/overview/index.js b/x-pack/plugins/monitoring/public/views/elasticsearch/overview/index.js deleted file mode 100644 index cc507934dd767..0000000000000 --- a/x-pack/plugins/monitoring/public/views/elasticsearch/overview/index.js +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { uiRoutes } from '../../../angular/helpers/routes'; -import { routeInitProvider } from '../../../lib/route_init'; -import template from './index.html'; -import { ElasticsearchOverviewController } from './controller'; -import { CODE_PATH_ELASTICSEARCH } from '../../../../common/constants'; - -uiRoutes.when('/elasticsearch', { - template, - resolve: { - clusters(Private) { - const routeInit = Private(routeInitProvider); - return routeInit({ codePaths: [CODE_PATH_ELASTICSEARCH] }); - }, - }, - controllerAs: 'elasticsearchOverview', - controller: ElasticsearchOverviewController, -}); diff --git a/x-pack/plugins/monitoring/public/views/index.js b/x-pack/plugins/monitoring/public/views/index.js deleted file mode 100644 index 8cfb8f35e68ba..0000000000000 --- a/x-pack/plugins/monitoring/public/views/index.js +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export { MonitoringViewBaseController } from './base_controller'; -export { MonitoringViewBaseTableController } from './base_table_controller'; -export { MonitoringViewBaseEuiTableController } from './base_eui_table_controller'; diff --git a/x-pack/plugins/monitoring/public/views/kibana/instance/index.html b/x-pack/plugins/monitoring/public/views/kibana/instance/index.html deleted file mode 100644 index 8bb17839683a8..0000000000000 --- a/x-pack/plugins/monitoring/public/views/kibana/instance/index.html +++ /dev/null @@ -1,8 +0,0 @@ - -
-
diff --git a/x-pack/plugins/monitoring/public/views/kibana/instance/index.js b/x-pack/plugins/monitoring/public/views/kibana/instance/index.js deleted file mode 100644 index a71289b084516..0000000000000 --- a/x-pack/plugins/monitoring/public/views/kibana/instance/index.js +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -/* - * Kibana Instance - */ -import React from 'react'; -import { i18n } from '@kbn/i18n'; -import { get } from 'lodash'; -import { uiRoutes } from '../../../angular/helpers/routes'; -import { ajaxErrorHandlersProvider } from '../../../lib/ajax_error_handler'; -import { routeInitProvider } from '../../../lib/route_init'; -import template from './index.html'; -import { Legacy } from '../../../legacy_shims'; -import { - EuiPage, - EuiPageBody, - EuiPageContent, - EuiSpacer, - EuiFlexGrid, - EuiFlexItem, - EuiPanel, -} from '@elastic/eui'; -import { MonitoringTimeseriesContainer } from '../../../components/chart'; -import { DetailStatus } from '../../../components/kibana/detail_status'; -import { MonitoringViewBaseController } from '../../base_controller'; -import { CODE_PATH_KIBANA, RULE_KIBANA_VERSION_MISMATCH } from '../../../../common/constants'; -import { AlertsCallout } from '../../../alerts/callout'; - -function getPageData($injector) { - const $http = $injector.get('$http'); - const globalState = $injector.get('globalState'); - const $route = $injector.get('$route'); - const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/kibana/${$route.current.params.uuid}`; - const timeBounds = Legacy.shims.timefilter.getBounds(); - - return $http - .post(url, { - ccs: globalState.ccs, - timeRange: { - min: timeBounds.min.toISOString(), - max: timeBounds.max.toISOString(), - }, - }) - .then((response) => response.data) - .catch((err) => { - const Private = $injector.get('Private'); - const ajaxErrorHandlers = Private(ajaxErrorHandlersProvider); - return ajaxErrorHandlers(err); - }); -} - -uiRoutes.when('/kibana/instances/:uuid', { - template, - resolve: { - clusters(Private) { - const routeInit = Private(routeInitProvider); - return routeInit({ codePaths: [CODE_PATH_KIBANA] }); - }, - pageData: getPageData, - }, - controllerAs: 'monitoringKibanaInstanceApp', - controller: class extends MonitoringViewBaseController { - constructor($injector, $scope) { - super({ - title: `Kibana - ${get($scope.pageData, 'kibanaSummary.name')}`, - telemetryPageViewTitle: 'kibana_instance', - defaultData: {}, - getPageData, - reactNodeId: 'monitoringKibanaInstanceApp', - $scope, - $injector, - alerts: { - shouldFetch: true, - options: { - alertTypeIds: [RULE_KIBANA_VERSION_MISMATCH], - }, - }, - }); - - $scope.$watch( - () => this.data, - (data) => { - if (!data || !data.metrics) { - return; - } - this.setTitle(`Kibana - ${get(data, 'kibanaSummary.name')}`); - this.setPageTitle( - i18n.translate('xpack.monitoring.kibana.instance.pageTitle', { - defaultMessage: 'Kibana instance: {instance}', - values: { - instance: get($scope.pageData, 'kibanaSummary.name'), - }, - }) - ); - - this.renderReact( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ); - } - ); - } - }, -}); diff --git a/x-pack/plugins/monitoring/public/views/kibana/instances/get_page_data.js b/x-pack/plugins/monitoring/public/views/kibana/instances/get_page_data.js deleted file mode 100644 index 82c49ee0ebb13..0000000000000 --- a/x-pack/plugins/monitoring/public/views/kibana/instances/get_page_data.js +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { ajaxErrorHandlersProvider } from '../../../lib/ajax_error_handler'; -import { Legacy } from '../../../legacy_shims'; - -export function getPageData($injector) { - const $http = $injector.get('$http'); - const globalState = $injector.get('globalState'); - const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/kibana/instances`; - const timeBounds = Legacy.shims.timefilter.getBounds(); - - return $http - .post(url, { - ccs: globalState.ccs, - timeRange: { - min: timeBounds.min.toISOString(), - max: timeBounds.max.toISOString(), - }, - }) - .then((response) => response.data) - .catch((err) => { - const Private = $injector.get('Private'); - const ajaxErrorHandlers = Private(ajaxErrorHandlersProvider); - return ajaxErrorHandlers(err); - }); -} diff --git a/x-pack/plugins/monitoring/public/views/kibana/instances/index.html b/x-pack/plugins/monitoring/public/views/kibana/instances/index.html deleted file mode 100644 index 8e1639a2323a5..0000000000000 --- a/x-pack/plugins/monitoring/public/views/kibana/instances/index.html +++ /dev/null @@ -1,7 +0,0 @@ - -
-
diff --git a/x-pack/plugins/monitoring/public/views/kibana/instances/index.js b/x-pack/plugins/monitoring/public/views/kibana/instances/index.js deleted file mode 100644 index 2601a366e6843..0000000000000 --- a/x-pack/plugins/monitoring/public/views/kibana/instances/index.js +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { i18n } from '@kbn/i18n'; -import { uiRoutes } from '../../../angular/helpers/routes'; -import { routeInitProvider } from '../../../lib/route_init'; -import { MonitoringViewBaseEuiTableController } from '../../'; -import { getPageData } from './get_page_data'; -import template from './index.html'; -import { KibanaInstances } from '../../../components/kibana/instances'; -import { SetupModeRenderer } from '../../../components/renderers'; -import { SetupModeContext } from '../../../components/setup_mode/setup_mode_context'; -import { - KIBANA_SYSTEM_ID, - CODE_PATH_KIBANA, - RULE_KIBANA_VERSION_MISMATCH, -} from '../../../../common/constants'; - -uiRoutes.when('/kibana/instances', { - template, - resolve: { - clusters(Private) { - const routeInit = Private(routeInitProvider); - return routeInit({ codePaths: [CODE_PATH_KIBANA] }); - }, - pageData: getPageData, - }, - controllerAs: 'kibanas', - controller: class KibanaInstancesList extends MonitoringViewBaseEuiTableController { - constructor($injector, $scope) { - super({ - title: i18n.translate('xpack.monitoring.kibana.instances.routeTitle', { - defaultMessage: 'Kibana - Instances', - }), - pageTitle: i18n.translate('xpack.monitoring.kibana.instances.pageTitle', { - defaultMessage: 'Kibana instances', - }), - storageKey: 'kibana.instances', - getPageData, - reactNodeId: 'monitoringKibanaInstancesApp', - $scope, - $injector, - alerts: { - shouldFetch: true, - options: { - alertTypeIds: [RULE_KIBANA_VERSION_MISMATCH], - }, - }, - }); - - const renderReact = () => { - this.renderReact( - ( - - {flyoutComponent} - - {bottomBarComponent} - - )} - /> - ); - }; - - this.onTableChangeRender = renderReact; - - $scope.$watch( - () => this.data, - (data) => { - if (!data) { - return; - } - - renderReact(); - } - ); - } - }, -}); diff --git a/x-pack/plugins/monitoring/public/views/kibana/overview/index.html b/x-pack/plugins/monitoring/public/views/kibana/overview/index.html deleted file mode 100644 index 5b131e113dfa4..0000000000000 --- a/x-pack/plugins/monitoring/public/views/kibana/overview/index.html +++ /dev/null @@ -1,7 +0,0 @@ - -
-
diff --git a/x-pack/plugins/monitoring/public/views/kibana/overview/index.js b/x-pack/plugins/monitoring/public/views/kibana/overview/index.js deleted file mode 100644 index ad59265a98531..0000000000000 --- a/x-pack/plugins/monitoring/public/views/kibana/overview/index.js +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -/** - * Kibana Overview - */ -import React from 'react'; -import { i18n } from '@kbn/i18n'; -import { uiRoutes } from '../../../angular/helpers/routes'; -import { MonitoringTimeseriesContainer } from '../../../components/chart'; -import { ajaxErrorHandlersProvider } from '../../../lib/ajax_error_handler'; -import { routeInitProvider } from '../../../lib/route_init'; -import template from './index.html'; -import { Legacy } from '../../../legacy_shims'; -import { - EuiPage, - EuiPageBody, - EuiPageContent, - EuiPanel, - EuiSpacer, - EuiFlexGroup, - EuiFlexItem, -} from '@elastic/eui'; -import { ClusterStatus } from '../../../components/kibana/cluster_status'; -import { MonitoringViewBaseController } from '../../base_controller'; -import { CODE_PATH_KIBANA } from '../../../../common/constants'; - -function getPageData($injector) { - const $http = $injector.get('$http'); - const globalState = $injector.get('globalState'); - const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/kibana`; - const timeBounds = Legacy.shims.timefilter.getBounds(); - - return $http - .post(url, { - ccs: globalState.ccs, - timeRange: { - min: timeBounds.min.toISOString(), - max: timeBounds.max.toISOString(), - }, - }) - .then((response) => response.data) - .catch((err) => { - const Private = $injector.get('Private'); - const ajaxErrorHandlers = Private(ajaxErrorHandlersProvider); - return ajaxErrorHandlers(err); - }); -} - -uiRoutes.when('/kibana', { - template, - resolve: { - clusters: function (Private) { - const routeInit = Private(routeInitProvider); - return routeInit({ codePaths: [CODE_PATH_KIBANA] }); - }, - pageData: getPageData, - }, - controllerAs: 'monitoringKibanaOverviewApp', - controller: class extends MonitoringViewBaseController { - constructor($injector, $scope) { - super({ - title: `Kibana`, - pageTitle: i18n.translate('xpack.monitoring.kibana.overview.pageTitle', { - defaultMessage: 'Kibana overview', - }), - defaultData: {}, - getPageData, - reactNodeId: 'monitoringKibanaOverviewApp', - $scope, - $injector, - }); - - $scope.$watch( - () => this.data, - (data) => { - if (!data || !data.clusterStatus) { - return; - } - - this.renderReact( - - - - - - - - - - - - - - - - - - - ); - } - ); - } - }, -}); diff --git a/x-pack/plugins/monitoring/public/views/license/controller.js b/x-pack/plugins/monitoring/public/views/license/controller.js deleted file mode 100644 index 297edf6481a55..0000000000000 --- a/x-pack/plugins/monitoring/public/views/license/controller.js +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { get, find } from 'lodash'; -import { i18n } from '@kbn/i18n'; -import React from 'react'; -import { render, unmountComponentAtNode } from 'react-dom'; -import { Legacy } from '../../legacy_shims'; -import { formatDateTimeLocal } from '../../../common/formatting'; -import { BASE_PATH as MANAGEMENT_BASE_PATH } from '../../../../../plugins/license_management/common/constants'; -import { License } from '../../components'; - -const REACT_NODE_ID = 'licenseReact'; - -export class LicenseViewController { - constructor($injector, $scope) { - Legacy.shims.timefilter.disableTimeRangeSelector(); - Legacy.shims.timefilter.disableAutoRefreshSelector(); - - $scope.$on('$destroy', () => { - unmountComponentAtNode(document.getElementById(REACT_NODE_ID)); - }); - - this.init($injector, $scope, i18n); - } - - init($injector, $scope) { - const globalState = $injector.get('globalState'); - const title = $injector.get('title'); - const $route = $injector.get('$route'); - - const cluster = find($route.current.locals.clusters, { - cluster_uuid: globalState.cluster_uuid, - }); - $scope.cluster = cluster; - const routeTitle = i18n.translate('xpack.monitoring.license.licenseRouteTitle', { - defaultMessage: 'License', - }); - title($scope.cluster, routeTitle); - - this.license = cluster.license; - this.isExpired = Date.now() > get(cluster, 'license.expiry_date_in_millis'); - this.isPrimaryCluster = cluster.isPrimary; - - const basePath = Legacy.shims.getBasePath(); - this.uploadLicensePath = basePath + '/app/kibana#' + MANAGEMENT_BASE_PATH + 'upload_license'; - - this.renderReact($scope); - } - - renderReact($scope) { - const injector = Legacy.shims.getAngularInjector(); - const timezone = injector.get('config').get('dateFormat:tz'); - $scope.$evalAsync(() => { - const { isPrimaryCluster, license, isExpired, uploadLicensePath } = this; - let expiryDate = license.expiry_date_in_millis; - if (license.expiry_date_in_millis !== undefined) { - expiryDate = formatDateTimeLocal(license.expiry_date_in_millis, timezone); - } - - // Mount the React component to the template - render( - , - document.getElementById(REACT_NODE_ID) - ); - }); - } -} diff --git a/x-pack/plugins/monitoring/public/views/license/index.html b/x-pack/plugins/monitoring/public/views/license/index.html deleted file mode 100644 index 7fb9c69941004..0000000000000 --- a/x-pack/plugins/monitoring/public/views/license/index.html +++ /dev/null @@ -1,3 +0,0 @@ - -
-
diff --git a/x-pack/plugins/monitoring/public/views/license/index.js b/x-pack/plugins/monitoring/public/views/license/index.js deleted file mode 100644 index 0ffb953268690..0000000000000 --- a/x-pack/plugins/monitoring/public/views/license/index.js +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { uiRoutes } from '../../angular/helpers/routes'; -import { routeInitProvider } from '../../lib/route_init'; -import template from './index.html'; -import { LicenseViewController } from './controller'; -import { CODE_PATH_LICENSE } from '../../../common/constants'; - -uiRoutes.when('/license', { - template, - resolve: { - clusters: (Private) => { - const routeInit = Private(routeInitProvider); - return routeInit({ codePaths: [CODE_PATH_LICENSE] }); - }, - }, - controllerAs: 'licenseView', - controller: LicenseViewController, -}); diff --git a/x-pack/plugins/monitoring/public/views/loading/index.html b/x-pack/plugins/monitoring/public/views/loading/index.html deleted file mode 100644 index 9a5971a65bc39..0000000000000 --- a/x-pack/plugins/monitoring/public/views/loading/index.html +++ /dev/null @@ -1,5 +0,0 @@ - -
-
-
-
diff --git a/x-pack/plugins/monitoring/public/views/loading/index.js b/x-pack/plugins/monitoring/public/views/loading/index.js deleted file mode 100644 index 6406b9e6364f0..0000000000000 --- a/x-pack/plugins/monitoring/public/views/loading/index.js +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -/** - * Controller for single index detail - */ -import React from 'react'; -import { render } from 'react-dom'; -import { i18n } from '@kbn/i18n'; -import { uiRoutes } from '../../angular/helpers/routes'; -import { routeInitProvider } from '../../lib/route_init'; -import template from './index.html'; -import { Legacy } from '../../legacy_shims'; -import { CODE_PATH_ELASTICSEARCH } from '../../../common/constants'; -import { PageLoading } from '../../components'; -import { ajaxErrorHandlersProvider } from '../../lib/ajax_error_handler'; - -const CODE_PATHS = [CODE_PATH_ELASTICSEARCH]; -uiRoutes.when('/loading', { - template, - controllerAs: 'monitoringLoading', - controller: class { - constructor($injector, $scope) { - const Private = $injector.get('Private'); - const titleService = $injector.get('title'); - titleService( - $scope.cluster, - i18n.translate('xpack.monitoring.loading.pageTitle', { - defaultMessage: 'Loading', - }) - ); - - this.init = () => { - const reactNodeId = 'monitoringLoadingReact'; - const renderElement = document.getElementById(reactNodeId); - if (!renderElement) { - console.warn(`"#${reactNodeId}" element has not been added to the DOM yet`); - return; - } - const I18nContext = Legacy.shims.I18nContext; - render( - - - , - renderElement - ); - }; - - const routeInit = Private(routeInitProvider); - routeInit({ codePaths: CODE_PATHS, fetchAllClusters: true, unsetGlobalState: true }) - .then((clusters) => { - if (!clusters || !clusters.length) { - window.location.hash = '#/no-data'; - $scope.$apply(); - return; - } - if (clusters.length === 1) { - // Bypass the cluster listing if there is just 1 cluster - window.history.replaceState(null, null, '#/overview'); - $scope.$apply(); - return; - } - - window.history.replaceState(null, null, '#/home'); - $scope.$apply(); - }) - .catch((err) => { - const Private = $injector.get('Private'); - const ajaxErrorHandlers = Private(ajaxErrorHandlersProvider); - return $scope.$apply(() => ajaxErrorHandlers(err)); - }); - } - }, -}); diff --git a/x-pack/plugins/monitoring/public/views/logstash/node/advanced/index.html b/x-pack/plugins/monitoring/public/views/logstash/node/advanced/index.html deleted file mode 100644 index 63f51809fd7e7..0000000000000 --- a/x-pack/plugins/monitoring/public/views/logstash/node/advanced/index.html +++ /dev/null @@ -1,9 +0,0 @@ - -
-
diff --git a/x-pack/plugins/monitoring/public/views/logstash/node/advanced/index.js b/x-pack/plugins/monitoring/public/views/logstash/node/advanced/index.js deleted file mode 100644 index 9acfd81d186fd..0000000000000 --- a/x-pack/plugins/monitoring/public/views/logstash/node/advanced/index.js +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -/* - * Logstash Node Advanced View - */ -import React from 'react'; -import { i18n } from '@kbn/i18n'; -import { uiRoutes } from '../../../../angular/helpers/routes'; -import { ajaxErrorHandlersProvider } from '../../../../lib/ajax_error_handler'; -import { routeInitProvider } from '../../../../lib/route_init'; -import template from './index.html'; -import { Legacy } from '../../../../legacy_shims'; -import { MonitoringViewBaseController } from '../../../base_controller'; -import { DetailStatus } from '../../../../components/logstash/detail_status'; -import { - EuiPage, - EuiPageBody, - EuiPageContent, - EuiPanel, - EuiSpacer, - EuiFlexGrid, - EuiFlexItem, -} from '@elastic/eui'; -import { MonitoringTimeseriesContainer } from '../../../../components/chart'; -import { - CODE_PATH_LOGSTASH, - RULE_LOGSTASH_VERSION_MISMATCH, -} from '../../../../../common/constants'; -import { AlertsCallout } from '../../../../alerts/callout'; - -function getPageData($injector) { - const $http = $injector.get('$http'); - const globalState = $injector.get('globalState'); - const $route = $injector.get('$route'); - const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/logstash/node/${$route.current.params.uuid}`; - const timeBounds = Legacy.shims.timefilter.getBounds(); - - return $http - .post(url, { - ccs: globalState.ccs, - timeRange: { - min: timeBounds.min.toISOString(), - max: timeBounds.max.toISOString(), - }, - is_advanced: true, - }) - .then((response) => response.data) - .catch((err) => { - const Private = $injector.get('Private'); - const ajaxErrorHandlers = Private(ajaxErrorHandlersProvider); - return ajaxErrorHandlers(err); - }); -} - -uiRoutes.when('/logstash/node/:uuid/advanced', { - template, - resolve: { - clusters(Private) { - const routeInit = Private(routeInitProvider); - return routeInit({ codePaths: [CODE_PATH_LOGSTASH] }); - }, - pageData: getPageData, - }, - controller: class extends MonitoringViewBaseController { - constructor($injector, $scope) { - super({ - defaultData: {}, - getPageData, - reactNodeId: 'monitoringLogstashNodeAdvancedApp', - $scope, - $injector, - alerts: { - shouldFetch: true, - options: { - alertTypeIds: [RULE_LOGSTASH_VERSION_MISMATCH], - }, - }, - telemetryPageViewTitle: 'logstash_node_advanced', - }); - - $scope.$watch( - () => this.data, - (data) => { - if (!data || !data.nodeSummary) { - return; - } - - this.setTitle( - i18n.translate('xpack.monitoring.logstash.node.advanced.routeTitle', { - defaultMessage: 'Logstash - {nodeName} - Advanced', - values: { - nodeName: data.nodeSummary.name, - }, - }) - ); - - this.setPageTitle( - i18n.translate('xpack.monitoring.logstash.node.advanced.pageTitle', { - defaultMessage: 'Logstash node: {nodeName}', - values: { - nodeName: data.nodeSummary.name, - }, - }) - ); - - const metricsToShow = [ - data.metrics.logstash_node_cpu_utilization, - data.metrics.logstash_queue_events_count, - data.metrics.logstash_node_cgroup_cpu, - data.metrics.logstash_pipeline_queue_size, - data.metrics.logstash_node_cgroup_stats, - ]; - - this.renderReact( - - - - - - - - - - {metricsToShow.map((metric, index) => ( - - - - - ))} - - - - - ); - } - ); - } - }, -}); diff --git a/x-pack/plugins/monitoring/public/views/logstash/node/index.html b/x-pack/plugins/monitoring/public/views/logstash/node/index.html deleted file mode 100644 index 062c830dd8b7a..0000000000000 --- a/x-pack/plugins/monitoring/public/views/logstash/node/index.html +++ /dev/null @@ -1,9 +0,0 @@ - -
-
diff --git a/x-pack/plugins/monitoring/public/views/logstash/node/index.js b/x-pack/plugins/monitoring/public/views/logstash/node/index.js deleted file mode 100644 index b23875ba1a3bb..0000000000000 --- a/x-pack/plugins/monitoring/public/views/logstash/node/index.js +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -/* - * Logstash Node - */ -import React from 'react'; -import { i18n } from '@kbn/i18n'; -import { uiRoutes } from '../../../angular/helpers/routes'; -import { ajaxErrorHandlersProvider } from '../../../lib/ajax_error_handler'; -import { routeInitProvider } from '../../../lib/route_init'; -import template from './index.html'; -import { Legacy } from '../../../legacy_shims'; -import { DetailStatus } from '../../../components/logstash/detail_status'; -import { - EuiPage, - EuiPageBody, - EuiPageContent, - EuiPanel, - EuiSpacer, - EuiFlexGrid, - EuiFlexItem, -} from '@elastic/eui'; -import { MonitoringTimeseriesContainer } from '../../../components/chart'; -import { MonitoringViewBaseController } from '../../base_controller'; -import { CODE_PATH_LOGSTASH, RULE_LOGSTASH_VERSION_MISMATCH } from '../../../../common/constants'; -import { AlertsCallout } from '../../../alerts/callout'; - -function getPageData($injector) { - const $http = $injector.get('$http'); - const $route = $injector.get('$route'); - const globalState = $injector.get('globalState'); - const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/logstash/node/${$route.current.params.uuid}`; - const timeBounds = Legacy.shims.timefilter.getBounds(); - - return $http - .post(url, { - ccs: globalState.ccs, - timeRange: { - min: timeBounds.min.toISOString(), - max: timeBounds.max.toISOString(), - }, - is_advanced: false, - }) - .then((response) => response.data) - .catch((err) => { - const Private = $injector.get('Private'); - const ajaxErrorHandlers = Private(ajaxErrorHandlersProvider); - return ajaxErrorHandlers(err); - }); -} - -uiRoutes.when('/logstash/node/:uuid', { - template, - resolve: { - clusters(Private) { - const routeInit = Private(routeInitProvider); - return routeInit({ codePaths: [CODE_PATH_LOGSTASH] }); - }, - pageData: getPageData, - }, - controller: class extends MonitoringViewBaseController { - constructor($injector, $scope) { - super({ - defaultData: {}, - getPageData, - reactNodeId: 'monitoringLogstashNodeApp', - $scope, - $injector, - alerts: { - shouldFetch: true, - options: { - alertTypeIds: [RULE_LOGSTASH_VERSION_MISMATCH], - }, - }, - telemetryPageViewTitle: 'logstash_node', - }); - - $scope.$watch( - () => this.data, - (data) => { - if (!data || !data.nodeSummary) { - return; - } - - this.setTitle( - i18n.translate('xpack.monitoring.logstash.node.routeTitle', { - defaultMessage: 'Logstash - {nodeName}', - values: { - nodeName: data.nodeSummary.name, - }, - }) - ); - - this.setPageTitle( - i18n.translate('xpack.monitoring.logstash.node.pageTitle', { - defaultMessage: 'Logstash node: {nodeName}', - values: { - nodeName: data.nodeSummary.name, - }, - }) - ); - - const metricsToShow = [ - data.metrics.logstash_events_input_rate, - data.metrics.logstash_jvm_usage, - data.metrics.logstash_events_output_rate, - data.metrics.logstash_node_cpu_metric, - data.metrics.logstash_events_latency, - data.metrics.logstash_os_load, - ]; - - this.renderReact( - - - - - - - - - - {metricsToShow.map((metric, index) => ( - - - - - ))} - - - - - ); - } - ); - } - }, -}); diff --git a/x-pack/plugins/monitoring/public/views/logstash/node/pipelines/index.html b/x-pack/plugins/monitoring/public/views/logstash/node/pipelines/index.html deleted file mode 100644 index cae3a169bfd5a..0000000000000 --- a/x-pack/plugins/monitoring/public/views/logstash/node/pipelines/index.html +++ /dev/null @@ -1,8 +0,0 @@ - -
-
diff --git a/x-pack/plugins/monitoring/public/views/logstash/node/pipelines/index.js b/x-pack/plugins/monitoring/public/views/logstash/node/pipelines/index.js deleted file mode 100644 index 0d5105696102a..0000000000000 --- a/x-pack/plugins/monitoring/public/views/logstash/node/pipelines/index.js +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -/* - * Logstash Node Pipelines Listing - */ - -import React from 'react'; -import { i18n } from '@kbn/i18n'; -import { uiRoutes } from '../../../../angular/helpers/routes'; -import { ajaxErrorHandlersProvider } from '../../../../lib/ajax_error_handler'; -import { routeInitProvider } from '../../../../lib/route_init'; -import { isPipelineMonitoringSupportedInVersion } from '../../../../lib/logstash/pipelines'; -import template from './index.html'; -import { Legacy } from '../../../../legacy_shims'; -import { MonitoringViewBaseEuiTableController } from '../../../'; -import { PipelineListing } from '../../../../components/logstash/pipeline_listing/pipeline_listing'; -import { DetailStatus } from '../../../../components/logstash/detail_status'; -import { CODE_PATH_LOGSTASH } from '../../../../../common/constants'; - -const getPageData = ($injector, _api = undefined, routeOptions = {}) => { - _api; // fixing eslint - const $route = $injector.get('$route'); - const $http = $injector.get('$http'); - const globalState = $injector.get('globalState'); - const Private = $injector.get('Private'); - - const logstashUuid = $route.current.params.uuid; - const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/logstash/node/${logstashUuid}/pipelines`; - const timeBounds = Legacy.shims.timefilter.getBounds(); - - return $http - .post(url, { - ccs: globalState.ccs, - timeRange: { - min: timeBounds.min.toISOString(), - max: timeBounds.max.toISOString(), - }, - ...routeOptions, - }) - .then((response) => response.data) - .catch((err) => { - const ajaxErrorHandlers = Private(ajaxErrorHandlersProvider); - return ajaxErrorHandlers(err); - }); -}; - -function makeUpgradeMessage(logstashVersion) { - if (isPipelineMonitoringSupportedInVersion(logstashVersion)) { - return null; - } - - return i18n.translate('xpack.monitoring.logstash.node.pipelines.notAvailableDescription', { - defaultMessage: - 'Pipeline monitoring is only available in Logstash version 6.0.0 or higher. This node is running version {logstashVersion}.', - values: { - logstashVersion, - }, - }); -} - -uiRoutes.when('/logstash/node/:uuid/pipelines', { - template, - resolve: { - clusters(Private) { - const routeInit = Private(routeInitProvider); - return routeInit({ codePaths: [CODE_PATH_LOGSTASH] }); - }, - }, - controller: class extends MonitoringViewBaseEuiTableController { - constructor($injector, $scope) { - const config = $injector.get('config'); - - super({ - defaultData: {}, - getPageData, - reactNodeId: 'monitoringLogstashNodePipelinesApp', - $scope, - $injector, - fetchDataImmediately: false, // We want to apply pagination before sending the first request - telemetryPageViewTitle: 'logstash_node_pipelines', - }); - - $scope.$watch( - () => this.data, - (data) => { - if (!data || !data.nodeSummary) { - return; - } - - this.setTitle( - i18n.translate('xpack.monitoring.logstash.node.pipelines.routeTitle', { - defaultMessage: 'Logstash - {nodeName} - Pipelines', - values: { - nodeName: data.nodeSummary.name, - }, - }) - ); - - this.setPageTitle( - i18n.translate('xpack.monitoring.logstash.node.pipelines.pageTitle', { - defaultMessage: 'Logstash node pipelines: {nodeName}', - values: { - nodeName: data.nodeSummary.name, - }, - }) - ); - - const pagination = { - ...this.pagination, - totalItemCount: data.totalPipelineCount, - }; - - this.renderReact( - - ); - } - ); - } - }, -}); diff --git a/x-pack/plugins/monitoring/public/views/logstash/nodes/get_page_data.js b/x-pack/plugins/monitoring/public/views/logstash/nodes/get_page_data.js deleted file mode 100644 index 4c9167a47b0d7..0000000000000 --- a/x-pack/plugins/monitoring/public/views/logstash/nodes/get_page_data.js +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { ajaxErrorHandlersProvider } from '../../../lib/ajax_error_handler'; -import { Legacy } from '../../../legacy_shims'; - -export function getPageData($injector) { - const $http = $injector.get('$http'); - const globalState = $injector.get('globalState'); - const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/logstash/nodes`; - const timeBounds = Legacy.shims.timefilter.getBounds(); - - return $http - .post(url, { - ccs: globalState.ccs, - timeRange: { - min: timeBounds.min.toISOString(), - max: timeBounds.max.toISOString(), - }, - }) - .then((response) => response.data) - .catch((err) => { - const Private = $injector.get('Private'); - const ajaxErrorHandlers = Private(ajaxErrorHandlersProvider); - return ajaxErrorHandlers(err); - }); -} diff --git a/x-pack/plugins/monitoring/public/views/logstash/nodes/index.html b/x-pack/plugins/monitoring/public/views/logstash/nodes/index.html deleted file mode 100644 index 6da00b1c771b8..0000000000000 --- a/x-pack/plugins/monitoring/public/views/logstash/nodes/index.html +++ /dev/null @@ -1,3 +0,0 @@ - -
-
diff --git a/x-pack/plugins/monitoring/public/views/logstash/nodes/index.js b/x-pack/plugins/monitoring/public/views/logstash/nodes/index.js deleted file mode 100644 index 56b5d0ec6c82a..0000000000000 --- a/x-pack/plugins/monitoring/public/views/logstash/nodes/index.js +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { i18n } from '@kbn/i18n'; -import { uiRoutes } from '../../../angular/helpers/routes'; -import { routeInitProvider } from '../../../lib/route_init'; -import { MonitoringViewBaseEuiTableController } from '../../'; -import { getPageData } from './get_page_data'; -import template from './index.html'; -import { Listing } from '../../../components/logstash/listing'; -import { SetupModeRenderer } from '../../../components/renderers'; -import { SetupModeContext } from '../../../components/setup_mode/setup_mode_context'; -import { - CODE_PATH_LOGSTASH, - LOGSTASH_SYSTEM_ID, - RULE_LOGSTASH_VERSION_MISMATCH, -} from '../../../../common/constants'; - -uiRoutes.when('/logstash/nodes', { - template, - resolve: { - clusters(Private) { - const routeInit = Private(routeInitProvider); - return routeInit({ codePaths: [CODE_PATH_LOGSTASH] }); - }, - pageData: getPageData, - }, - controllerAs: 'lsNodes', - controller: class LsNodesList extends MonitoringViewBaseEuiTableController { - constructor($injector, $scope) { - super({ - title: i18n.translate('xpack.monitoring.logstash.nodes.routeTitle', { - defaultMessage: 'Logstash - Nodes', - }), - pageTitle: i18n.translate('xpack.monitoring.logstash.nodes.pageTitle', { - defaultMessage: 'Logstash nodes', - }), - storageKey: 'logstash.nodes', - getPageData, - reactNodeId: 'monitoringLogstashNodesApp', - $scope, - $injector, - alerts: { - shouldFetch: true, - options: { - alertTypeIds: [RULE_LOGSTASH_VERSION_MISMATCH], - }, - }, - }); - - const renderComponent = () => { - this.renderReact( - ( - - {flyoutComponent} - - {bottomBarComponent} - - )} - /> - ); - }; - - this.onTableChangeRender = renderComponent; - - $scope.$watch( - () => this.data, - () => renderComponent() - ); - } - }, -}); diff --git a/x-pack/plugins/monitoring/public/views/logstash/overview/index.html b/x-pack/plugins/monitoring/public/views/logstash/overview/index.html deleted file mode 100644 index 088aa35892bbe..0000000000000 --- a/x-pack/plugins/monitoring/public/views/logstash/overview/index.html +++ /dev/null @@ -1,3 +0,0 @@ - -
-
diff --git a/x-pack/plugins/monitoring/public/views/logstash/overview/index.js b/x-pack/plugins/monitoring/public/views/logstash/overview/index.js deleted file mode 100644 index b5e8ecbefc532..0000000000000 --- a/x-pack/plugins/monitoring/public/views/logstash/overview/index.js +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -/** - * Logstash Overview - */ -import React from 'react'; -import { i18n } from '@kbn/i18n'; -import { uiRoutes } from '../../../angular/helpers/routes'; -import { ajaxErrorHandlersProvider } from '../../../lib/ajax_error_handler'; -import { routeInitProvider } from '../../../lib/route_init'; -import template from './index.html'; -import { Legacy } from '../../../legacy_shims'; -import { Overview } from '../../../components/logstash/overview'; -import { MonitoringViewBaseController } from '../../base_controller'; -import { CODE_PATH_LOGSTASH } from '../../../../common/constants'; - -function getPageData($injector) { - const $http = $injector.get('$http'); - const globalState = $injector.get('globalState'); - const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/logstash`; - const timeBounds = Legacy.shims.timefilter.getBounds(); - - return $http - .post(url, { - ccs: globalState.ccs, - timeRange: { - min: timeBounds.min.toISOString(), - max: timeBounds.max.toISOString(), - }, - }) - .then((response) => response.data) - .catch((err) => { - const Private = $injector.get('Private'); - const ajaxErrorHandlers = Private(ajaxErrorHandlersProvider); - return ajaxErrorHandlers(err); - }); -} - -uiRoutes.when('/logstash', { - template, - resolve: { - clusters: function (Private) { - const routeInit = Private(routeInitProvider); - return routeInit({ codePaths: [CODE_PATH_LOGSTASH] }); - }, - pageData: getPageData, - }, - controller: class extends MonitoringViewBaseController { - constructor($injector, $scope) { - super({ - title: 'Logstash', - pageTitle: i18n.translate('xpack.monitoring.logstash.overview.pageTitle', { - defaultMessage: 'Logstash overview', - }), - getPageData, - reactNodeId: 'monitoringLogstashOverviewApp', - $scope, - $injector, - }); - - $scope.$watch( - () => this.data, - (data) => { - this.renderReact( - - ); - } - ); - } - }, -}); diff --git a/x-pack/plugins/monitoring/public/views/logstash/pipeline/index.html b/x-pack/plugins/monitoring/public/views/logstash/pipeline/index.html deleted file mode 100644 index afd1d994f1e9c..0000000000000 --- a/x-pack/plugins/monitoring/public/views/logstash/pipeline/index.html +++ /dev/null @@ -1,12 +0,0 @@ - -
-
diff --git a/x-pack/plugins/monitoring/public/views/logstash/pipeline/index.js b/x-pack/plugins/monitoring/public/views/logstash/pipeline/index.js deleted file mode 100644 index dd7bcc8436358..0000000000000 --- a/x-pack/plugins/monitoring/public/views/logstash/pipeline/index.js +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -/* - * Logstash Node Pipeline View - */ -import React from 'react'; -import { uiRoutes } from '../../../angular/helpers/routes'; -import moment from 'moment'; -import { ajaxErrorHandlersProvider } from '../../../lib/ajax_error_handler'; -import { routeInitProvider } from '../../../lib/route_init'; -import { CALCULATE_DURATION_SINCE, CODE_PATH_LOGSTASH } from '../../../../common/constants'; -import { formatTimestampToDuration } from '../../../../common/format_timestamp_to_duration'; -import template from './index.html'; -import { i18n } from '@kbn/i18n'; -import { List } from '../../../components/logstash/pipeline_viewer/models/list'; -import { PipelineState } from '../../../components/logstash/pipeline_viewer/models/pipeline_state'; -import { PipelineViewer } from '../../../components/logstash/pipeline_viewer'; -import { Pipeline } from '../../../components/logstash/pipeline_viewer/models/pipeline'; -import { vertexFactory } from '../../../components/logstash/pipeline_viewer/models/graph/vertex_factory'; -import { MonitoringViewBaseController } from '../../base_controller'; -import { EuiPageBody, EuiPage, EuiPageContent } from '@elastic/eui'; - -let previousPipelineHash = undefined; -let detailVertexId = undefined; - -function getPageData($injector) { - const $route = $injector.get('$route'); - const $http = $injector.get('$http'); - const globalState = $injector.get('globalState'); - const minIntervalSeconds = $injector.get('minIntervalSeconds'); - const Private = $injector.get('Private'); - - const { ccs, cluster_uuid: clusterUuid } = globalState; - const pipelineId = $route.current.params.id; - const pipelineHash = $route.current.params.hash || ''; - - // Pipeline version was changed, so clear out detailVertexId since that vertex won't - // exist in the updated pipeline version - if (pipelineHash !== previousPipelineHash) { - previousPipelineHash = pipelineHash; - detailVertexId = undefined; - } - - const url = pipelineHash - ? `../api/monitoring/v1/clusters/${clusterUuid}/logstash/pipeline/${pipelineId}/${pipelineHash}` - : `../api/monitoring/v1/clusters/${clusterUuid}/logstash/pipeline/${pipelineId}`; - return $http - .post(url, { - ccs, - detailVertexId, - }) - .then((response) => response.data) - .then((data) => { - data.versions = data.versions.map((version) => { - const relativeFirstSeen = formatTimestampToDuration( - version.firstSeen, - CALCULATE_DURATION_SINCE - ); - const relativeLastSeen = formatTimestampToDuration( - version.lastSeen, - CALCULATE_DURATION_SINCE - ); - - const fudgeFactorSeconds = 2 * minIntervalSeconds; - const isLastSeenCloseToNow = Date.now() - version.lastSeen <= fudgeFactorSeconds * 1000; - - return { - ...version, - relativeFirstSeen: i18n.translate( - 'xpack.monitoring.logstash.pipeline.relativeFirstSeenAgoLabel', - { - defaultMessage: '{relativeFirstSeen} ago', - values: { relativeFirstSeen }, - } - ), - relativeLastSeen: isLastSeenCloseToNow - ? i18n.translate('xpack.monitoring.logstash.pipeline.relativeLastSeenNowLabel', { - defaultMessage: 'now', - }) - : i18n.translate('xpack.monitoring.logstash.pipeline.relativeLastSeenAgoLabel', { - defaultMessage: 'until {relativeLastSeen} ago', - values: { relativeLastSeen }, - }), - }; - }); - - return data; - }) - .catch((err) => { - const ajaxErrorHandlers = Private(ajaxErrorHandlersProvider); - return ajaxErrorHandlers(err); - }); -} - -uiRoutes.when('/logstash/pipelines/:id/:hash?', { - template, - resolve: { - clusters(Private) { - const routeInit = Private(routeInitProvider); - return routeInit({ codePaths: [CODE_PATH_LOGSTASH] }); - }, - pageData: getPageData, - }, - controller: class extends MonitoringViewBaseController { - constructor($injector, $scope) { - const config = $injector.get('config'); - const dateFormat = config.get('dateFormat'); - - super({ - title: i18n.translate('xpack.monitoring.logstash.pipeline.routeTitle', { - defaultMessage: 'Logstash - Pipeline', - }), - storageKey: 'logstash.pipelines', - getPageData, - reactNodeId: 'monitoringLogstashPipelineApp', - $scope, - options: { - enableTimeFilter: false, - }, - $injector, - }); - - const timeseriesTooltipXValueFormatter = (xValue) => moment(xValue).format(dateFormat); - - const setDetailVertexId = (vertex) => { - if (!vertex) { - detailVertexId = undefined; - } else { - detailVertexId = vertex.id; - } - - return this.updateData(); - }; - - $scope.$watch( - () => this.data, - (data) => { - if (!data || !data.pipeline) { - return; - } - this.setPageTitle( - i18n.translate('xpack.monitoring.logstash.pipeline.pageTitle', { - defaultMessage: 'Logstash pipeline: {pipeline}', - values: { - pipeline: data.pipeline.id, - }, - }) - ); - this.pipelineState = new PipelineState(data.pipeline); - this.detailVertex = data.vertex ? vertexFactory(null, data.vertex) : null; - this.renderReact( - - - - - - - - ); - } - ); - - $scope.$on('$destroy', () => { - previousPipelineHash = undefined; - detailVertexId = undefined; - }); - } - }, -}); diff --git a/x-pack/plugins/monitoring/public/views/logstash/pipelines/index.html b/x-pack/plugins/monitoring/public/views/logstash/pipelines/index.html deleted file mode 100644 index bef8a7a4737f3..0000000000000 --- a/x-pack/plugins/monitoring/public/views/logstash/pipelines/index.html +++ /dev/null @@ -1,7 +0,0 @@ - -
-
diff --git a/x-pack/plugins/monitoring/public/views/logstash/pipelines/index.js b/x-pack/plugins/monitoring/public/views/logstash/pipelines/index.js deleted file mode 100644 index f3121687f17db..0000000000000 --- a/x-pack/plugins/monitoring/public/views/logstash/pipelines/index.js +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { i18n } from '@kbn/i18n'; -import { find } from 'lodash'; -import { uiRoutes } from '../../../angular/helpers/routes'; -import { ajaxErrorHandlersProvider } from '../../../lib/ajax_error_handler'; -import { routeInitProvider } from '../../../lib/route_init'; -import { isPipelineMonitoringSupportedInVersion } from '../../../lib/logstash/pipelines'; -import template from './index.html'; -import { Legacy } from '../../../legacy_shims'; -import { PipelineListing } from '../../../components/logstash/pipeline_listing/pipeline_listing'; -import { MonitoringViewBaseEuiTableController } from '../..'; -import { CODE_PATH_LOGSTASH } from '../../../../common/constants'; - -/* - * Logstash Pipelines Listing page - */ - -const getPageData = ($injector, _api = undefined, routeOptions = {}) => { - _api; // to fix eslint - const $http = $injector.get('$http'); - const globalState = $injector.get('globalState'); - const Private = $injector.get('Private'); - - const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/logstash/pipelines`; - const timeBounds = Legacy.shims.timefilter.getBounds(); - - return $http - .post(url, { - ccs: globalState.ccs, - timeRange: { - min: timeBounds.min.toISOString(), - max: timeBounds.max.toISOString(), - }, - ...routeOptions, - }) - .then((response) => response.data) - .catch((err) => { - const ajaxErrorHandlers = Private(ajaxErrorHandlersProvider); - return ajaxErrorHandlers(err); - }); -}; - -function makeUpgradeMessage(logstashVersions) { - if ( - !Array.isArray(logstashVersions) || - logstashVersions.length === 0 || - logstashVersions.some(isPipelineMonitoringSupportedInVersion) - ) { - return null; - } - - return 'Pipeline monitoring is only available in Logstash version 6.0.0 or higher.'; -} - -uiRoutes.when('/logstash/pipelines', { - template, - resolve: { - clusters(Private) { - const routeInit = Private(routeInitProvider); - return routeInit({ codePaths: [CODE_PATH_LOGSTASH] }); - }, - }, - controller: class LogstashPipelinesList extends MonitoringViewBaseEuiTableController { - constructor($injector, $scope) { - super({ - title: i18n.translate('xpack.monitoring.logstash.pipelines.routeTitle', { - defaultMessage: 'Logstash Pipelines', - }), - pageTitle: i18n.translate('xpack.monitoring.logstash.pipelines.pageTitle', { - defaultMessage: 'Logstash pipelines', - }), - storageKey: 'logstash.pipelines', - getPageData, - reactNodeId: 'monitoringLogstashPipelinesApp', - $scope, - $injector, - fetchDataImmediately: false, // We want to apply pagination before sending the first request - }); - - const $route = $injector.get('$route'); - const config = $injector.get('config'); - this.data = $route.current.locals.pageData; - const globalState = $injector.get('globalState'); - $scope.cluster = find($route.current.locals.clusters, { - cluster_uuid: globalState.cluster_uuid, - }); - - const renderReact = (pageData) => { - if (!pageData) { - return; - } - - const upgradeMessage = pageData - ? makeUpgradeMessage(pageData.clusterStatus.versions, i18n) - : null; - - const pagination = { - ...this.pagination, - totalItemCount: pageData.totalPipelineCount, - }; - - super.renderReact( - this.onBrush({ xaxis })} - stats={pageData.clusterStatus} - data={pageData.pipelines} - {...this.getPaginationTableProps(pagination)} - upgradeMessage={upgradeMessage} - dateFormat={config.get('dateFormat')} - /> - ); - }; - - $scope.$watch( - () => this.data, - (pageData) => { - renderReact(pageData); - } - ); - } - }, -}); diff --git a/x-pack/plugins/monitoring/public/views/no_data/controller.js b/x-pack/plugins/monitoring/public/views/no_data/controller.js deleted file mode 100644 index 4a6a73dfb2010..0000000000000 --- a/x-pack/plugins/monitoring/public/views/no_data/controller.js +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { - ClusterSettingsChecker, - NodeSettingsChecker, - Enabler, - startChecks, -} from '../../lib/elasticsearch_settings'; -import { ModelUpdater } from './model_updater'; -import { NoData } from '../../components'; -import { CODE_PATH_LICENSE } from '../../../common/constants'; -import { MonitoringViewBaseController } from '../base_controller'; -import { i18n } from '@kbn/i18n'; -import { Legacy } from '../../legacy_shims'; - -export class NoDataController extends MonitoringViewBaseController { - constructor($injector, $scope) { - window.injectorThree = $injector; - const monitoringClusters = $injector.get('monitoringClusters'); - const $http = $injector.get('$http'); - const checkers = [new ClusterSettingsChecker($http), new NodeSettingsChecker($http)]; - - const getData = async () => { - let catchReason; - try { - const monitoringClustersData = await monitoringClusters(undefined, undefined, [ - CODE_PATH_LICENSE, - ]); - if (monitoringClustersData && monitoringClustersData.length) { - window.history.replaceState(null, null, '#/home'); - return monitoringClustersData; - } - } catch (err) { - if (err && err.status === 503) { - catchReason = { - property: 'custom', - message: err.data.message, - }; - } - } - - this.errors.length = 0; - if (catchReason) { - this.reason = catchReason; - } else if (!this.isCollectionEnabledUpdating && !this.isCollectionIntervalUpdating) { - /** - * `no-use-before-define` is fine here, since getData is an async function. - * Needs to be done this way, since there is no `this` before super is executed - * */ - await startChecks(checkers, updateModel); // eslint-disable-line no-use-before-define - } - }; - - super({ - title: i18n.translate('xpack.monitoring.noData.routeTitle', { - defaultMessage: 'Setup Monitoring', - }), - getPageData: async () => await getData(), - reactNodeId: 'noDataReact', - $scope, - $injector, - }); - Object.assign(this, this.getDefaultModel()); - - //Need to set updateModel after super since there is no `this` otherwise - const { updateModel } = new ModelUpdater($scope, this); - const enabler = new Enabler($http, updateModel); - $scope.$watch( - () => this, - () => { - if (this.isCollectionEnabledUpdated && !this.reason) { - return; - } - this.render(enabler); - }, - true - ); - } - - getDefaultModel() { - return { - errors: [], // errors can happen from trying to check or set ES settings - checkMessage: null, // message to show while waiting for api response - isLoading: true, // flag for in-progress state of checking for no data reason - isCollectionEnabledUpdating: false, // flags to indicate whether to show a spinner while waiting for ajax - isCollectionEnabledUpdated: false, - isCollectionIntervalUpdating: false, - isCollectionIntervalUpdated: false, - }; - } - - render(enabler) { - const props = this; - this.renderReact(); - } -} diff --git a/x-pack/plugins/monitoring/public/views/no_data/index.html b/x-pack/plugins/monitoring/public/views/no_data/index.html deleted file mode 100644 index c6fc97b639f42..0000000000000 --- a/x-pack/plugins/monitoring/public/views/no_data/index.html +++ /dev/null @@ -1,5 +0,0 @@ - -
-
-
-
diff --git a/x-pack/plugins/monitoring/public/views/no_data/index.js b/x-pack/plugins/monitoring/public/views/no_data/index.js deleted file mode 100644 index 4bbc490ce29ed..0000000000000 --- a/x-pack/plugins/monitoring/public/views/no_data/index.js +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { uiRoutes } from '../../angular/helpers/routes'; -import template from './index.html'; -import { NoDataController } from './controller'; - -uiRoutes.when('/no-data', { - template, - controller: NoDataController, -}); diff --git a/x-pack/plugins/monitoring/public/views/no_data/model_updater.js b/x-pack/plugins/monitoring/public/views/no_data/model_updater.js deleted file mode 100644 index 115dc782162a7..0000000000000 --- a/x-pack/plugins/monitoring/public/views/no_data/model_updater.js +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -/* - * Class for handling model updates of an Angular controller - * Some properties are simple primitives like strings or booleans, - * but sometimes we need a property in the model to be an Array. For example, - * there may be multiple errors that happen in a flow. - * - * I use 1 method to handling property values that are either primitives or - * arrays, because it allows the callers to be a little more dumb. All they - * have to know is the property name, rather than the type as well. - */ -export class ModelUpdater { - constructor($scope, model) { - this.$scope = $scope; - this.model = model; - this.updateModel = this.updateModel.bind(this); - } - - updateModel(properties) { - const { $scope, model } = this; - const keys = Object.keys(properties); - $scope.$evalAsync(() => { - keys.forEach((key) => { - if (Array.isArray(model[key])) { - model[key].push(properties[key]); - } else { - model[key] = properties[key]; - } - }); - }); - } -} diff --git a/x-pack/plugins/monitoring/public/views/no_data/model_updater.test.js b/x-pack/plugins/monitoring/public/views/no_data/model_updater.test.js deleted file mode 100644 index b286bfb10a9e4..0000000000000 --- a/x-pack/plugins/monitoring/public/views/no_data/model_updater.test.js +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { ModelUpdater } from './model_updater'; - -describe('Model Updater for Angular Controller with React Components', () => { - let $scope; - let model; - let updater; - - beforeEach(() => { - $scope = {}; - $scope.$evalAsync = (cb) => cb(); - - model = {}; - - updater = new ModelUpdater($scope, model); - jest.spyOn(updater, 'updateModel'); - }); - - test('should successfully construct an object', () => { - expect(typeof updater).toBe('object'); - expect(updater.updateModel).not.toHaveBeenCalled(); - }); - - test('updateModel method should add properties to the model', () => { - expect(typeof updater).toBe('object'); - updater.updateModel({ - foo: 'bar', - bar: 'baz', - error: 'monkeywrench', - }); - expect(model).toEqual({ - foo: 'bar', - bar: 'baz', - error: 'monkeywrench', - }); - }); - - test('updateModel method should push properties to the model if property is originally an array', () => { - model.errors = ['first']; - updater.updateModel({ - errors: 'second', - primitive: 'hello', - }); - expect(model).toEqual({ - errors: ['first', 'second'], - primitive: 'hello', - }); - }); -}); diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 2e17a38e83c68..41cc7825bcf36 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -17940,8 +17940,7 @@ "xpack.monitoring.cluster.overview.logstashPanel.withPersistentQueuesLabel": "永続キューあり", "xpack.monitoring.cluster.overview.pageTitle": "クラスターの概要", "xpack.monitoring.cluster.overviewTitle": "概要", - "xpack.monitoring.clusterAlertsNavigation.clusterAlertsLinkText": "クラスターアラート", - "xpack.monitoring.clustersNavigation.clustersLinkText": "クラスター", + "xpack.monitoring.cluster.listing.tabTitle": "クラスター", "xpack.monitoring.clusterStats.uuidNotFoundErrorMessage": "選択された時間範囲にクラスターが見つかりませんでした。UUID:{clusterUuid}", "xpack.monitoring.clusterStats.uuidNotSpecifiedErrorMessage": "{clusterUuid} が指定されていません", "xpack.monitoring.elasticsearch.ccr.ccrListingTable.alertsColumnTitle": "アラート", @@ -17953,10 +17952,10 @@ "xpack.monitoring.elasticsearch.ccr.ccrListingTable.syncLagOpsColumnTitle": "同期の遅延(オペレーション数)", "xpack.monitoring.elasticsearch.ccr.heading": "CCR", "xpack.monitoring.elasticsearch.ccr.pageTitle": "Elasticsearch - CCR", - "xpack.monitoring.elasticsearch.ccr.routeTitle": "Elasticsearch - CCR", + "xpack.monitoring.elasticsearch.ccr.title": "Elasticsearch - CCR", "xpack.monitoring.elasticsearch.ccr.shard.instanceTitle": "インデックス{followerIndex} シャード:{shardId}", "xpack.monitoring.elasticsearch.ccr.shard.pageTitle": "Elasticsearch Ccrシャード - インデックス:{followerIndex} シャード:{shardId}", - "xpack.monitoring.elasticsearch.ccr.shard.routeTitle": "Elasticsearch - CCR - シャード", + "xpack.monitoring.elasticsearch.ccr.shard.title": "Elasticsearch - CCR - シャード", "xpack.monitoring.elasticsearch.ccr.shardsTable.alertsColumnTitle": "アラート", "xpack.monitoring.elasticsearch.ccr.shardsTable.errorColumnTitle": "エラー", "xpack.monitoring.elasticsearch.ccr.shardsTable.lastFetchTimeColumnTitle": "最終取得時刻", @@ -17990,7 +17989,7 @@ "xpack.monitoring.elasticsearch.indexDetailStatus.totalShardsTitle": "合計シャード数", "xpack.monitoring.elasticsearch.indexDetailStatus.totalTitle": "合計", "xpack.monitoring.elasticsearch.indexDetailStatus.unassignedShardsTitle": "未割り当てシャード", - "xpack.monitoring.elasticsearch.indices.advanced.routeTitle": "Elasticsearch - インデックス - {indexName} - 高度な設定", + "xpack.monitoring.elasticsearch.index.advanced.title": "Elasticsearch - インデックス - {indexName} - 高度な設定", "xpack.monitoring.elasticsearch.indices.alertsColumnTitle": "アラート", "xpack.monitoring.elasticsearch.indices.dataTitle": "データ", "xpack.monitoring.elasticsearch.indices.documentCountTitle": "ドキュメントカウント", @@ -17999,8 +17998,8 @@ "xpack.monitoring.elasticsearch.indices.monitoringTablePlaceholder": "インデックスのフィルタリング…", "xpack.monitoring.elasticsearch.indices.nameTitle": "名前", "xpack.monitoring.elasticsearch.indices.noIndicesMatchYourSelectionDescription": "選択項目に一致するインデックスがありません。時間範囲を変更してみてください。", - "xpack.monitoring.elasticsearch.indices.overview.pageTitle": "インデックス:{indexName}", - "xpack.monitoring.elasticsearch.indices.overview.routeTitle": "Elasticsearch - インデックス - {indexName} - 概要", + "xpack.monitoring.elasticsearch.index.overview.pageTitle": "インデックス:{indexName}", + "xpack.monitoring.elasticsearch.index.overview.title": "Elasticsearch - インデックス - {indexName} - 概要", "xpack.monitoring.elasticsearch.indices.pageTitle": "デフォルトのインデックス", "xpack.monitoring.elasticsearch.indices.routeTitle": "Elasticsearch - インデックス", "xpack.monitoring.elasticsearch.indices.searchRateTitle": "検索レート", @@ -18019,7 +18018,7 @@ "xpack.monitoring.elasticsearch.mlJobListing.statusIconLabel": "ジョブ状態:{status}", "xpack.monitoring.elasticsearch.mlJobs.pageTitle": "Elasticsearch - 機械学習ジョブ", "xpack.monitoring.elasticsearch.mlJobs.routeTitle": "Elasticsearch - 機械学習ジョブ", - "xpack.monitoring.elasticsearch.node.advanced.routeTitle": "Elasticsearch - ノード - {nodeSummaryName} - 高度な設定", + "xpack.monitoring.elasticsearch.node.advanced.title": "Elasticsearch - ノード - {nodeName} - 高度な設定", "xpack.monitoring.elasticsearch.node.cells.tooltip.iconLabel": "このメトリックの詳細", "xpack.monitoring.elasticsearch.node.cells.tooltip.max": "最高値", "xpack.monitoring.elasticsearch.node.cells.tooltip.min": "最低値", @@ -18028,7 +18027,7 @@ "xpack.monitoring.elasticsearch.node.cells.trendingDownText": "ダウン", "xpack.monitoring.elasticsearch.node.cells.trendingUpText": "アップ", "xpack.monitoring.elasticsearch.node.overview.pageTitle": "Elasticsearchノード:{node}", - "xpack.monitoring.elasticsearch.node.overview.routeTitle": "Elasticsearch - ノード - {nodeName} - 概要", + "xpack.monitoring.elasticsearch.node.overview.title": "Elasticsearch - ノード - {nodeName} - 概要", "xpack.monitoring.elasticsearch.node.statusIconLabel": "ステータス:{status}", "xpack.monitoring.elasticsearch.nodeDetailStatus.alerts": "アラート", "xpack.monitoring.elasticsearch.nodeDetailStatus.dataLabel": "データ", @@ -18117,8 +18116,7 @@ "xpack.monitoring.es.nodeType.nodeLabel": "ノード", "xpack.monitoring.esNavigation.ccrLinkText": "CCR", "xpack.monitoring.esNavigation.indicesLinkText": "インデックス", - "xpack.monitoring.esNavigation.instance.advancedLinkText": "高度な設定", - "xpack.monitoring.esNavigation.instance.overviewLinkText": "概要", + "xpack.monitoring.esItemNavigation.advancedLinkText": "高度な設定", "xpack.monitoring.esNavigation.jobsLinkText": "機械学習ジョブ", "xpack.monitoring.esNavigation.nodesLinkText": "ノード", "xpack.monitoring.esNavigation.overviewLinkText": "概要", @@ -18237,7 +18235,6 @@ "xpack.monitoring.logstash.node.advanced.pageTitle": "Logstashノード:{nodeName}", "xpack.monitoring.logstash.node.advanced.routeTitle": "Logstash - {nodeName} - 高度な設定", "xpack.monitoring.logstash.node.pageTitle": "Logstashノード:{nodeName}", - "xpack.monitoring.logstash.node.pipelines.notAvailableDescription": "パイプラインの監視は Logstash バージョン 6.0.0 以降でのみ利用できます。このノードはバージョン {logstashVersion} を実行しています。", "xpack.monitoring.logstash.node.pipelines.pageTitle": "Logstashノードパイプライン:{nodeName}", "xpack.monitoring.logstash.node.pipelines.routeTitle": "Logstash - {nodeName} - パイプライン", "xpack.monitoring.logstash.node.routeTitle": "Logstash - {nodeName}", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index d990312f5d619..54811936957ab 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -18215,8 +18215,7 @@ "xpack.monitoring.cluster.overview.logstashPanel.withPersistentQueuesLabel": "持久性队列", "xpack.monitoring.cluster.overview.pageTitle": "集群概览", "xpack.monitoring.cluster.overviewTitle": "概览", - "xpack.monitoring.clusterAlertsNavigation.clusterAlertsLinkText": "集群告警", - "xpack.monitoring.clustersNavigation.clustersLinkText": "集群", + "xpack.monitoring.cluster.listing.tabTitle": "集群", "xpack.monitoring.clusterStats.uuidNotFoundErrorMessage": "在选定时间范围内找不到该集群。UUID:{clusterUuid}", "xpack.monitoring.clusterStats.uuidNotSpecifiedErrorMessage": "{clusterUuid} 未指定", "xpack.monitoring.elasticsearch.ccr.ccrListingTable.alertsColumnTitle": "告警", @@ -18228,10 +18227,10 @@ "xpack.monitoring.elasticsearch.ccr.ccrListingTable.syncLagOpsColumnTitle": "同步延迟(操作)", "xpack.monitoring.elasticsearch.ccr.heading": "CCR", "xpack.monitoring.elasticsearch.ccr.pageTitle": "Elasticsearch Ccr", - "xpack.monitoring.elasticsearch.ccr.routeTitle": "Elasticsearch - CCR", + "xpack.monitoring.elasticsearch.ccr.title": "Elasticsearch - CCR", "xpack.monitoring.elasticsearch.ccr.shard.instanceTitle": "索引:{followerIndex} 分片:{shardId}", "xpack.monitoring.elasticsearch.ccr.shard.pageTitle": "Elasticsearch Ccr 分片 - 索引:{followerIndex} 分片:{shardId}", - "xpack.monitoring.elasticsearch.ccr.shard.routeTitle": "Elasticsearch - CCR - 分片", + "xpack.monitoring.elasticsearch.ccr.shard.title": "Elasticsearch - CCR - 分片", "xpack.monitoring.elasticsearch.ccr.shardsTable.alertsColumnTitle": "告警", "xpack.monitoring.elasticsearch.ccr.shardsTable.errorColumnTitle": "错误", "xpack.monitoring.elasticsearch.ccr.shardsTable.lastFetchTimeColumnTitle": "上次提取时间", @@ -18265,7 +18264,7 @@ "xpack.monitoring.elasticsearch.indexDetailStatus.totalShardsTitle": "分片合计", "xpack.monitoring.elasticsearch.indexDetailStatus.totalTitle": "合计", "xpack.monitoring.elasticsearch.indexDetailStatus.unassignedShardsTitle": "未分配分片", - "xpack.monitoring.elasticsearch.indices.advanced.routeTitle": "Elasticsearch - 索引 - {indexName} - 高级", + "xpack.monitoring.elasticsearch.index.advanced.title": "Elasticsearch - 索引 - {indexName} - 高级", "xpack.monitoring.elasticsearch.indices.alertsColumnTitle": "告警", "xpack.monitoring.elasticsearch.indices.dataTitle": "数据", "xpack.monitoring.elasticsearch.indices.documentCountTitle": "文档计数", @@ -18274,8 +18273,8 @@ "xpack.monitoring.elasticsearch.indices.monitoringTablePlaceholder": "筛选索引……", "xpack.monitoring.elasticsearch.indices.nameTitle": "名称", "xpack.monitoring.elasticsearch.indices.noIndicesMatchYourSelectionDescription": "没有索引匹配您的选择。请尝试更改时间范围选择。", - "xpack.monitoring.elasticsearch.indices.overview.pageTitle": "索引:{indexName}", - "xpack.monitoring.elasticsearch.indices.overview.routeTitle": "Elasticsearch - 索引 - {indexName} - 概览", + "xpack.monitoring.elasticsearch.index.overview.pageTitle": "索引:{indexName}", + "xpack.monitoring.elasticsearch.index.overview.title": "Elasticsearch - 索引 - {indexName} - 概览", "xpack.monitoring.elasticsearch.indices.pageTitle": "Elasticsearch 索引", "xpack.monitoring.elasticsearch.indices.routeTitle": "Elasticsearch - 索引", "xpack.monitoring.elasticsearch.indices.searchRateTitle": "搜索速率", @@ -18294,7 +18293,7 @@ "xpack.monitoring.elasticsearch.mlJobListing.statusIconLabel": "作业状态:{status}", "xpack.monitoring.elasticsearch.mlJobs.pageTitle": "Elasticsearch Machine Learning 作业", "xpack.monitoring.elasticsearch.mlJobs.routeTitle": "Elasticsearch - Machine Learning 作业", - "xpack.monitoring.elasticsearch.node.advanced.routeTitle": "Elasticsearch - 节点 - {nodeSummaryName} - 高级", + "xpack.monitoring.elasticsearch.node.advanced.title": "Elasticsearch - 节点 - {nodeName} - 高级", "xpack.monitoring.elasticsearch.node.cells.tooltip.iconLabel": "有关此指标的更多信息", "xpack.monitoring.elasticsearch.node.cells.tooltip.max": "最大值", "xpack.monitoring.elasticsearch.node.cells.tooltip.min": "最小值", @@ -18303,7 +18302,7 @@ "xpack.monitoring.elasticsearch.node.cells.trendingDownText": "向下", "xpack.monitoring.elasticsearch.node.cells.trendingUpText": "向上", "xpack.monitoring.elasticsearch.node.overview.pageTitle": "Elasticsearch 节点:{node}", - "xpack.monitoring.elasticsearch.node.overview.routeTitle": "Elasticsearch - 节点 - {nodeName} - 概览", + "xpack.monitoring.elasticsearch.node.overview.title": "Elasticsearch - 节点 - {nodeName} - 概览", "xpack.monitoring.elasticsearch.node.statusIconLabel": "状态:{status}", "xpack.monitoring.elasticsearch.nodeDetailStatus.alerts": "告警", "xpack.monitoring.elasticsearch.nodeDetailStatus.dataLabel": "数据", @@ -18392,8 +18391,7 @@ "xpack.monitoring.es.nodeType.nodeLabel": "节点", "xpack.monitoring.esNavigation.ccrLinkText": "CCR", "xpack.monitoring.esNavigation.indicesLinkText": "索引", - "xpack.monitoring.esNavigation.instance.advancedLinkText": "高级", - "xpack.monitoring.esNavigation.instance.overviewLinkText": "概览", + "xpack.monitoring.esItemNavigation.advancedLinkText": "高级", "xpack.monitoring.esNavigation.jobsLinkText": "Machine Learning 作业", "xpack.monitoring.esNavigation.nodesLinkText": "节点", "xpack.monitoring.esNavigation.overviewLinkText": "概览", @@ -18512,7 +18510,6 @@ "xpack.monitoring.logstash.node.advanced.pageTitle": "Logstash 节点:{nodeName}", "xpack.monitoring.logstash.node.advanced.routeTitle": "Logstash - {nodeName} - 高级", "xpack.monitoring.logstash.node.pageTitle": "Logstash 节点:{nodeName}", - "xpack.monitoring.logstash.node.pipelines.notAvailableDescription": "仅 Logstash 版本 6.0.0 或更高版本提供管道监测功能。此节点正在运行版本 {logstashVersion}。", "xpack.monitoring.logstash.node.pipelines.pageTitle": "Logstash 节点管道:{nodeName}", "xpack.monitoring.logstash.node.pipelines.routeTitle": "Logstash - {nodeName} - 管道", "xpack.monitoring.logstash.node.routeTitle": "Logstash - {nodeName}", From dba055c6543409ff1ca4f1d9f7d46f44963d8da1 Mon Sep 17 00:00:00 2001 From: Trevor Pierce <1Copenut@users.noreply.github.com> Date: Mon, 18 Oct 2021 11:41:53 -0500 Subject: [PATCH 003/204] Replace Inspector's EuiPopover with EuiComboBox (#113566) * Replacing EuiPopover with EuiComboBox * The combobox will help alleviate issues when the list of options is very long * Refactoring the Combobox to listen for change events * Added an onChange handler * Renamed the method to render the combobox * Commented out additional blocks of code before final refactor * Finished refactoring the Request Selector to use EUI Combobox * Removed three helper methods for the EUIPopover. * `togglePopover()` * `closePopover()` * `renderRequestDropdownItem()` * Removed the local state object and interface (no longer needed) * Renamed the const `options` to `selectedOptions` in `handleSelectd()` method to better reflect where the options array was coming from. * Updating tests and translations * Fixed the inspector functional test to use comboBox service * Removed two unused translations * Updating Combobox options to pass data-test-sub string * Updated two tests for Combobox single option * Updated the test expectations to the default string * Both tests were looking for a named string instead of a default message * Adding error handling to Inspector combobox * Checking for the item status code * Adding a " (failed)" message if the status code returns `2` * Updating test to look for "Chart_data" instead of "Chartdata" * Updating two tests to validate single combobox options * Added helper method to check default text against combobox options * Added helper method to get the selected combobox option * Checking two inspector instances using helpers * Adding a defensive check to helper method. * Correct a type error in test return * Adding back translated failLabel Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Nathan L Smith --- .../requests/components/request_selector.tsx | 142 +++++------------- test/functional/apps/discover/_inspector.ts | 4 +- test/functional/apps/visualize/_vega_chart.ts | 6 +- test/functional/services/inspector.ts | 43 ++++-- .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - .../apps/maps/embeddable/dashboard.js | 7 +- 7 files changed, 85 insertions(+), 119 deletions(-) diff --git a/src/plugins/inspector/public/views/requests/components/request_selector.tsx b/src/plugins/inspector/public/views/requests/components/request_selector.tsx index 2d94c7ff5bb18..04fac0bd93b7e 100644 --- a/src/plugins/inspector/public/views/requests/components/request_selector.tsx +++ b/src/plugins/inspector/public/views/requests/components/request_selector.tsx @@ -13,118 +13,73 @@ import { i18n } from '@kbn/i18n'; import { EuiBadge, - EuiButtonEmpty, - EuiContextMenuPanel, - EuiContextMenuItem, + EuiComboBox, + EuiComboBoxOptionOption, EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner, - EuiPopover, - EuiTextColor, EuiToolTip, } from '@elastic/eui'; import { RequestStatus } from '../../../../common/adapters'; import { Request } from '../../../../common/adapters/request/types'; -interface RequestSelectorState { - isPopoverOpen: boolean; -} - interface RequestSelectorProps { requests: Request[]; selectedRequest: Request; - onRequestChanged: Function; + onRequestChanged: (request: Request) => void; } -export class RequestSelector extends Component { +export class RequestSelector extends Component { static propTypes = { requests: PropTypes.array.isRequired, selectedRequest: PropTypes.object.isRequired, onRequestChanged: PropTypes.func, }; - state = { - isPopoverOpen: false, - }; + handleSelected = (selectedOptions: Array>) => { + const selectedOption = this.props.requests.find( + (request) => request.id === selectedOptions[0].value + ); - togglePopover = () => { - this.setState((prevState: RequestSelectorState) => ({ - isPopoverOpen: !prevState.isPopoverOpen, - })); + if (selectedOption) { + this.props.onRequestChanged(selectedOption); + } }; - closePopover = () => { - this.setState({ - isPopoverOpen: false, + renderRequestCombobox() { + const options = this.props.requests.map((item) => { + const hasFailed = item.status === RequestStatus.ERROR; + const testLabel = item.name.replace(/\s+/, '_'); + + return { + 'data-test-subj': `inspectorRequestChooser${testLabel}`, + label: hasFailed + ? `${item.name} ${i18n.translate('inspector.requests.failedLabel', { + defaultMessage: ' (failed)', + })}` + : item.name, + value: item.id, + }; }); - }; - - renderRequestDropdownItem = (request: Request, index: number) => { - const hasFailed = request.status === RequestStatus.ERROR; - const inProgress = request.status === RequestStatus.PENDING; return ( - { - this.props.onRequestChanged(request); - this.closePopover(); - }} - toolTipContent={request.description} - toolTipPosition="left" - data-test-subj={`inspectorRequestChooser${request.name}`} - > - - {request.name} - - {hasFailed && ( - - )} - - {inProgress && ( - - )} - - - ); - }; - - renderRequestDropdown() { - const button = ( - - {this.props.selectedRequest.name} - - ); - - return ( - - - + isClearable={false} + onChange={this.handleSelected} + options={options} + prepend="Request" + selectedOptions={[ + { + label: this.props.selectedRequest.name, + value: this.props.selectedRequest.id, + }, + ]} + singleSelection={{ asPlainText: true }} + /> ); } @@ -132,23 +87,8 @@ export class RequestSelector extends Component - - - - - - - {requests.length <= 1 && ( -
- {selectedRequest.name} -
- )} - {requests.length > 1 && this.renderRequestDropdown()} -
+ + {requests.length && this.renderRequestCombobox()} {selectedRequest.status !== RequestStatus.PENDING && ( { - const requests = await inspector.getRequestNames(); + const singleExampleRequest = await inspector.hasSingleRequest(); + const selectedExampleRequest = await inspector.getSelectedOption(); - expect(requests).to.be('Unnamed request #0'); + expect(singleExampleRequest).to.be(true); + expect(selectedExampleRequest).to.equal('Unnamed request #0'); }); it('should log the request statistic', async () => { diff --git a/test/functional/services/inspector.ts b/test/functional/services/inspector.ts index 5364dbebe904c..753d9b7b0b85e 100644 --- a/test/functional/services/inspector.ts +++ b/test/functional/services/inspector.ts @@ -16,6 +16,7 @@ export class InspectorService extends FtrService { private readonly flyout = this.ctx.getService('flyout'); private readonly testSubjects = this.ctx.getService('testSubjects'); private readonly find = this.ctx.getService('find'); + private readonly comboBox = this.ctx.getService('comboBox'); private async getIsEnabled(): Promise { const ariaDisabled = await this.testSubjects.getAttribute('openInspectorButton', 'disabled'); @@ -206,20 +207,29 @@ export class InspectorService extends FtrService { } /** - * Returns request name as the comma-separated string + * Returns the selected option value from combobox */ - public async getRequestNames(): Promise { + public async getSelectedOption(): Promise { await this.openInspectorRequestsView(); - const requestChooserExists = await this.testSubjects.exists('inspectorRequestChooser'); - if (requestChooserExists) { - await this.testSubjects.click('inspectorRequestChooser'); - const menu = await this.testSubjects.find('inspectorRequestChooserMenuPanel'); - const requestNames = await menu.getVisibleText(); - return requestNames.trim().split('\n').join(','); + const selectedOption = await this.comboBox.getComboBoxSelectedOptions( + 'inspectorRequestChooser' + ); + + if (selectedOption.length !== 1) { + return 'Combobox has multiple options'; } - const singleRequest = await this.testSubjects.find('inspectorRequestName'); - return await singleRequest.getVisibleText(); + return selectedOption[0]; + } + + /** + * Returns request name as the comma-separated string from combobox + */ + public async getRequestNames(): Promise { + await this.openInspectorRequestsView(); + + const comboBoxOptions = await this.comboBox.getOptionsList('inspectorRequestChooser'); + return comboBoxOptions.trim().split('\n').join(','); } public getOpenRequestStatisticButton() { @@ -233,4 +243,17 @@ export class InspectorService extends FtrService { public getOpenRequestDetailResponseButton() { return this.testSubjects.find('inspectorRequestDetailResponse'); } + + /** + * Returns true if the value equals the combobox options list + * @param value default combobox single option text + */ + public async hasSingleRequest( + value: string = "You've selected all available options" + ): Promise { + await this.openInspectorRequestsView(); + const comboBoxOptions = await this.comboBox.getOptionsList('inspectorRequestChooser'); + + return value === comboBoxOptions; + } } diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 41cc7825bcf36..79d092cebe366 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -4162,7 +4162,6 @@ "inspector.requests.noRequestsLoggedTitle": "リクエストが記録されていません", "inspector.requests.requestFailedTooltipTitle": "リクエストに失敗しました", "inspector.requests.requestInProgressAriaLabel": "リクエストが進行中", - "inspector.requests.requestLabel": "リクエスト:", "inspector.requests.requestsDescriptionTooltip": "データを収集したリクエストを表示します", "inspector.requests.requestsTitle": "リクエスト", "inspector.requests.requestSucceededTooltipTitle": "リクエスト成功", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 54811936957ab..c80cd968f38a8 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -4201,7 +4201,6 @@ "inspector.requests.noRequestsLoggedTitle": "未记录任何请求", "inspector.requests.requestFailedTooltipTitle": "请求失败", "inspector.requests.requestInProgressAriaLabel": "进行中的请求", - "inspector.requests.requestLabel": "请求:", "inspector.requests.requestsDescriptionTooltip": "查看已收集数据的请求", "inspector.requests.requestsTitle": "请求", "inspector.requests.requestSucceededTooltipTitle": "请求成功", diff --git a/x-pack/test/functional/apps/maps/embeddable/dashboard.js b/x-pack/test/functional/apps/maps/embeddable/dashboard.js index 6c962c98c6a98..a892a0d547339 100644 --- a/x-pack/test/functional/apps/maps/embeddable/dashboard.js +++ b/x-pack/test/functional/apps/maps/embeddable/dashboard.js @@ -77,9 +77,12 @@ export default function ({ getPageObjects, getService }) { await inspector.close(); await dashboardPanelActions.openInspectorByTitle('geo grid vector grid example'); - const gridExampleRequestNames = await inspector.getRequestNames(); + const singleExampleRequest = await inspector.hasSingleRequest(); + const selectedExampleRequest = await inspector.getSelectedOption(); await inspector.close(); - expect(gridExampleRequestNames).to.equal('logstash-*'); + + expect(singleExampleRequest).to.be(true); + expect(selectedExampleRequest).to.equal('logstash-*'); }); it('should apply container state (time, query, filters) to embeddable when loaded', async () => { From 2f27ccffbfb8b66510c8b17853eaa8fbd69f21a1 Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Mon, 18 Oct 2021 13:16:13 -0400 Subject: [PATCH 004/204] [Fleet] Allow package to specify cluster privileges (#114945) --- .../plugins/fleet/common/types/models/epm.ts | 5 + .../common/types/models/package_policy.ts | 5 + x-pack/plugins/fleet/server/mocks/index.ts | 2 +- .../routes/package_policy/handlers.test.ts | 2 +- .../fleet/server/saved_objects/index.ts | 10 ++ ...kage_policies_to_agent_permissions.test.ts | 98 +++++++++++++++++++ .../package_policies_to_agent_permissions.ts | 9 ++ .../server/services/package_policy.test.ts | 96 +++++++++++++++--- .../fleet/server/services/package_policy.ts | 42 +++++--- 9 files changed, 242 insertions(+), 27 deletions(-) diff --git a/x-pack/plugins/fleet/common/types/models/epm.ts b/x-pack/plugins/fleet/common/types/models/epm.ts index eaac2a8113231..6f107ae44bfa7 100644 --- a/x-pack/plugins/fleet/common/types/models/epm.ts +++ b/x-pack/plugins/fleet/common/types/models/epm.ts @@ -126,6 +126,11 @@ interface RegistryAdditionalProperties { readme?: string; internal?: boolean; // Registry addition[0] and EPM uses it[1] [0]: https://github.com/elastic/package-registry/blob/dd7b021893aa8d66a5a5fde963d8ff2792a9b8fa/util/package.go#L63 [1] data_streams?: RegistryDataStream[]; // Registry addition [0] [0]: https://github.com/elastic/package-registry/blob/dd7b021893aa8d66a5a5fde963d8ff2792a9b8fa/util/package.go#L65 + elasticsearch?: { + privileges?: { + cluster?: string[]; + }; + }; } interface RegistryOverridePropertyValue { icons?: RegistryImage[]; diff --git a/x-pack/plugins/fleet/common/types/models/package_policy.ts b/x-pack/plugins/fleet/common/types/models/package_policy.ts index aca537ae31b52..df484646ef66b 100644 --- a/x-pack/plugins/fleet/common/types/models/package_policy.ts +++ b/x-pack/plugins/fleet/common/types/models/package_policy.ts @@ -65,6 +65,11 @@ export interface NewPackagePolicy { package?: PackagePolicyPackage; inputs: NewPackagePolicyInput[]; vars?: PackagePolicyConfigRecord; + elasticsearch?: { + privileges?: { + cluster?: string[]; + }; + }; } export interface UpdatePackagePolicy extends NewPackagePolicy { diff --git a/x-pack/plugins/fleet/server/mocks/index.ts b/x-pack/plugins/fleet/server/mocks/index.ts index 0e7b335da6775..c7f6b6fefc414 100644 --- a/x-pack/plugins/fleet/server/mocks/index.ts +++ b/x-pack/plugins/fleet/server/mocks/index.ts @@ -65,7 +65,7 @@ export const xpackMocks = { export const createPackagePolicyServiceMock = (): jest.Mocked => { return { - compilePackagePolicyInputs: jest.fn(), + _compilePackagePolicyInputs: jest.fn(), buildPackagePolicyFromPackage: jest.fn(), bulkCreate: jest.fn(), create: jest.fn(), diff --git a/x-pack/plugins/fleet/server/routes/package_policy/handlers.test.ts b/x-pack/plugins/fleet/server/routes/package_policy/handlers.test.ts index 729417fa96060..5441af0af686a 100644 --- a/x-pack/plugins/fleet/server/routes/package_policy/handlers.test.ts +++ b/x-pack/plugins/fleet/server/routes/package_policy/handlers.test.ts @@ -35,7 +35,7 @@ jest.mock( } => { return { packagePolicyService: { - compilePackagePolicyInputs: jest.fn((packageInfo, vars, dataInputs) => + _compilePackagePolicyInputs: jest.fn((registryPkgInfo, packageInfo, vars, dataInputs) => Promise.resolve(dataInputs) ), buildPackagePolicyFromPackage: jest.fn(), diff --git a/x-pack/plugins/fleet/server/saved_objects/index.ts b/x-pack/plugins/fleet/server/saved_objects/index.ts index ac5ca401da000..f0b51b19dda33 100644 --- a/x-pack/plugins/fleet/server/saved_objects/index.ts +++ b/x-pack/plugins/fleet/server/saved_objects/index.ts @@ -236,6 +236,16 @@ const getSavedObjectTypes = ( version: { type: 'keyword' }, }, }, + elasticsearch: { + enabled: false, + properties: { + privileges: { + properties: { + cluster: { type: 'keyword' }, + }, + }, + }, + }, vars: { type: 'flattened' }, inputs: { type: 'nested', diff --git a/x-pack/plugins/fleet/server/services/package_policies_to_agent_permissions.test.ts b/x-pack/plugins/fleet/server/services/package_policies_to_agent_permissions.test.ts index 2ce68b46387c9..72566a18bd66e 100644 --- a/x-pack/plugins/fleet/server/services/package_policies_to_agent_permissions.test.ts +++ b/x-pack/plugins/fleet/server/services/package_policies_to_agent_permissions.test.ts @@ -279,6 +279,104 @@ describe('storedPackagePoliciesToAgentPermissions()', () => { }); }); + it('Returns the cluster privileges if there is one in the package policy', async () => { + getPackageInfoMock.mockResolvedValueOnce({ + name: 'test-package', + version: '0.0.0', + latestVersion: '0.0.0', + release: 'experimental', + format_version: '1.0.0', + title: 'Test Package', + description: '', + icons: [], + owner: { github: '' }, + status: 'not_installed', + assets: { + kibana: { + dashboard: [], + visualization: [], + search: [], + index_pattern: [], + map: [], + lens: [], + security_rule: [], + ml_module: [], + tag: [], + }, + elasticsearch: { + component_template: [], + ingest_pipeline: [], + ilm_policy: [], + transform: [], + index_template: [], + data_stream_ilm_policy: [], + ml_model: [], + }, + }, + data_streams: [ + { + type: 'logs', + dataset: 'some-logs', + title: '', + release: '', + package: 'test-package', + path: '', + ingest_pipeline: '', + streams: [{ input: 'test-logs', title: 'Test Logs', template_path: '' }], + }, + ], + }); + + const packagePolicies: PackagePolicy[] = [ + { + id: '12345', + name: 'test-policy', + namespace: 'test', + enabled: true, + package: { name: 'test-package', version: '0.0.0', title: 'Test Package' }, + elasticsearch: { + privileges: { + cluster: ['monitor'], + }, + }, + inputs: [ + { + type: 'test-logs', + enabled: true, + streams: [ + { + id: 'test-logs', + enabled: true, + data_stream: { type: 'logs', dataset: 'some-logs' }, + compiled_stream: { data_stream: { dataset: 'compiled' } }, + }, + ], + }, + ], + created_at: '', + updated_at: '', + created_by: '', + updated_by: '', + revision: 1, + policy_id: '', + output_id: '', + }, + ]; + + const permissions = await storedPackagePoliciesToAgentPermissions(soClient, packagePolicies); + expect(permissions).toMatchObject({ + 'test-policy': { + indices: [ + { + names: ['logs-compiled-test'], + privileges: ['auto_configure', 'create_doc'], + }, + ], + cluster: ['monitor'], + }, + }); + }); + it('Returns the dataset for osquery_manager package', async () => { getPackageInfoMock.mockResolvedValueOnce({ format_version: '1.0.0', diff --git a/x-pack/plugins/fleet/server/services/package_policies_to_agent_permissions.ts b/x-pack/plugins/fleet/server/services/package_policies_to_agent_permissions.ts index 22dcb8ac7b4cb..383747fe126c0 100644 --- a/x-pack/plugins/fleet/server/services/package_policies_to_agent_permissions.ts +++ b/x-pack/plugins/fleet/server/services/package_policies_to_agent_permissions.ts @@ -121,12 +121,21 @@ export async function storedPackagePoliciesToAgentPermissions( }); } + let clusterRoleDescriptor = {}; + const cluster = packagePolicy?.elasticsearch?.privileges?.cluster ?? []; + if (cluster.length > 0) { + clusterRoleDescriptor = { + cluster, + }; + } + return [ packagePolicy.name, { indices: dataStreamsForPermissions.map((ds) => getDataStreamPrivileges(ds, packagePolicy.namespace) ), + ...clusterRoleDescriptor, }, ]; } diff --git a/x-pack/plugins/fleet/server/services/package_policy.test.ts b/x-pack/plugins/fleet/server/services/package_policy.test.ts index 0b6b3579f7b87..9dc05ee2cb4ba 100644 --- a/x-pack/plugins/fleet/server/services/package_policy.test.ts +++ b/x-pack/plugins/fleet/server/services/package_policy.test.ts @@ -33,6 +33,7 @@ import type { InputsOverride, NewPackagePolicy, NewPackagePolicyInput, + RegistryPackage, } from '../../common'; import { IngestManagerError } from '../errors'; @@ -43,6 +44,7 @@ import { _applyIndexPrivileges, } from './package_policy'; import { appContextService } from './app_context'; +import { fetchInfo } from './epm/registry'; async function mockedGetAssetsData(_a: any, _b: any, dataset: string) { if (dataset === 'dataset1') { @@ -88,6 +90,10 @@ hosts: ]; } +function mockedRegistryInfo(): RegistryPackage { + return {} as RegistryPackage; +} + jest.mock('./epm/packages/assets', () => { return { getAssetsData: mockedGetAssetsData, @@ -100,11 +106,7 @@ jest.mock('./epm/packages', () => { }; }); -jest.mock('./epm/registry', () => { - return { - fetchInfo: () => ({}), - }; -}); +jest.mock('./epm/registry'); jest.mock('./agent_policy', () => { return { @@ -126,12 +128,18 @@ jest.mock('./agent_policy', () => { }; }); +const mockedFetchInfo = fetchInfo as jest.Mock>; + type CombinedExternalCallback = PutPackagePolicyUpdateCallback | PostPackagePolicyCreateCallback; describe('Package policy service', () => { - describe('compilePackagePolicyInputs', () => { + beforeEach(() => { + mockedFetchInfo.mockResolvedValue({} as RegistryPackage); + }); + describe('_compilePackagePolicyInputs', () => { it('should work with config variables from the stream', async () => { - const inputs = await packagePolicyService.compilePackagePolicyInputs( + const inputs = await packagePolicyService._compilePackagePolicyInputs( + mockedRegistryInfo(), { data_streams: [ { @@ -194,7 +202,8 @@ describe('Package policy service', () => { }); it('should work with a two level dataset name', async () => { - const inputs = await packagePolicyService.compilePackagePolicyInputs( + const inputs = await packagePolicyService._compilePackagePolicyInputs( + mockedRegistryInfo(), { data_streams: [ { @@ -246,7 +255,8 @@ describe('Package policy service', () => { }); it('should work with config variables at the input level', async () => { - const inputs = await packagePolicyService.compilePackagePolicyInputs( + const inputs = await packagePolicyService._compilePackagePolicyInputs( + mockedRegistryInfo(), { data_streams: [ { @@ -309,7 +319,8 @@ describe('Package policy service', () => { }); it('should work with config variables at the package level', async () => { - const inputs = await packagePolicyService.compilePackagePolicyInputs( + const inputs = await packagePolicyService._compilePackagePolicyInputs( + mockedRegistryInfo(), { data_streams: [ { @@ -377,7 +388,8 @@ describe('Package policy service', () => { }); it('should work with an input with a template and no streams', async () => { - const inputs = await packagePolicyService.compilePackagePolicyInputs( + const inputs = await packagePolicyService._compilePackagePolicyInputs( + mockedRegistryInfo(), { data_streams: [], policy_templates: [ @@ -419,7 +431,8 @@ describe('Package policy service', () => { }); it('should work with an input with a template and streams', async () => { - const inputs = await packagePolicyService.compilePackagePolicyInputs( + const inputs = await packagePolicyService._compilePackagePolicyInputs( + mockedRegistryInfo(), { data_streams: [ { @@ -524,7 +537,8 @@ describe('Package policy service', () => { }); it('should work with a package without input', async () => { - const inputs = await packagePolicyService.compilePackagePolicyInputs( + const inputs = await packagePolicyService._compilePackagePolicyInputs( + mockedRegistryInfo(), { policy_templates: [ { @@ -540,7 +554,8 @@ describe('Package policy service', () => { }); it('should work with a package with a empty inputs array', async () => { - const inputs = await packagePolicyService.compilePackagePolicyInputs( + const inputs = await packagePolicyService._compilePackagePolicyInputs( + mockedRegistryInfo(), { policy_templates: [ { @@ -834,6 +849,59 @@ describe('Package policy service', () => { expect(modifiedStream.vars!.paths.value).toEqual(expect.arrayContaining(['north', 'south'])); expect(modifiedStream.vars!.period.value).toEqual('12mo'); }); + + it('should update elasticsearch.priviles.cluster when updating', async () => { + const savedObjectsClient = savedObjectsClientMock.create(); + const mockPackagePolicy = createPackagePolicyMock(); + + const attributes = { + ...mockPackagePolicy, + inputs: [], + }; + + mockedFetchInfo.mockResolvedValue({ + elasticsearch: { + privileges: { + cluster: ['monitor'], + }, + }, + } as RegistryPackage); + + savedObjectsClient.get.mockResolvedValue({ + id: 'test', + type: 'abcd', + references: [], + version: 'test', + attributes, + }); + + savedObjectsClient.update.mockImplementation( + async ( + type: string, + id: string, + attrs: any + ): Promise> => { + savedObjectsClient.get.mockResolvedValue({ + id: 'test', + type: 'abcd', + references: [], + version: 'test', + attributes: attrs, + }); + return attrs; + } + ); + const elasticsearchClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + + const result = await packagePolicyService.update( + savedObjectsClient, + elasticsearchClient, + 'the-package-policy-id', + { ...mockPackagePolicy, inputs: [] } + ); + + expect(result.elasticsearch).toMatchObject({ privileges: { cluster: ['monitor'] } }); + }); }); describe('runDeleteExternalCallbacks', () => { diff --git a/x-pack/plugins/fleet/server/services/package_policy.ts b/x-pack/plugins/fleet/server/services/package_policy.ts index b7772892f542a..b0c0b9499c68e 100644 --- a/x-pack/plugins/fleet/server/services/package_policy.ts +++ b/x-pack/plugins/fleet/server/services/package_policy.ts @@ -114,7 +114,7 @@ class PackagePolicyService { 'There is already a package with the same name on this agent policy' ); } - + let elasticsearch: PackagePolicy['elasticsearch']; // Add ids to stream const packagePolicyId = options?.id || uuid.v4(); let inputs: PackagePolicyInput[] = packagePolicy.inputs.map((input) => @@ -155,7 +155,15 @@ class PackagePolicyService { } } - inputs = await this.compilePackagePolicyInputs(pkgInfo, packagePolicy.vars || {}, inputs); + const registryPkgInfo = await Registry.fetchInfo(pkgInfo.name, pkgInfo.version); + inputs = await this._compilePackagePolicyInputs( + registryPkgInfo, + pkgInfo, + packagePolicy.vars || {}, + inputs + ); + + elasticsearch = registryPkgInfo.elasticsearch; } const isoDate = new Date().toISOString(); @@ -164,6 +172,7 @@ class PackagePolicyService { { ...packagePolicy, inputs, + elasticsearch, revision: 1, created_at: isoDate, created_by: options?.user?.username ?? 'system', @@ -375,15 +384,21 @@ class PackagePolicyService { ); inputs = enforceFrozenInputs(oldPackagePolicy.inputs, inputs); - + let elasticsearch: PackagePolicy['elasticsearch']; if (packagePolicy.package?.name) { const pkgInfo = await getPackageInfo({ savedObjectsClient: soClient, pkgName: packagePolicy.package.name, pkgVersion: packagePolicy.package.version, }); - - inputs = await this.compilePackagePolicyInputs(pkgInfo, packagePolicy.vars || {}, inputs); + const registryPkgInfo = await Registry.fetchInfo(pkgInfo.name, pkgInfo.version); + inputs = await this._compilePackagePolicyInputs( + registryPkgInfo, + pkgInfo, + packagePolicy.vars || {}, + inputs + ); + elasticsearch = registryPkgInfo.elasticsearch; } await soClient.update( @@ -392,6 +407,7 @@ class PackagePolicyService { { ...restOfPackagePolicy, inputs, + elasticsearch, revision: oldPackagePolicy.revision + 1, updated_at: new Date().toISOString(), updated_by: options?.user?.username ?? 'system', @@ -563,12 +579,14 @@ class PackagePolicyService { packageInfo, packageToPackagePolicyInputs(packageInfo) as InputsOverride[] ); - - updatePackagePolicy.inputs = await this.compilePackagePolicyInputs( + const registryPkgInfo = await Registry.fetchInfo(packageInfo.name, packageInfo.version); + updatePackagePolicy.inputs = await this._compilePackagePolicyInputs( + registryPkgInfo, packageInfo, updatePackagePolicy.vars || {}, updatePackagePolicy.inputs as PackagePolicyInput[] ); + updatePackagePolicy.elasticsearch = registryPkgInfo.elasticsearch; await this.update(soClient, esClient, id, updatePackagePolicy, options); result.push({ @@ -618,12 +636,14 @@ class PackagePolicyService { packageToPackagePolicyInputs(packageInfo) as InputsOverride[], true ); - - updatedPackagePolicy.inputs = await this.compilePackagePolicyInputs( + const registryPkgInfo = await Registry.fetchInfo(packageInfo.name, packageInfo.version); + updatedPackagePolicy.inputs = await this._compilePackagePolicyInputs( + registryPkgInfo, packageInfo, updatedPackagePolicy.vars || {}, updatedPackagePolicy.inputs as PackagePolicyInput[] ); + updatedPackagePolicy.elasticsearch = registryPkgInfo.elasticsearch; const hasErrors = 'errors' in updatedPackagePolicy; @@ -663,12 +683,12 @@ class PackagePolicyService { } } - public async compilePackagePolicyInputs( + public async _compilePackagePolicyInputs( + registryPkgInfo: RegistryPackage, pkgInfo: PackageInfo, vars: PackagePolicy['vars'], inputs: PackagePolicyInput[] ): Promise { - const registryPkgInfo = await Registry.fetchInfo(pkgInfo.name, pkgInfo.version); const inputsPromises = inputs.map(async (input) => { const compiledInput = await _compilePackagePolicyInput(registryPkgInfo, pkgInfo, vars, input); const compiledStreams = await _compilePackageStreams(registryPkgInfo, pkgInfo, vars, input); From fcf86628a078abf4766ef9eea97f2fab70349430 Mon Sep 17 00:00:00 2001 From: Frank Hassanabad Date: Mon, 18 Oct 2021 11:22:49 -0600 Subject: [PATCH 005/204] Adds missing DOM.Iterable (#115218) ## Summary It was brought to our attention from security solutions that developers found we are missing the iterators and entries and others from TypeScript that are available when you include within `lib` `DOM.iterable`. For example without it you cannot use `entries` like this: Screen Shot 2021-10-15 at 9 10 17 AM Until you add it within he base config. Developers are indicating they noticed that workarounds such as lodash or casting is possible but I think this is the wrong direction to go and it's just an oversight that we missed adding the `DOM.iterable` unless someone tells us otherwise. If it is intentional to omit I would like to add docs to the `tsconfig.base.json` about why we omit it for programmers to understand the intention and why we should discourage use or recommend a library such as lodash instead. --- tsconfig.base.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tsconfig.base.json b/tsconfig.base.json index 9de81f68110c1..18c0ad38f4601 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -31,7 +31,8 @@ "lib": [ "esnext", // includes support for browser APIs - "dom" + "dom", + "DOM.Iterable" ], // Node 8 should support everything output by esnext, we override this // in webpack with loader-level compiler options From 584d09e60cc2582a9366c7a4d398fbaaf39f564c Mon Sep 17 00:00:00 2001 From: Tim Sullivan Date: Mon, 18 Oct 2021 10:24:16 -0700 Subject: [PATCH 006/204] [Reporting] Revisit handling timeouts for different phases of screenshot capture (#113807) * [Reporting] Revisit handling timeouts for different phases of screenshot capture * remove translations for changed text * add wip unit test * simplify class * todo more testing * fix ts * update snapshots * simplify open_url * fixup me * move setupPage to a method of the ObservableHandler class * do not pass entire config object to helper functions * distinguish internal timeouts vs external timeout * add tests for waitUntil * checkIsPageOpen test * restore passing of renderErrors * updates per feedback * Update x-pack/plugins/reporting/server/lib/screenshots/observable_handler.ts Co-authored-by: Michael Dokolin * Update x-pack/plugins/reporting/server/lib/screenshots/observable_handler.ts Co-authored-by: Michael Dokolin * Update x-pack/plugins/reporting/server/lib/screenshots/observable_handler.ts Co-authored-by: Michael Dokolin * Update x-pack/plugins/reporting/server/lib/screenshots/observable_handler.ts Co-authored-by: Michael Dokolin * Update x-pack/plugins/reporting/server/lib/screenshots/observable_handler.ts Co-authored-by: Michael Dokolin * fix parsing * apply simplifications consistently * dont main waitUntil a higher order component * resolve the timeouts options outside of the service * comment correction Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Michael Dokolin --- .../lib/screenshots/check_browser_open.ts | 22 -- .../screenshots/get_number_of_items.test.ts | 9 +- .../lib/screenshots/get_number_of_items.ts | 12 +- .../reporting/server/lib/screenshots/index.ts | 19 ++ .../server/lib/screenshots/observable.test.ts | 10 +- .../server/lib/screenshots/observable.ts | 179 ++++----------- .../screenshots/observable_handler.test.ts | 160 +++++++++++++ .../lib/screenshots/observable_handler.ts | 214 ++++++++++++++++++ .../server/lib/screenshots/open_url.ts | 19 +- .../server/lib/screenshots/wait_for_render.ts | 8 +- .../screenshots/wait_for_visualizations.ts | 13 +- .../translations/translations/ja-JP.json | 3 - .../translations/translations/zh-CN.json | 3 - 13 files changed, 465 insertions(+), 206 deletions(-) delete mode 100644 x-pack/plugins/reporting/server/lib/screenshots/check_browser_open.ts create mode 100644 x-pack/plugins/reporting/server/lib/screenshots/observable_handler.test.ts create mode 100644 x-pack/plugins/reporting/server/lib/screenshots/observable_handler.ts diff --git a/x-pack/plugins/reporting/server/lib/screenshots/check_browser_open.ts b/x-pack/plugins/reporting/server/lib/screenshots/check_browser_open.ts deleted file mode 100644 index 95bfa7af870fe..0000000000000 --- a/x-pack/plugins/reporting/server/lib/screenshots/check_browser_open.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { HeadlessChromiumDriver } from '../../browsers'; -import { getChromiumDisconnectedError } from '../../browsers/chromium'; - -/* - * Call this function within error-handling `catch` blocks while setup and wait - * for the Kibana URL to be ready for screenshot. This detects if a block of - * code threw an exception because the page is closed or crashed. - * - * Also call once after `setup$` fires in the screenshot pipeline - */ -export const checkPageIsOpen = (browser: HeadlessChromiumDriver) => { - if (!browser.isPageOpen()) { - throw getChromiumDisconnectedError(); - } -}; diff --git a/x-pack/plugins/reporting/server/lib/screenshots/get_number_of_items.test.ts b/x-pack/plugins/reporting/server/lib/screenshots/get_number_of_items.test.ts index 0ca622d67283c..f160fcb8b27ad 100644 --- a/x-pack/plugins/reporting/server/lib/screenshots/get_number_of_items.test.ts +++ b/x-pack/plugins/reporting/server/lib/screenshots/get_number_of_items.test.ts @@ -6,6 +6,7 @@ */ import { set } from 'lodash'; +import { durationToNumber } from '../../../common/schema_utils'; import { HeadlessChromiumDriver } from '../../browsers'; import { createMockBrowserDriverFactory, @@ -25,6 +26,7 @@ describe('getNumberOfItems', () => { let layout: LayoutInstance; let logger: jest.Mocked; let browser: HeadlessChromiumDriver; + let timeout: number; beforeEach(async () => { const schema = createMockConfigSchema(set({}, 'capture.timeouts.waitForElements', 0)); @@ -34,6 +36,7 @@ describe('getNumberOfItems', () => { captureConfig = config.get('capture'); layout = createMockLayoutInstance(captureConfig); logger = createMockLevelLogger(); + timeout = durationToNumber(captureConfig.timeouts.waitForElements); await createMockBrowserDriverFactory(core, logger, { evaluate: jest.fn( @@ -62,7 +65,7 @@ describe('getNumberOfItems', () => {
`; - await expect(getNumberOfItems(captureConfig, browser, layout, logger)).resolves.toBe(10); + await expect(getNumberOfItems(timeout, browser, layout, logger)).resolves.toBe(10); }); it('should determine the number of items by selector ', async () => { @@ -72,7 +75,7 @@ describe('getNumberOfItems', () => { `; - await expect(getNumberOfItems(captureConfig, browser, layout, logger)).resolves.toBe(3); + await expect(getNumberOfItems(timeout, browser, layout, logger)).resolves.toBe(3); }); it('should fall back to the selector when the attribute is empty', async () => { @@ -82,6 +85,6 @@ describe('getNumberOfItems', () => { `; - await expect(getNumberOfItems(captureConfig, browser, layout, logger)).resolves.toBe(2); + await expect(getNumberOfItems(timeout, browser, layout, logger)).resolves.toBe(2); }); }); diff --git a/x-pack/plugins/reporting/server/lib/screenshots/get_number_of_items.ts b/x-pack/plugins/reporting/server/lib/screenshots/get_number_of_items.ts index 9bbd8e07898be..9e5dfa180fd0f 100644 --- a/x-pack/plugins/reporting/server/lib/screenshots/get_number_of_items.ts +++ b/x-pack/plugins/reporting/server/lib/screenshots/get_number_of_items.ts @@ -6,15 +6,13 @@ */ import { i18n } from '@kbn/i18n'; -import { durationToNumber } from '../../../common/schema_utils'; import { LevelLogger, startTrace } from '../'; import { HeadlessChromiumDriver } from '../../browsers'; -import { CaptureConfig } from '../../types'; import { LayoutInstance } from '../layouts'; import { CONTEXT_GETNUMBEROFITEMS, CONTEXT_READMETADATA } from './constants'; export const getNumberOfItems = async ( - captureConfig: CaptureConfig, + timeout: number, browser: HeadlessChromiumDriver, layout: LayoutInstance, logger: LevelLogger @@ -33,7 +31,6 @@ export const getNumberOfItems = async ( // the dashboard is using the `itemsCountAttribute` attribute to let us // know how many items to expect since gridster incrementally adds panels // we have to use this hint to wait for all of them - const timeout = durationToNumber(captureConfig.timeouts.waitForElements); await browser.waitForSelector( `${renderCompleteSelector},[${itemsCountAttribute}]`, { timeout }, @@ -65,11 +62,8 @@ export const getNumberOfItems = async ( logger.error(err); throw new Error( i18n.translate('xpack.reporting.screencapture.readVisualizationsError', { - defaultMessage: `An error occurred when trying to read the page for visualization panel info. You may need to increase '{configKey}'. {error}`, - values: { - error: err, - configKey: 'xpack.reporting.capture.timeouts.waitForElements', - }, + defaultMessage: `An error occurred when trying to read the page for visualization panel info: {error}`, + values: { error: err }, }) ); } diff --git a/x-pack/plugins/reporting/server/lib/screenshots/index.ts b/x-pack/plugins/reporting/server/lib/screenshots/index.ts index 1ca8b5e00fee4..2b8a0d6207a9b 100644 --- a/x-pack/plugins/reporting/server/lib/screenshots/index.ts +++ b/x-pack/plugins/reporting/server/lib/screenshots/index.ts @@ -12,6 +12,19 @@ import { LayoutInstance } from '../layouts'; export { getScreenshots$ } from './observable'; +export interface PhaseInstance { + timeoutValue: number; + configValue: string; + label: string; +} + +export interface PhaseTimeouts { + openUrl: PhaseInstance; + waitForElements: PhaseInstance; + renderComplete: PhaseInstance; + loadDelay: number; +} + export interface ScreenshotObservableOpts { logger: LevelLogger; urlsOrUrlLocatorTuples: UrlOrUrlLocatorTuple[]; @@ -49,6 +62,12 @@ export interface Screenshot { description: string | null; } +export interface PageSetupResults { + elementsPositionAndAttributes: ElementsPositionAndAttribute[] | null; + timeRange: string | null; + error?: Error; +} + export interface ScreenshotResults { timeRange: string | null; screenshots: Screenshot[]; diff --git a/x-pack/plugins/reporting/server/lib/screenshots/observable.test.ts b/x-pack/plugins/reporting/server/lib/screenshots/observable.test.ts index c33bdad44f9e7..3dc06996f0f04 100644 --- a/x-pack/plugins/reporting/server/lib/screenshots/observable.test.ts +++ b/x-pack/plugins/reporting/server/lib/screenshots/observable.test.ts @@ -98,7 +98,6 @@ describe('Screenshot Observable Pipeline', () => { }, ], "error": undefined, - "renderErrors": undefined, "screenshots": Array [ Object { "data": Object { @@ -173,7 +172,6 @@ describe('Screenshot Observable Pipeline', () => { }, ], "error": undefined, - "renderErrors": undefined, "screenshots": Array [ Object { "data": Object { @@ -225,7 +223,6 @@ describe('Screenshot Observable Pipeline', () => { }, ], "error": undefined, - "renderErrors": undefined, "screenshots": Array [ Object { "data": Object { @@ -314,8 +311,7 @@ describe('Screenshot Observable Pipeline', () => { }, }, ], - "error": [Error: An error occurred when trying to read the page for visualization panel info. You may need to increase 'xpack.reporting.capture.timeouts.waitForElements'. Error: Mock error!], - "renderErrors": undefined, + "error": [Error: The "wait for elements" phase encountered an error: Error: An error occurred when trying to read the page for visualization panel info: Error: Mock error!], "screenshots": Array [ Object { "data": Object { @@ -357,8 +353,7 @@ describe('Screenshot Observable Pipeline', () => { }, }, ], - "error": [Error: An error occurred when trying to read the page for visualization panel info. You may need to increase 'xpack.reporting.capture.timeouts.waitForElements'. Error: Mock error!], - "renderErrors": undefined, + "error": [Error: An error occurred when trying to read the page for visualization panel info: Error: Mock error!], "screenshots": Array [ Object { "data": Object { @@ -465,7 +460,6 @@ describe('Screenshot Observable Pipeline', () => { }, ], "error": undefined, - "renderErrors": undefined, "screenshots": Array [ Object { "data": Object { diff --git a/x-pack/plugins/reporting/server/lib/screenshots/observable.ts b/x-pack/plugins/reporting/server/lib/screenshots/observable.ts index b8fecdc91a3f2..173dbaaf99557 100644 --- a/x-pack/plugins/reporting/server/lib/screenshots/observable.ts +++ b/x-pack/plugins/reporting/server/lib/screenshots/observable.ts @@ -8,138 +8,70 @@ import apm from 'elastic-apm-node'; import * as Rx from 'rxjs'; import { catchError, concatMap, first, mergeMap, take, takeUntil, toArray } from 'rxjs/operators'; +import { durationToNumber } from '../../../common/schema_utils'; import { HeadlessChromiumDriverFactory } from '../../browsers'; import { CaptureConfig } from '../../types'; -import { ElementsPositionAndAttribute, ScreenshotObservableOpts, ScreenshotResults } from './'; -import { checkPageIsOpen } from './check_browser_open'; -import { DEFAULT_PAGELOAD_SELECTOR } from './constants'; -import { getElementPositionAndAttributes } from './get_element_position_data'; -import { getNumberOfItems } from './get_number_of_items'; -import { getScreenshots } from './get_screenshots'; -import { getTimeRange } from './get_time_range'; -import { getRenderErrors } from './get_render_errors'; -import { injectCustomCss } from './inject_css'; -import { openUrl } from './open_url'; -import { waitForRenderComplete } from './wait_for_render'; -import { waitForVisualizations } from './wait_for_visualizations'; +import { + ElementPosition, + ElementsPositionAndAttribute, + PageSetupResults, + ScreenshotObservableOpts, + ScreenshotResults, +} from './'; +import { ScreenshotObservableHandler } from './observable_handler'; -const DEFAULT_SCREENSHOT_CLIP_HEIGHT = 1200; -const DEFAULT_SCREENSHOT_CLIP_WIDTH = 1800; +export { ElementPosition, ElementsPositionAndAttribute, ScreenshotResults }; -interface ScreenSetupData { - elementsPositionAndAttributes: ElementsPositionAndAttribute[] | null; - timeRange: string | null; - renderErrors?: string[]; - error?: Error; -} +const getTimeouts = (captureConfig: CaptureConfig) => ({ + openUrl: { + timeoutValue: durationToNumber(captureConfig.timeouts.openUrl), + configValue: `xpack.reporting.capture.timeouts.openUrl`, + label: 'open URL', + }, + waitForElements: { + timeoutValue: durationToNumber(captureConfig.timeouts.waitForElements), + configValue: `xpack.reporting.capture.timeouts.waitForElements`, + label: 'wait for elements', + }, + renderComplete: { + timeoutValue: durationToNumber(captureConfig.timeouts.renderComplete), + configValue: `xpack.reporting.capture.timeouts.renderComplete`, + label: 'render complete', + }, + loadDelay: durationToNumber(captureConfig.loadDelay), +}); export function getScreenshots$( captureConfig: CaptureConfig, browserDriverFactory: HeadlessChromiumDriverFactory, - { - logger, - urlsOrUrlLocatorTuples, - conditionalHeaders, - layout, - browserTimezone, - }: ScreenshotObservableOpts + opts: ScreenshotObservableOpts ): Rx.Observable { const apmTrans = apm.startTransaction(`reporting screenshot pipeline`, 'reporting'); - const apmCreatePage = apmTrans?.startSpan('create_page', 'wait'); - const create$ = browserDriverFactory.createPage({ browserTimezone }, logger); + const { browserTimezone, logger } = opts; - return create$.pipe( + return browserDriverFactory.createPage({ browserTimezone }, logger).pipe( mergeMap(({ driver, exit$ }) => { apmCreatePage?.end(); exit$.subscribe({ error: () => apmTrans?.end() }); - return Rx.from(urlsOrUrlLocatorTuples).pipe( - concatMap((urlOrUrlLocatorTuple, index) => { - const setup$: Rx.Observable = Rx.of(1).pipe( - mergeMap(() => { - // If we're moving to another page in the app, we'll want to wait for the app to tell us - // it's loaded the next page. - const page = index + 1; - const pageLoadSelector = - page > 1 ? `[data-shared-page="${page}"]` : DEFAULT_PAGELOAD_SELECTOR; + const screen = new ScreenshotObservableHandler(driver, opts, getTimeouts(captureConfig)); - return openUrl( - captureConfig, - driver, - urlOrUrlLocatorTuple, - pageLoadSelector, - conditionalHeaders, - logger - ); - }), - mergeMap(() => getNumberOfItems(captureConfig, driver, layout, logger)), - mergeMap(async (itemsCount) => { - // set the viewport to the dimentions from the job, to allow elements to flow into the expected layout - const viewport = layout.getViewport(itemsCount) || getDefaultViewPort(); - await Promise.all([ - driver.setViewport(viewport, logger), - waitForVisualizations(captureConfig, driver, itemsCount, layout, logger), - ]); - }), - mergeMap(async () => { - // Waiting till _after_ elements have rendered before injecting our CSS - // allows for them to be displayed properly in many cases - await injectCustomCss(driver, layout, logger); - - const apmPositionElements = apmTrans?.startSpan('position_elements', 'correction'); - if (layout.positionElements) { - // position panel elements for print layout - await layout.positionElements(driver, logger); - } - if (apmPositionElements) apmPositionElements.end(); - - await waitForRenderComplete(captureConfig, driver, layout, logger); - }), - mergeMap(async () => { - return await Promise.all([ - getTimeRange(driver, layout, logger), - getElementPositionAndAttributes(driver, layout, logger), - getRenderErrors(driver, layout, logger), - ]).then(([timeRange, elementsPositionAndAttributes, renderErrors]) => ({ - elementsPositionAndAttributes, - timeRange, - renderErrors, - })); - }), + return Rx.from(opts.urlsOrUrlLocatorTuples).pipe( + concatMap((urlOrUrlLocatorTuple, index) => { + return Rx.of(1).pipe( + screen.setupPage(index, urlOrUrlLocatorTuple, apmTrans), catchError((err) => { - checkPageIsOpen(driver); // if browser has closed, throw a relevant error about it + screen.checkPageIsOpen(); // this fails the job if the browser has closed logger.error(err); - return Rx.of({ - elementsPositionAndAttributes: null, - timeRange: null, - error: err, - }); - }) - ); - - return setup$.pipe( + return Rx.of({ ...defaultSetupResult, error: err }); // allow failover screenshot capture + }), takeUntil(exit$), - mergeMap(async (data: ScreenSetupData): Promise => { - checkPageIsOpen(driver); // re-check that the browser has not closed - - const elements = data.elementsPositionAndAttributes - ? data.elementsPositionAndAttributes - : getDefaultElementPosition(layout.getViewport(1)); - const screenshots = await getScreenshots(driver, elements, logger); - const { timeRange, error: setupError, renderErrors } = data; - return { - timeRange, - screenshots, - error: setupError, - renderErrors, - elementsPositionAndAttributes: elements, - }; - }) + screen.getScreenshots() ); }), - take(urlsOrUrlLocatorTuples.length), + take(opts.urlsOrUrlLocatorTuples.length), toArray() ); }), @@ -147,30 +79,7 @@ export function getScreenshots$( ); } -/* - * If Kibana is showing a non-HTML error message, the viewport might not be - * provided by the browser. - */ -const getDefaultViewPort = () => ({ - height: DEFAULT_SCREENSHOT_CLIP_HEIGHT, - width: DEFAULT_SCREENSHOT_CLIP_WIDTH, - zoom: 1, -}); -/* - * If an error happens setting up the page, we don't know if there actually - * are any visualizations showing. These defaults should help capture the page - * enough for the user to see the error themselves - */ -const getDefaultElementPosition = (dimensions: { height?: number; width?: number } | null) => { - const height = dimensions?.height || DEFAULT_SCREENSHOT_CLIP_HEIGHT; - const width = dimensions?.width || DEFAULT_SCREENSHOT_CLIP_WIDTH; - - const defaultObject = { - position: { - boundingClientRect: { top: 0, left: 0, height, width }, - scroll: { x: 0, y: 0 }, - }, - attributes: {}, - }; - return [defaultObject]; +const defaultSetupResult: PageSetupResults = { + elementsPositionAndAttributes: null, + timeRange: null, }; diff --git a/x-pack/plugins/reporting/server/lib/screenshots/observable_handler.test.ts b/x-pack/plugins/reporting/server/lib/screenshots/observable_handler.test.ts new file mode 100644 index 0000000000000..25a8bed370d86 --- /dev/null +++ b/x-pack/plugins/reporting/server/lib/screenshots/observable_handler.test.ts @@ -0,0 +1,160 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as Rx from 'rxjs'; +import { first, map } from 'rxjs/operators'; +import { HeadlessChromiumDriver } from '../../browsers'; +import { ReportingConfigType } from '../../config'; +import { ConditionalHeaders } from '../../export_types/common'; +import { + createMockBrowserDriverFactory, + createMockConfigSchema, + createMockLayoutInstance, + createMockLevelLogger, + createMockReportingCore, +} from '../../test_helpers'; +import { LayoutInstance } from '../layouts'; +import { PhaseTimeouts, ScreenshotObservableOpts } from './'; +import { ScreenshotObservableHandler } from './observable_handler'; + +const logger = createMockLevelLogger(); + +describe('ScreenshotObservableHandler', () => { + let captureConfig: ReportingConfigType['capture']; + let layout: LayoutInstance; + let conditionalHeaders: ConditionalHeaders; + let opts: ScreenshotObservableOpts; + let timeouts: PhaseTimeouts; + let driver: HeadlessChromiumDriver; + + beforeAll(async () => { + captureConfig = { + timeouts: { + openUrl: 30000, + waitForElements: 30000, + renderComplete: 30000, + }, + loadDelay: 5000, + } as unknown as typeof captureConfig; + + layout = createMockLayoutInstance(captureConfig); + + conditionalHeaders = { + headers: { testHeader: 'testHeadValue' }, + conditions: {} as unknown as ConditionalHeaders['conditions'], + }; + + opts = { + conditionalHeaders, + layout, + logger, + urlsOrUrlLocatorTuples: [], + }; + + timeouts = { + openUrl: { + timeoutValue: 60000, + configValue: `xpack.reporting.capture.timeouts.openUrl`, + label: 'open URL', + }, + waitForElements: { + timeoutValue: 30000, + configValue: `xpack.reporting.capture.timeouts.waitForElements`, + label: 'wait for elements', + }, + renderComplete: { + timeoutValue: 60000, + configValue: `xpack.reporting.capture.timeouts.renderComplete`, + label: 'render complete', + }, + loadDelay: 5000, + }; + }); + + beforeEach(async () => { + const reporting = await createMockReportingCore(createMockConfigSchema()); + const driverFactory = await createMockBrowserDriverFactory(reporting, logger); + ({ driver } = await driverFactory.createPage({}, logger).pipe(first()).toPromise()); + driver.isPageOpen = jest.fn().mockImplementation(() => true); + }); + + describe('waitUntil', () => { + it('catches TimeoutError and references the timeout config in a custom message', async () => { + const screenshots = new ScreenshotObservableHandler(driver, opts, timeouts); + const test$ = Rx.interval(1000).pipe( + screenshots.waitUntil({ + timeoutValue: 200, + configValue: 'test.config.value', + label: 'Test Config', + }) + ); + + const testPipeline = () => test$.toPromise(); + await expect(testPipeline).rejects.toMatchInlineSnapshot( + `[Error: The "Test Config" phase took longer than 0.2 seconds. You may need to increase "test.config.value": TimeoutError: Timeout has occurred]` + ); + }); + + it('catches other Errors and explains where they were thrown', async () => { + const screenshots = new ScreenshotObservableHandler(driver, opts, timeouts); + const test$ = Rx.throwError(new Error(`Test Error to Throw`)).pipe( + screenshots.waitUntil({ + timeoutValue: 200, + configValue: 'test.config.value', + label: 'Test Config', + }) + ); + + const testPipeline = () => test$.toPromise(); + await expect(testPipeline).rejects.toMatchInlineSnapshot( + `[Error: The "Test Config" phase encountered an error: Error: Test Error to Throw]` + ); + }); + + it('is a pass-through if there is no Error', async () => { + const screenshots = new ScreenshotObservableHandler(driver, opts, timeouts); + const test$ = Rx.of('nice to see you').pipe( + screenshots.waitUntil({ + timeoutValue: 20, + configValue: 'xxxxxxxxxxxxxxxxx', + label: 'xxxxxxxxxxx', + }) + ); + + await expect(test$.toPromise()).resolves.toBe(`nice to see you`); + }); + }); + + describe('checkPageIsOpen', () => { + it('throws a decorated Error when page is not open', async () => { + driver.isPageOpen = jest.fn().mockImplementation(() => false); + const screenshots = new ScreenshotObservableHandler(driver, opts, timeouts); + const test$ = Rx.of(234455).pipe( + map((input) => { + screenshots.checkPageIsOpen(); + return input; + }) + ); + + await expect(test$.toPromise()).rejects.toMatchInlineSnapshot( + `[Error: Browser was closed unexpectedly! Check the server logs for more info.]` + ); + }); + + it('is a pass-through when the page is open', async () => { + const screenshots = new ScreenshotObservableHandler(driver, opts, timeouts); + const test$ = Rx.of(234455).pipe( + map((input) => { + screenshots.checkPageIsOpen(); + return input; + }) + ); + + await expect(test$.toPromise()).resolves.toBe(234455); + }); + }); +}); diff --git a/x-pack/plugins/reporting/server/lib/screenshots/observable_handler.ts b/x-pack/plugins/reporting/server/lib/screenshots/observable_handler.ts new file mode 100644 index 0000000000000..87c247273ef04 --- /dev/null +++ b/x-pack/plugins/reporting/server/lib/screenshots/observable_handler.ts @@ -0,0 +1,214 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import apm from 'elastic-apm-node'; +import * as Rx from 'rxjs'; +import { catchError, mergeMap, timeout } from 'rxjs/operators'; +import { numberToDuration } from '../../../common/schema_utils'; +import { UrlOrUrlLocatorTuple } from '../../../common/types'; +import { HeadlessChromiumDriver } from '../../browsers'; +import { getChromiumDisconnectedError } from '../../browsers/chromium'; +import { + PageSetupResults, + PhaseInstance, + PhaseTimeouts, + ScreenshotObservableOpts, + ScreenshotResults, +} from './'; +import { getElementPositionAndAttributes } from './get_element_position_data'; +import { getNumberOfItems } from './get_number_of_items'; +import { getRenderErrors } from './get_render_errors'; +import { getScreenshots } from './get_screenshots'; +import { getTimeRange } from './get_time_range'; +import { injectCustomCss } from './inject_css'; +import { openUrl } from './open_url'; +import { waitForRenderComplete } from './wait_for_render'; +import { waitForVisualizations } from './wait_for_visualizations'; + +export class ScreenshotObservableHandler { + private conditionalHeaders: ScreenshotObservableOpts['conditionalHeaders']; + private layout: ScreenshotObservableOpts['layout']; + private logger: ScreenshotObservableOpts['logger']; + private waitErrorRegistered = false; + + constructor( + private readonly driver: HeadlessChromiumDriver, + opts: ScreenshotObservableOpts, + private timeouts: PhaseTimeouts + ) { + this.conditionalHeaders = opts.conditionalHeaders; + this.layout = opts.layout; + this.logger = opts.logger; + } + + /* + * Decorates a TimeoutError with context of the phase that has timed out. + */ + public waitUntil(phase: PhaseInstance) { + const { timeoutValue, label, configValue } = phase; + return (source: Rx.Observable) => { + return source.pipe( + timeout(timeoutValue), + catchError((error: string | Error) => { + if (this.waitErrorRegistered) { + throw error; // do not create a stack of errors within the error + } + + this.logger.error(error); + let throwError = new Error(`The "${label}" phase encountered an error: ${error}`); + + if (error instanceof Rx.TimeoutError) { + throwError = new Error( + `The "${label}" phase took longer than` + + ` ${numberToDuration(timeoutValue).asSeconds()} seconds.` + + ` You may need to increase "${configValue}": ${error}` + ); + } + + this.waitErrorRegistered = true; + this.logger.error(throwError); + throw throwError; + }) + ); + }; + } + + private openUrl(index: number, urlOrUrlLocatorTuple: UrlOrUrlLocatorTuple) { + return mergeMap(() => + openUrl( + this.timeouts.openUrl.timeoutValue, + this.driver, + index, + urlOrUrlLocatorTuple, + this.conditionalHeaders, + this.logger + ) + ); + } + + private waitForElements() { + const driver = this.driver; + const waitTimeout = this.timeouts.waitForElements.timeoutValue; + return (withPageOpen: Rx.Observable) => + withPageOpen.pipe( + mergeMap(() => getNumberOfItems(waitTimeout, driver, this.layout, this.logger)), + mergeMap(async (itemsCount) => { + // set the viewport to the dimentions from the job, to allow elements to flow into the expected layout + const viewport = this.layout.getViewport(itemsCount) || getDefaultViewPort(); + await Promise.all([ + driver.setViewport(viewport, this.logger), + waitForVisualizations(waitTimeout, driver, itemsCount, this.layout, this.logger), + ]); + }) + ); + } + + private completeRender(apmTrans: apm.Transaction | null) { + const driver = this.driver; + const layout = this.layout; + const logger = this.logger; + + return (withElements: Rx.Observable) => + withElements.pipe( + mergeMap(async () => { + // Waiting till _after_ elements have rendered before injecting our CSS + // allows for them to be displayed properly in many cases + await injectCustomCss(driver, layout, logger); + + const apmPositionElements = apmTrans?.startSpan('position_elements', 'correction'); + // position panel elements for print layout + await layout.positionElements?.(driver, logger); + apmPositionElements?.end(); + + await waitForRenderComplete(this.timeouts.loadDelay, driver, layout, logger); + }), + mergeMap(() => + Promise.all([ + getTimeRange(driver, layout, logger), + getElementPositionAndAttributes(driver, layout, logger), + getRenderErrors(driver, layout, logger), + ]).then(([timeRange, elementsPositionAndAttributes, renderErrors]) => ({ + elementsPositionAndAttributes, + timeRange, + renderErrors, + })) + ) + ); + } + + public setupPage( + index: number, + urlOrUrlLocatorTuple: UrlOrUrlLocatorTuple, + apmTrans: apm.Transaction | null + ) { + return (initial: Rx.Observable) => + initial.pipe( + this.openUrl(index, urlOrUrlLocatorTuple), + this.waitUntil(this.timeouts.openUrl), + this.waitForElements(), + this.waitUntil(this.timeouts.waitForElements), + this.completeRender(apmTrans), + this.waitUntil(this.timeouts.renderComplete) + ); + } + + public getScreenshots() { + return (withRenderComplete: Rx.Observable) => + withRenderComplete.pipe( + mergeMap(async (data: PageSetupResults): Promise => { + this.checkPageIsOpen(); // fail the report job if the browser has closed + + const elements = + data.elementsPositionAndAttributes ?? + getDefaultElementPosition(this.layout.getViewport(1)); + const screenshots = await getScreenshots(this.driver, elements, this.logger); + const { timeRange, error: setupError } = data; + + return { + timeRange, + screenshots, + error: setupError, + elementsPositionAndAttributes: elements, + }; + }) + ); + } + + public checkPageIsOpen() { + if (!this.driver.isPageOpen()) { + throw getChromiumDisconnectedError(); + } + } +} + +const DEFAULT_SCREENSHOT_CLIP_HEIGHT = 1200; +const DEFAULT_SCREENSHOT_CLIP_WIDTH = 1800; + +const getDefaultElementPosition = (dimensions: { height?: number; width?: number } | null) => { + const height = dimensions?.height || DEFAULT_SCREENSHOT_CLIP_HEIGHT; + const width = dimensions?.width || DEFAULT_SCREENSHOT_CLIP_WIDTH; + + return [ + { + position: { + boundingClientRect: { top: 0, left: 0, height, width }, + scroll: { x: 0, y: 0 }, + }, + attributes: {}, + }, + ]; +}; + +/* + * If Kibana is showing a non-HTML error message, the viewport might not be + * provided by the browser. + */ +const getDefaultViewPort = () => ({ + height: DEFAULT_SCREENSHOT_CLIP_HEIGHT, + width: DEFAULT_SCREENSHOT_CLIP_WIDTH, + zoom: 1, +}); diff --git a/x-pack/plugins/reporting/server/lib/screenshots/open_url.ts b/x-pack/plugins/reporting/server/lib/screenshots/open_url.ts index 588cd792bdf06..63a5e80289e3e 100644 --- a/x-pack/plugins/reporting/server/lib/screenshots/open_url.ts +++ b/x-pack/plugins/reporting/server/lib/screenshots/open_url.ts @@ -6,21 +6,25 @@ */ import { i18n } from '@kbn/i18n'; -import { LocatorParams, UrlOrUrlLocatorTuple } from '../../../common/types'; import { LevelLogger, startTrace } from '../'; -import { durationToNumber } from '../../../common/schema_utils'; +import { LocatorParams, UrlOrUrlLocatorTuple } from '../../../common/types'; import { HeadlessChromiumDriver } from '../../browsers'; import { ConditionalHeaders } from '../../export_types/common'; -import { CaptureConfig } from '../../types'; +import { DEFAULT_PAGELOAD_SELECTOR } from './constants'; export const openUrl = async ( - captureConfig: CaptureConfig, + timeout: number, browser: HeadlessChromiumDriver, + index: number, urlOrUrlLocatorTuple: UrlOrUrlLocatorTuple, - waitForSelector: string, conditionalHeaders: ConditionalHeaders, logger: LevelLogger ): Promise => { + // If we're moving to another page in the app, we'll want to wait for the app to tell us + // it's loaded the next page. + const page = index + 1; + const waitForSelector = page > 1 ? `[data-shared-page="${page}"]` : DEFAULT_PAGELOAD_SELECTOR; + const endTrace = startTrace('open_url', 'wait'); let url: string; let locator: undefined | LocatorParams; @@ -32,14 +36,13 @@ export const openUrl = async ( } try { - const timeout = durationToNumber(captureConfig.timeouts.openUrl); await browser.open(url, { conditionalHeaders, waitForSelector, timeout, locator }, logger); } catch (err) { logger.error(err); throw new Error( i18n.translate('xpack.reporting.screencapture.couldntLoadKibana', { - defaultMessage: `An error occurred when trying to open the Kibana URL. You may need to increase '{configKey}'. {error}`, - values: { configKey: 'xpack.reporting.capture.timeouts.openUrl', error: err }, + defaultMessage: `An error occurred when trying to open the Kibana URL: {error}`, + values: { error: err }, }) ); } diff --git a/x-pack/plugins/reporting/server/lib/screenshots/wait_for_render.ts b/x-pack/plugins/reporting/server/lib/screenshots/wait_for_render.ts index f8293bfce3346..1ac4b58b61507 100644 --- a/x-pack/plugins/reporting/server/lib/screenshots/wait_for_render.ts +++ b/x-pack/plugins/reporting/server/lib/screenshots/wait_for_render.ts @@ -6,15 +6,13 @@ */ import { i18n } from '@kbn/i18n'; -import { durationToNumber } from '../../../common/schema_utils'; -import { HeadlessChromiumDriver } from '../../browsers'; -import { CaptureConfig } from '../../types'; import { LevelLogger, startTrace } from '../'; +import { HeadlessChromiumDriver } from '../../browsers'; import { LayoutInstance } from '../layouts'; import { CONTEXT_WAITFORRENDER } from './constants'; export const waitForRenderComplete = async ( - captureConfig: CaptureConfig, + loadDelay: number, browser: HeadlessChromiumDriver, layout: LayoutInstance, logger: LevelLogger @@ -69,7 +67,7 @@ export const waitForRenderComplete = async ( return Promise.all(renderedTasks).then(hackyWaitForVisualizations); }, - args: [layout.selectors.renderComplete, durationToNumber(captureConfig.loadDelay)], + args: [layout.selectors.renderComplete, loadDelay], }, { context: CONTEXT_WAITFORRENDER }, logger diff --git a/x-pack/plugins/reporting/server/lib/screenshots/wait_for_visualizations.ts b/x-pack/plugins/reporting/server/lib/screenshots/wait_for_visualizations.ts index 0ab274da7e1cf..d4bf1db2a0c5a 100644 --- a/x-pack/plugins/reporting/server/lib/screenshots/wait_for_visualizations.ts +++ b/x-pack/plugins/reporting/server/lib/screenshots/wait_for_visualizations.ts @@ -6,10 +6,8 @@ */ import { i18n } from '@kbn/i18n'; -import { durationToNumber } from '../../../common/schema_utils'; import { LevelLogger, startTrace } from '../'; import { HeadlessChromiumDriver } from '../../browsers'; -import { CaptureConfig } from '../../types'; import { LayoutInstance } from '../layouts'; import { CONTEXT_WAITFORELEMENTSTOBEINDOM } from './constants'; @@ -25,7 +23,7 @@ const getCompletedItemsCount = ({ renderCompleteSelector }: SelectorArgs) => { * 3. Wait for the render complete event to be fired once for each item */ export const waitForVisualizations = async ( - captureConfig: CaptureConfig, + timeout: number, browser: HeadlessChromiumDriver, toEqual: number, layout: LayoutInstance, @@ -42,7 +40,6 @@ export const waitForVisualizations = async ( ); try { - const timeout = durationToNumber(captureConfig.timeouts.renderComplete); await browser.waitFor( { fn: getCompletedItemsCount, args: [{ renderCompleteSelector }], toEqual, timeout }, { context: CONTEXT_WAITFORELEMENTSTOBEINDOM }, @@ -54,12 +51,8 @@ export const waitForVisualizations = async ( logger.error(err); throw new Error( i18n.translate('xpack.reporting.screencapture.couldntFinishRendering', { - defaultMessage: `An error occurred when trying to wait for {count} visualizations to finish rendering. You may need to increase '{configKey}'. {error}`, - values: { - count: toEqual, - configKey: 'xpack.reporting.capture.timeouts.renderComplete', - error: err, - }, + defaultMessage: `An error occurred when trying to wait for {count} visualizations to finish rendering. {error}`, + values: { count: toEqual, error: err }, }) ); } diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 79d092cebe366..c941cbd9ddf80 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -19451,13 +19451,10 @@ "xpack.reporting.registerFeature.reportingDescription": "Discover、可視化、ダッシュボードから生成されたレポートを管理します。", "xpack.reporting.registerFeature.reportingTitle": "レポート", "xpack.reporting.screencapture.browserWasClosed": "ブラウザーは予期せず終了しました。詳細については、サーバーログを確認してください。", - "xpack.reporting.screencapture.couldntFinishRendering": "{count} 件のビジュアライゼーションのレンダリングが完了するのを待つ間にエラーが発生しました。「{configKey}」を増やす必要があるかもしれません。 {error}", - "xpack.reporting.screencapture.couldntLoadKibana": "Kibana URL を開こうとするときにエラーが発生しました。「{configKey}」を増やす必要があるかもしれません。 {error}", "xpack.reporting.screencapture.injectCss": "Kibana CSS をレポート用に更新しようとしたときにエラーが発生しました。{error}", "xpack.reporting.screencapture.injectingCss": "カスタム css の投入中", "xpack.reporting.screencapture.logWaitingForElements": "要素または項目のカウント属性を待ち、または見つからないため中断", "xpack.reporting.screencapture.noElements": "ビジュアライゼーションパネルのページを読み取る間にエラーが発生しました:パネルが見つかりませんでした。", - "xpack.reporting.screencapture.readVisualizationsError": "ビジュアライゼーションパネル情報のページを読み取ろうとしたときにエラーが発生しました。「{configKey}」を増やす必要があるかもしれません。 {error}", "xpack.reporting.screencapture.renderIsComplete": "レンダリングが完了しました", "xpack.reporting.screencapture.screenshotsTaken": "撮影したスクリーンショット:{numScreenhots}", "xpack.reporting.screencapture.takingScreenshots": "スクリーンショットの撮影中", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index c80cd968f38a8..e9e9f02c8fe99 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -19733,13 +19733,10 @@ "xpack.reporting.registerFeature.reportingDescription": "管理您从 Discover、Visualize 和 Dashboard 生成的报告。", "xpack.reporting.registerFeature.reportingTitle": "Reporting", "xpack.reporting.screencapture.browserWasClosed": "浏览器已意外关闭!有关更多信息,请查看服务器日志。", - "xpack.reporting.screencapture.couldntFinishRendering": "尝试等候 {count} 个可视化完成渲染时发生错误。您可能需要增加“{configKey}”。{error}", - "xpack.reporting.screencapture.couldntLoadKibana": "尝试打开 Kibana URL 时发生了错误。您可能需要增加“{configKey}”。{error}", "xpack.reporting.screencapture.injectCss": "尝试为 Reporting 更新 Kibana CSS 时发生错误。{error}", "xpack.reporting.screencapture.injectingCss": "正在注入定制 css", "xpack.reporting.screencapture.logWaitingForElements": "等候元素或项目计数属性;或未发现要中断", "xpack.reporting.screencapture.noElements": "读取页面以获取可视化面板时发生了错误:未找到任何面板。", - "xpack.reporting.screencapture.readVisualizationsError": "尝试页面以获取可视化面板信息时发生了错误。您可能需要增加“{configKey}”。{error}", "xpack.reporting.screencapture.renderIsComplete": "渲染已完成", "xpack.reporting.screencapture.screenshotsTaken": "已捕获的屏幕截图:{numScreenhots}", "xpack.reporting.screencapture.takingScreenshots": "正在捕获屏幕截图", From 8c3b4337eba67d83a67e6ae46f252c66c35646c0 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Mon, 18 Oct 2021 21:43:49 +0300 Subject: [PATCH 007/204] [DataTable] Add rowHeightsOptions to table (#114637) * Upgraded the version of EUI to 38.2.0 from 38.0.1 * Updated the i18n mappings required for EUI v.38.2.0 * Update i18n snapshots and resolve linting error * Removed html_id_generator mocks. Current mock was failing due to missing useGeneratedHtmlId export. This is safe to remove because EUI contains a .testenv that contains an mock for html_id_generator. More info at https://github.com/elastic/eui/blob/master/src/services/accessibility/html_id_generator.testenv.ts * Resolve linting error in i18n mapping file * Removed html_id_generator mocks. Current mock was failing due to missing useGeneratedHtmlId export. This is safe to remove because EUI contains a .testenv that contains a mock for html_id_generator. More info at https://github.com/elastic/eui/blob/master/src/services/accessibility/html_id_generator.testenv.ts * Update plugin snapshots * Resolve merge conflict in license_checker config.ts file * Upgrade EUI to version 39.0.0 from the original target (38.2.0) to handle an issue found with a functional test during the original upgrade * Updated the i18n mapping for EUI v.39.0.0 * Update various snapshots to account for the an i18n translation token addition in EUI v. 39.0.0 * Updated test cases marked as obsolete by CI * Update src/dev/license_checker/config.ts Removing TODO comments from src/dev/license_checker/config.ts as they are no longer needed. Co-authored-by: Constance * Add option auto fit row to content * Fix tests * Fix tests * Add temp fix for correct rendering grid with auto-height when changing data or setting * Fix lint * Fix lint and tests * Adds new dependency for temp fix Co-authored-by: Brianna Hall Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Bree Hall <40739624+breehall@users.noreply.github.com> Co-authored-by: Constance --- src/plugins/vis_types/table/common/types.ts | 1 + .../__snapshots__/table_vis_fn.test.ts.snap | 1 + .../table_vis_basic.test.tsx.snap | 3 ++ .../components/table_vis_basic.test.tsx | 3 +- .../public/components/table_vis_basic.tsx | 36 +++++++++++++++++-- .../public/components/table_vis_cell.tsx | 4 +-- .../public/components/table_vis_options.tsx | 10 ++++++ .../table/public/table_vis_fn.test.ts | 1 + .../vis_types/table/public/table_vis_fn.ts | 5 +++ .../vis_types/table/public/table_vis_type.ts | 1 + src/plugins/vis_types/table/public/to_ast.ts | 1 + 11 files changed, 60 insertions(+), 6 deletions(-) diff --git a/src/plugins/vis_types/table/common/types.ts b/src/plugins/vis_types/table/common/types.ts index 015af80adf0dc..9f607a964977b 100644 --- a/src/plugins/vis_types/table/common/types.ts +++ b/src/plugins/vis_types/table/common/types.ts @@ -24,5 +24,6 @@ export interface TableVisParams { showTotal: boolean; totalFunc: AggTypes; percentageCol: string; + autoFitRowToContent?: boolean; row?: boolean; } diff --git a/src/plugins/vis_types/table/public/__snapshots__/table_vis_fn.test.ts.snap b/src/plugins/vis_types/table/public/__snapshots__/table_vis_fn.test.ts.snap index be7e2822f128d..1a2badbd26634 100644 --- a/src/plugins/vis_types/table/public/__snapshots__/table_vis_fn.test.ts.snap +++ b/src/plugins/vis_types/table/public/__snapshots__/table_vis_fn.test.ts.snap @@ -26,6 +26,7 @@ Object { "type": "render", "value": Object { "visConfig": Object { + "autoFitRowToContent": false, "buckets": Array [], "metrics": Array [ Object { diff --git a/src/plugins/vis_types/table/public/components/__snapshots__/table_vis_basic.test.tsx.snap b/src/plugins/vis_types/table/public/components/__snapshots__/table_vis_basic.test.tsx.snap index 85cf9422630d6..38e3dcbb7097c 100644 --- a/src/plugins/vis_types/table/public/components/__snapshots__/table_vis_basic.test.tsx.snap +++ b/src/plugins/vis_types/table/public/components/__snapshots__/table_vis_basic.test.tsx.snap @@ -17,6 +17,7 @@ exports[`TableVisBasic should init data grid 1`] = ` "header": "underline", } } + key="0" minSizeForControls={1} onColumnResize={[Function]} renderCellValue={[Function]} @@ -55,6 +56,7 @@ exports[`TableVisBasic should init data grid with title provided - for split mod "header": "underline", } } + key="0" minSizeForControls={1} onColumnResize={[Function]} renderCellValue={[Function]} @@ -86,6 +88,7 @@ exports[`TableVisBasic should render the toolbar 1`] = ` "header": "underline", } } + key="0" minSizeForControls={1} onColumnResize={[Function]} renderCellValue={[Function]} diff --git a/src/plugins/vis_types/table/public/components/table_vis_basic.test.tsx b/src/plugins/vis_types/table/public/components/table_vis_basic.test.tsx index 0fb74a41b5df0..0296f2ec1327a 100644 --- a/src/plugins/vis_types/table/public/components/table_vis_basic.test.tsx +++ b/src/plugins/vis_types/table/public/components/table_vis_basic.test.tsx @@ -67,6 +67,7 @@ describe('TableVisBasic', () => { }); it('should sort rows by column and pass the sorted rows for consumers', () => { + (createTableVisCell as jest.Mock).mockClear(); const uiStateProps = { ...props.uiStateProps, sort: { @@ -96,7 +97,7 @@ describe('TableVisBasic', () => { visConfig={{ ...props.visConfig, showToolbar: true }} /> ); - expect(createTableVisCell).toHaveBeenCalledWith(sortedRows, table.formattedColumns); + expect(createTableVisCell).toHaveBeenCalledWith(sortedRows, table.formattedColumns, undefined); expect(createGridColumns).toHaveBeenCalledWith( table.columns, sortedRows, diff --git a/src/plugins/vis_types/table/public/components/table_vis_basic.tsx b/src/plugins/vis_types/table/public/components/table_vis_basic.tsx index e627b9e7f92f2..cfe1ce5d40a1e 100644 --- a/src/plugins/vis_types/table/public/components/table_vis_basic.tsx +++ b/src/plugins/vis_types/table/public/components/table_vis_basic.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import React, { memo, useCallback, useMemo } from 'react'; +import React, { memo, useCallback, useMemo, useEffect, useState, useRef } from 'react'; import { EuiDataGrid, EuiDataGridProps, EuiDataGridSorting, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { orderBy } from 'lodash'; @@ -47,8 +47,16 @@ export const TableVisBasic = memo( // renderCellValue is a component which renders a cell based on column and row indexes const renderCellValue = useMemo( - () => createTableVisCell(sortedRows, formattedColumns), - [formattedColumns, sortedRows] + () => createTableVisCell(sortedRows, formattedColumns, visConfig.autoFitRowToContent), + [formattedColumns, sortedRows, visConfig.autoFitRowToContent] + ); + + const rowHeightsOptions = useMemo( + () => + visConfig.autoFitRowToContent + ? ({ defaultHeight: 'auto' } as unknown as EuiDataGridProps['rowHeightsOptions']) + : undefined, + [visConfig.autoFitRowToContent] ); // Columns config @@ -103,6 +111,26 @@ export const TableVisBasic = memo( [columns, setColumnsWidth] ); + const firstRender = useRef(true); + const [dataGridUpdateCounter, setDataGridUpdateCounter] = useState(0); + + // key was added as temporary solution to force re-render if we change autoFitRowToContent or we get new data + // cause we have problem with correct updating height cache in EUI datagrid when we use auto-height + // will be removed as soon as fix problem on EUI side + useEffect(() => { + // skip first render + if (firstRender.current) { + firstRender.current = false; + return; + } + // skip if auto height was turned off + if (!visConfig.autoFitRowToContent) { + return; + } + // update counter to remount grid from scratch + setDataGridUpdateCounter((counter) => counter + 1); + }, [visConfig.autoFitRowToContent, table, sort, pagination, columnsWidth]); + return ( <> {title && ( @@ -111,12 +139,14 @@ export const TableVisBasic = memo( )} id), diff --git a/src/plugins/vis_types/table/public/components/table_vis_cell.tsx b/src/plugins/vis_types/table/public/components/table_vis_cell.tsx index 9749cdcb5740c..7d7af447db489 100644 --- a/src/plugins/vis_types/table/public/components/table_vis_cell.tsx +++ b/src/plugins/vis_types/table/public/components/table_vis_cell.tsx @@ -13,7 +13,7 @@ import { DatatableRow } from 'src/plugins/expressions'; import { FormattedColumns } from '../types'; export const createTableVisCell = - (rows: DatatableRow[], formattedColumns: FormattedColumns) => + (rows: DatatableRow[], formattedColumns: FormattedColumns, autoFitRowToContent?: boolean) => ({ rowIndex, columnId }: EuiDataGridCellValueElementProps) => { const rowValue = rows[rowIndex][columnId]; const column = formattedColumns[columnId]; @@ -28,7 +28,7 @@ export const createTableVisCell = */ dangerouslySetInnerHTML={{ __html: content }} // eslint-disable-line react/no-danger data-test-subj="tbvChartCellContent" - className="tbvChartCellContent" + className={autoFitRowToContent ? '' : 'tbvChartCellContent'} /> ); diff --git a/src/plugins/vis_types/table/public/components/table_vis_options.tsx b/src/plugins/vis_types/table/public/components/table_vis_options.tsx index 8a6b8586fce7d..698aca6034a6b 100644 --- a/src/plugins/vis_types/table/public/components/table_vis_options.tsx +++ b/src/plugins/vis_types/table/public/components/table_vis_options.tsx @@ -93,6 +93,16 @@ function TableOptions({ data-test-subj="showMetricsAtAllLevels" /> + + { splitColumn: undefined, splitRow: undefined, showMetricsAtAllLevels: false, + autoFitRowToContent: false, sort: { columnIndex: null, direction: null, diff --git a/src/plugins/vis_types/table/public/table_vis_fn.ts b/src/plugins/vis_types/table/public/table_vis_fn.ts index ebddb0b4b7fef..861923ef5086e 100644 --- a/src/plugins/vis_types/table/public/table_vis_fn.ts +++ b/src/plugins/vis_types/table/public/table_vis_fn.ts @@ -118,6 +118,11 @@ export const createTableVisFn = (): TableExpressionFunctionDefinition => ({ defaultMessage: 'Specifies calculating function for the total row. Possible options are: ', }), }, + autoFitRowToContent: { + types: ['boolean'], + help: '', + default: false, + }, }, fn(input, args, handlers) { const convertedData = tableVisResponseHandler(input, args); diff --git a/src/plugins/vis_types/table/public/table_vis_type.ts b/src/plugins/vis_types/table/public/table_vis_type.ts index 4664e87cea79b..a641224e23f52 100644 --- a/src/plugins/vis_types/table/public/table_vis_type.ts +++ b/src/plugins/vis_types/table/public/table_vis_type.ts @@ -35,6 +35,7 @@ export const tableVisTypeDefinition: VisTypeDefinition = { showToolbar: false, totalFunc: 'sum', percentageCol: '', + autoFitRowToContent: false, }, }, editorConfig: { diff --git a/src/plugins/vis_types/table/public/to_ast.ts b/src/plugins/vis_types/table/public/to_ast.ts index 8e1c92c8dde4f..0268708f22dfe 100644 --- a/src/plugins/vis_types/table/public/to_ast.ts +++ b/src/plugins/vis_types/table/public/to_ast.ts @@ -64,6 +64,7 @@ export const toExpressionAst: VisToExpressionAst = (vis, params) showMetricsAtAllLevels: vis.params.showMetricsAtAllLevels, showToolbar: vis.params.showToolbar, showTotal: vis.params.showTotal, + autoFitRowToContent: vis.params.autoFitRowToContent, totalFunc: vis.params.totalFunc, title: vis.title, metrics: metrics.map(prepareDimension), From 4dcb09df59d4f9c4400b8cfc5b279179932da7d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Marcondes?= <55978943+cauemarcondes@users.noreply.github.com> Date: Mon, 18 Oct 2021 14:52:01 -0400 Subject: [PATCH 008/204] [APM] Adding error rate tests (#115190) --- .../tests/error_rate/service_apis.ts | 213 ++++++++++++++++++ .../test/apm_api_integration/tests/index.ts | 4 + 2 files changed, 217 insertions(+) create mode 100644 x-pack/test/apm_api_integration/tests/error_rate/service_apis.ts diff --git a/x-pack/test/apm_api_integration/tests/error_rate/service_apis.ts b/x-pack/test/apm_api_integration/tests/error_rate/service_apis.ts new file mode 100644 index 0000000000000..75ea10ed4d9d4 --- /dev/null +++ b/x-pack/test/apm_api_integration/tests/error_rate/service_apis.ts @@ -0,0 +1,213 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { service, timerange } from '@elastic/apm-generator'; +import expect from '@kbn/expect'; +import { mean, meanBy, sumBy } from 'lodash'; +import { LatencyAggregationType } from '../../../../plugins/apm/common/latency_aggregation_types'; +import { isFiniteNumber } from '../../../../plugins/apm/common/utils/is_finite_number'; +import { PromiseReturnType } from '../../../../plugins/observability/typings/common'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { registry } from '../../common/registry'; + +export default function ApiTest({ getService }: FtrProviderContext) { + const apmApiClient = getService('apmApiClient'); + const traceData = getService('traceData'); + + const serviceName = 'synth-go'; + const start = new Date('2021-01-01T00:00:00.000Z').getTime(); + const end = new Date('2021-01-01T00:15:00.000Z').getTime() - 1; + + async function getErrorRateValues({ + processorEvent, + }: { + processorEvent: 'transaction' | 'metric'; + }) { + const commonQuery = { + start: new Date(start).toISOString(), + end: new Date(end).toISOString(), + environment: 'ENVIRONMENT_ALL', + }; + const [ + serviceInventoryAPIResponse, + transactionsErrorRateChartAPIResponse, + transactionsGroupDetailsAPIResponse, + serviceInstancesAPIResponse, + ] = await Promise.all([ + apmApiClient.readUser({ + endpoint: 'GET /internal/apm/services', + params: { + query: { + ...commonQuery, + kuery: `service.name : "${serviceName}" and processor.event : "${processorEvent}"`, + }, + }, + }), + apmApiClient.readUser({ + endpoint: 'GET /internal/apm/services/{serviceName}/transactions/charts/error_rate', + params: { + path: { serviceName }, + query: { + ...commonQuery, + kuery: `processor.event : "${processorEvent}"`, + transactionType: 'request', + }, + }, + }), + apmApiClient.readUser({ + endpoint: `GET /internal/apm/services/{serviceName}/transactions/groups/main_statistics`, + params: { + path: { serviceName }, + query: { + ...commonQuery, + kuery: `processor.event : "${processorEvent}"`, + transactionType: 'request', + latencyAggregationType: 'avg' as LatencyAggregationType, + }, + }, + }), + apmApiClient.readUser({ + endpoint: `GET /internal/apm/services/{serviceName}/service_overview_instances/main_statistics`, + params: { + path: { serviceName }, + query: { + ...commonQuery, + kuery: `processor.event : "${processorEvent}"`, + transactionType: 'request', + latencyAggregationType: 'avg' as LatencyAggregationType, + }, + }, + }), + ]); + + const serviceInventoryErrorRate = + serviceInventoryAPIResponse.body.items[0].transactionErrorRate; + + const errorRateChartApiMean = meanBy( + transactionsErrorRateChartAPIResponse.body.currentPeriod.transactionErrorRate.filter( + (item) => isFiniteNumber(item.y) && item.y > 0 + ), + 'y' + ); + + const transactionsGroupErrorRateSum = sumBy( + transactionsGroupDetailsAPIResponse.body.transactionGroups, + 'errorRate' + ); + + const serviceInstancesErrorRateSum = sumBy( + serviceInstancesAPIResponse.body.currentPeriod, + 'errorRate' + ); + + return { + serviceInventoryErrorRate, + errorRateChartApiMean, + transactionsGroupErrorRateSum, + serviceInstancesErrorRateSum, + }; + } + + let errorRateMetricValues: PromiseReturnType; + let errorTransactionValues: PromiseReturnType; + + registry.when('Services APIs', { config: 'basic', archives: ['apm_8.0.0_empty'] }, () => { + describe('when data is loaded ', () => { + const GO_PROD_LIST_RATE = 75; + const GO_PROD_LIST_ERROR_RATE = 25; + const GO_PROD_ID_RATE = 50; + const GO_PROD_ID_ERROR_RATE = 50; + before(async () => { + const serviceGoProdInstance = service(serviceName, 'production', 'go').instance( + 'instance-a' + ); + + const transactionNameProductList = 'GET /api/product/list'; + const transactionNameProductId = 'GET /api/product/:id'; + + await traceData.index([ + ...timerange(start, end) + .interval('1m') + .rate(GO_PROD_LIST_RATE) + .flatMap((timestamp) => + serviceGoProdInstance + .transaction(transactionNameProductList) + .timestamp(timestamp) + .duration(1000) + .success() + .serialize() + ), + ...timerange(start, end) + .interval('1m') + .rate(GO_PROD_LIST_ERROR_RATE) + .flatMap((timestamp) => + serviceGoProdInstance + .transaction(transactionNameProductList) + .duration(1000) + .timestamp(timestamp) + .failure() + .serialize() + ), + ...timerange(start, end) + .interval('1m') + .rate(GO_PROD_ID_RATE) + .flatMap((timestamp) => + serviceGoProdInstance + .transaction(transactionNameProductId) + .timestamp(timestamp) + .duration(1000) + .success() + .serialize() + ), + ...timerange(start, end) + .interval('1m') + .rate(GO_PROD_ID_ERROR_RATE) + .flatMap((timestamp) => + serviceGoProdInstance + .transaction(transactionNameProductId) + .duration(1000) + .timestamp(timestamp) + .failure() + .serialize() + ), + ]); + }); + + after(() => traceData.clean()); + + describe('compare error rate value between service inventory, error rate chart, service inventory and transactions apis', () => { + before(async () => { + [errorTransactionValues, errorRateMetricValues] = await Promise.all([ + getErrorRateValues({ processorEvent: 'transaction' }), + getErrorRateValues({ processorEvent: 'metric' }), + ]); + }); + + it('returns same avg error rate value for Transaction-based and Metric-based data', () => { + [ + errorTransactionValues.serviceInventoryErrorRate, + errorTransactionValues.errorRateChartApiMean, + errorTransactionValues.serviceInstancesErrorRateSum, + errorRateMetricValues.serviceInventoryErrorRate, + errorRateMetricValues.errorRateChartApiMean, + errorRateMetricValues.serviceInstancesErrorRateSum, + ].forEach((value) => + expect(value).to.be.equal(mean([GO_PROD_LIST_ERROR_RATE, GO_PROD_ID_ERROR_RATE]) / 100) + ); + }); + + it('returns same sum error rate value for Transaction-based and Metric-based data', () => { + [ + errorTransactionValues.transactionsGroupErrorRateSum, + errorRateMetricValues.transactionsGroupErrorRateSum, + ].forEach((value) => + expect(value).to.be.equal((GO_PROD_LIST_ERROR_RATE + GO_PROD_ID_ERROR_RATE) / 100) + ); + }); + }); + }); + }); +} diff --git a/x-pack/test/apm_api_integration/tests/index.ts b/x-pack/test/apm_api_integration/tests/index.ts index c15a7d39a6cf6..09f4e2596ea46 100644 --- a/x-pack/test/apm_api_integration/tests/index.ts +++ b/x-pack/test/apm_api_integration/tests/index.ts @@ -229,6 +229,10 @@ export default function apmApiIntegrationTests(providerContext: FtrProviderConte loadTestFile(require.resolve('./historical_data/has_data')); }); + describe('error_rate/service_apis', function () { + loadTestFile(require.resolve('./error_rate/service_apis')); + }); + describe('latency/service_apis', function () { loadTestFile(require.resolve('./latency/service_apis')); }); From 83e9c7afdf18a1bd338da3497dfa8370e810e6ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yulia=20=C4=8Cech?= <6585477+yuliacech@users.noreply.github.com> Date: Mon, 18 Oct 2021 20:55:57 +0200 Subject: [PATCH 009/204] [Snapshot and Restore] Server side snapshots pagination (#110266) * [Snapshot % Restore] Added server side pagination and sorting to get snapshots route, refactored snapshots table to use EuiBasicTable with controlled pagination instead of EuiInMemoryTable * [Snapshot & Restore] Added server side sorting by shards and failed shards counts * [Snapshot & Restore] Fixed i18n errors * [Snapshot & Restore] Added server side sorting by repository * [Snapshot & Restore] Implemented server side search request for snapshot, repository and policy name * [Snapshot & Restore] Fixed eslint errors * [Snapshot & Restore] Removed uncommented code * [Snapshot & Restore] Fixed pagination/search bug * [Snapshot & Restore] Fixed pagination/search bug * [Snapshot & Restore] Fixed text truncate bug * [Snapshot & Restore] Fixed non existent repository search error * Update x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/components/snapshot_search_bar.tsx Co-authored-by: CJ Cenizal * Update x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/components/snapshot_empty_prompt.tsx Co-authored-by: James Rodewig <40268737+jrodewig@users.noreply.github.com> * [Snapshot & Restore] Fixed missing i18n and no snapshots callout * [Snapshot & Restore] Moved "getSnapshotSearchWildcard" to a separate file and added unit tests * [Snapshot & Restore] Added api integration tests for "get snapshots" endpoint (pagination, sorting, search) * [Snapshot & Restore] Renamed SnapshotSearchOptions/SnapshotTableOptions to -Params and added the link to the specs issue * [Snapshot & Restore] Fixed search wildcard to also match string in the middle of the value, not only starting with the string. Also updated the tests following the code review suggestions to make them more readable. * [Snapshot & Restore] Added incremental search back to snapshots list and a debounce of 500ms * [Snapshot & Restore] Updated snapshot search debounce value and extracted it into a constant * [Snapshot & Restore] Renamed debounceValue to cachedListParams and added a comment why debounce is used Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: CJ Cenizal Co-authored-by: James Rodewig <40268737+jrodewig@users.noreply.github.com> --- .../helpers/http_requests.ts | 4 +- .../__jest__/client_integration/home.test.ts | 14 +- .../snapshot_restore/common/constants.ts | 6 - .../public/application/lib/index.ts | 9 + .../application/lib/snapshot_list_params.ts | 88 +++ .../{snapshot_table => components}/index.ts | 4 + .../components/repository_empty_prompt.tsx | 62 ++ .../components/repository_error.tsx | 51 ++ .../components/snapshot_empty_prompt.tsx | 123 +++ .../components/snapshot_search_bar.tsx | 178 +++++ .../snapshot_table.tsx | 224 ++---- .../home/snapshot_list/snapshot_list.tsx | 315 ++------ .../services/http/snapshot_requests.ts | 7 +- .../snapshot_restore/public/shared_imports.ts | 2 + .../lib/get_snapshot_search_wildcard.test.ts | 60 ++ .../lib/get_snapshot_search_wildcard.ts | 30 + .../server/routes/api/snapshots.test.ts | 4 + .../server/routes/api/snapshots.ts | 102 ++- .../server/routes/api/validate_schemas.ts | 25 + .../snapshot_restore/server/routes/helpers.ts | 2 +- .../translations/translations/ja-JP.json | 3 - .../translations/translations/zh-CN.json | 3 - .../apis/management/snapshot_restore/index.ts | 3 +- .../snapshot_restore/lib/elasticsearch.ts | 39 +- .../management/snapshot_restore/lib/index.ts | 2 +- .../{snapshot_restore.ts => policies.ts} | 11 +- .../management/snapshot_restore/snapshots.ts | 729 ++++++++++++++++++ x-pack/test/api_integration/config.ts | 1 + 28 files changed, 1640 insertions(+), 461 deletions(-) create mode 100644 x-pack/plugins/snapshot_restore/public/application/lib/snapshot_list_params.ts rename x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/{snapshot_table => components}/index.ts (55%) create mode 100644 x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/components/repository_empty_prompt.tsx create mode 100644 x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/components/repository_error.tsx create mode 100644 x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/components/snapshot_empty_prompt.tsx create mode 100644 x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/components/snapshot_search_bar.tsx rename x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/{snapshot_table => components}/snapshot_table.tsx (71%) create mode 100644 x-pack/plugins/snapshot_restore/server/lib/get_snapshot_search_wildcard.test.ts create mode 100644 x-pack/plugins/snapshot_restore/server/lib/get_snapshot_search_wildcard.ts rename x-pack/test/api_integration/apis/management/snapshot_restore/{snapshot_restore.ts => policies.ts} (95%) create mode 100644 x-pack/test/api_integration/apis/management/snapshot_restore/snapshots.ts diff --git a/x-pack/plugins/snapshot_restore/__jest__/client_integration/helpers/http_requests.ts b/x-pack/plugins/snapshot_restore/__jest__/client_integration/helpers/http_requests.ts index 45b8b23cae477..605265f7311ba 100644 --- a/x-pack/plugins/snapshot_restore/__jest__/client_integration/helpers/http_requests.ts +++ b/x-pack/plugins/snapshot_restore/__jest__/client_integration/helpers/http_requests.ts @@ -6,7 +6,7 @@ */ import sinon, { SinonFakeServer } from 'sinon'; -import { API_BASE_PATH } from '../../../common/constants'; +import { API_BASE_PATH } from '../../../common'; type HttpResponse = Record | any[]; @@ -54,7 +54,7 @@ const registerHttpRequestMockHelpers = (server: SinonFakeServer) => { }; const setLoadSnapshotsResponse = (response: HttpResponse = {}) => { - const defaultResponse = { errors: {}, snapshots: [], repositories: [] }; + const defaultResponse = { errors: {}, snapshots: [], repositories: [], total: 0 }; server.respondWith('GET', `${API_BASE_PATH}snapshots`, mockResponse(defaultResponse, response)); }; diff --git a/x-pack/plugins/snapshot_restore/__jest__/client_integration/home.test.ts b/x-pack/plugins/snapshot_restore/__jest__/client_integration/home.test.ts index 52303e1134f9d..071868e23f7fe 100644 --- a/x-pack/plugins/snapshot_restore/__jest__/client_integration/home.test.ts +++ b/x-pack/plugins/snapshot_restore/__jest__/client_integration/home.test.ts @@ -8,7 +8,7 @@ import { act } from 'react-dom/test-utils'; import * as fixtures from '../../test/fixtures'; import { SNAPSHOT_STATE } from '../../public/application/constants'; -import { API_BASE_PATH } from '../../common/constants'; +import { API_BASE_PATH } from '../../common'; import { setupEnvironment, pageHelpers, @@ -431,6 +431,7 @@ describe('', () => { httpRequestsMockHelpers.setLoadSnapshotsResponse({ snapshots: [], repositories: ['my-repo'], + total: 0, }); testBed = await setup(); @@ -469,6 +470,7 @@ describe('', () => { httpRequestsMockHelpers.setLoadSnapshotsResponse({ snapshots, repositories: [REPOSITORY_NAME], + total: 2, }); testBed = await setup(); @@ -501,18 +503,10 @@ describe('', () => { }); }); - test('should show a warning if the number of snapshots exceeded the limit', () => { - // We have mocked the SNAPSHOT_LIST_MAX_SIZE to 2, so the warning should display - const { find, exists } = testBed; - expect(exists('maxSnapshotsWarning')).toBe(true); - expect(find('maxSnapshotsWarning').text()).toContain( - 'Cannot show the full list of snapshots' - ); - }); - test('should show a warning if one repository contains errors', async () => { httpRequestsMockHelpers.setLoadSnapshotsResponse({ snapshots, + total: 2, repositories: [REPOSITORY_NAME], errors: { repository_with_errors: { diff --git a/x-pack/plugins/snapshot_restore/common/constants.ts b/x-pack/plugins/snapshot_restore/common/constants.ts index a7c83ecf702e0..b18e118dc5ff6 100644 --- a/x-pack/plugins/snapshot_restore/common/constants.ts +++ b/x-pack/plugins/snapshot_restore/common/constants.ts @@ -65,9 +65,3 @@ export const TIME_UNITS: { [key: string]: 'd' | 'h' | 'm' | 's' } = { MINUTE: 'm', SECOND: 's', }; - -/** - * [Temporary workaround] In order to prevent client-side performance issues for users with a large number of snapshots, - * we set a hard-coded limit on the number of snapshots we return from the ES snapshots API - */ -export const SNAPSHOT_LIST_MAX_SIZE = 1000; diff --git a/x-pack/plugins/snapshot_restore/public/application/lib/index.ts b/x-pack/plugins/snapshot_restore/public/application/lib/index.ts index 1ec4d5b2907f2..19a42bef4cea4 100644 --- a/x-pack/plugins/snapshot_restore/public/application/lib/index.ts +++ b/x-pack/plugins/snapshot_restore/public/application/lib/index.ts @@ -6,3 +6,12 @@ */ export { useDecodedParams } from './use_decoded_params'; + +export { + SortField, + SortDirection, + SnapshotListParams, + getListParams, + getQueryFromListParams, + DEFAULT_SNAPSHOT_LIST_PARAMS, +} from './snapshot_list_params'; diff --git a/x-pack/plugins/snapshot_restore/public/application/lib/snapshot_list_params.ts b/x-pack/plugins/snapshot_restore/public/application/lib/snapshot_list_params.ts new file mode 100644 index 0000000000000..b75a3e01fb617 --- /dev/null +++ b/x-pack/plugins/snapshot_restore/public/application/lib/snapshot_list_params.ts @@ -0,0 +1,88 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Direction, Query } from '@elastic/eui'; + +export type SortField = + | 'snapshot' + | 'repository' + | 'indices' + | 'startTimeInMillis' + | 'durationInMillis' + | 'shards.total' + | 'shards.failed'; + +export type SortDirection = Direction; + +interface SnapshotTableParams { + sortField: SortField; + sortDirection: SortDirection; + pageIndex: number; + pageSize: number; +} +interface SnapshotSearchParams { + searchField?: string; + searchValue?: string; + searchMatch?: string; + searchOperator?: string; +} +export type SnapshotListParams = SnapshotTableParams & SnapshotSearchParams; + +// By default, we'll display the most recent snapshots at the top of the table (no query). +export const DEFAULT_SNAPSHOT_LIST_PARAMS: SnapshotListParams = { + sortField: 'startTimeInMillis', + sortDirection: 'desc', + pageIndex: 0, + pageSize: 20, +}; + +const resetSearchOptions = (listParams: SnapshotListParams): SnapshotListParams => ({ + ...listParams, + searchField: undefined, + searchValue: undefined, + searchMatch: undefined, + searchOperator: undefined, +}); + +// to init the query for repository and policyName search passed via url +export const getQueryFromListParams = (listParams: SnapshotListParams): Query => { + const { searchField, searchValue } = listParams; + if (!searchField || !searchValue) { + return Query.MATCH_ALL; + } + return Query.parse(`${searchField}=${searchValue}`); +}; + +export const getListParams = (listParams: SnapshotListParams, query: Query): SnapshotListParams => { + if (!query) { + return resetSearchOptions(listParams); + } + const clause = query.ast.clauses[0]; + if (!clause) { + return resetSearchOptions(listParams); + } + // term queries (free word search) converts to snapshot name search + if (clause.type === 'term') { + return { + ...listParams, + searchField: 'snapshot', + searchValue: String(clause.value), + searchMatch: clause.match, + searchOperator: 'eq', + }; + } + if (clause.type === 'field') { + return { + ...listParams, + searchField: clause.field, + searchValue: String(clause.value), + searchMatch: clause.match, + searchOperator: clause.operator, + }; + } + return resetSearchOptions(listParams); +}; diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/snapshot_table/index.ts b/x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/components/index.ts similarity index 55% rename from x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/snapshot_table/index.ts rename to x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/components/index.ts index 7ea85f4150900..8c69ca0297e3e 100644 --- a/x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/snapshot_table/index.ts +++ b/x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/components/index.ts @@ -6,3 +6,7 @@ */ export { SnapshotTable } from './snapshot_table'; +export { RepositoryError } from './repository_error'; +export { RepositoryEmptyPrompt } from './repository_empty_prompt'; +export { SnapshotEmptyPrompt } from './snapshot_empty_prompt'; +export { SnapshotSearchBar } from './snapshot_search_bar'; diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/components/repository_empty_prompt.tsx b/x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/components/repository_empty_prompt.tsx new file mode 100644 index 0000000000000..4c5e050ea489c --- /dev/null +++ b/x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/components/repository_empty_prompt.tsx @@ -0,0 +1,62 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { useHistory } from 'react-router-dom'; +import { EuiButton, EuiEmptyPrompt, EuiPageContent } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { reactRouterNavigate } from '../../../../../shared_imports'; +import { linkToAddRepository } from '../../../../services/navigation'; + +export const RepositoryEmptyPrompt: React.FunctionComponent = () => { + const history = useHistory(); + return ( + + + + + } + body={ + <> +

+ +

+

+ + + +

+ + } + data-test-subj="emptyPrompt" + /> +
+ ); +}; diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/components/repository_error.tsx b/x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/components/repository_error.tsx new file mode 100644 index 0000000000000..d3902770333cc --- /dev/null +++ b/x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/components/repository_error.tsx @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { useHistory } from 'react-router-dom'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiEmptyPrompt, EuiLink, EuiPageContent } from '@elastic/eui'; +import { reactRouterNavigate } from '../../../../../shared_imports'; +import { linkToRepositories } from '../../../../services/navigation'; + +export const RepositoryError: React.FunctionComponent = () => { + const history = useHistory(); + return ( + + + + + } + body={ +

+ + + + ), + }} + /> +

+ } + /> +
+ ); +}; diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/components/snapshot_empty_prompt.tsx b/x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/components/snapshot_empty_prompt.tsx new file mode 100644 index 0000000000000..2cfc1d5ebefc5 --- /dev/null +++ b/x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/components/snapshot_empty_prompt.tsx @@ -0,0 +1,123 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { Fragment } from 'react'; +import { useHistory } from 'react-router-dom'; +import { EuiButton, EuiEmptyPrompt, EuiIcon, EuiLink, EuiPageContent } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { APP_SLM_CLUSTER_PRIVILEGES } from '../../../../../../common'; +import { reactRouterNavigate, WithPrivileges } from '../../../../../shared_imports'; +import { linkToAddPolicy, linkToPolicies } from '../../../../services/navigation'; +import { useCore } from '../../../../app_context'; + +export const SnapshotEmptyPrompt: React.FunctionComponent<{ policiesCount: number }> = ({ + policiesCount, +}) => { + const { docLinks } = useCore(); + const history = useHistory(); + return ( + + + + + } + body={ + `cluster.${name}`)}> + {({ hasPrivileges }) => + hasPrivileges ? ( + +

+ + + + ), + }} + /> +

+

+ {policiesCount === 0 ? ( + + + + ) : ( + + + + )} +

+
+ ) : ( + +

+ +

+

+ + {' '} + + +

+
+ ) + } +
+ } + data-test-subj="emptyPrompt" + /> +
+ ); +}; diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/components/snapshot_search_bar.tsx b/x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/components/snapshot_search_bar.tsx new file mode 100644 index 0000000000000..b3e2c24e396f0 --- /dev/null +++ b/x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/components/snapshot_search_bar.tsx @@ -0,0 +1,178 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useState } from 'react'; +import useDebounce from 'react-use/lib/useDebounce'; + +import { FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; +import { SearchFilterConfig } from '@elastic/eui/src/components/search_bar/search_filters'; +import { SchemaType } from '@elastic/eui/src/components/search_bar/search_box'; +import { EuiSearchBarOnChangeArgs } from '@elastic/eui/src/components/search_bar/search_bar'; +import { EuiButton, EuiCallOut, EuiSearchBar, EuiSpacer, Query } from '@elastic/eui'; +import { SnapshotDeleteProvider } from '../../../../components'; +import { SnapshotDetails } from '../../../../../../common/types'; +import { getQueryFromListParams, SnapshotListParams, getListParams } from '../../../../lib'; + +const SEARCH_DEBOUNCE_VALUE_MS = 200; + +const onlyOneClauseMessage = i18n.translate( + 'xpack.snapshotRestore.snapshotList.searchBar.onlyOneClauseMessage', + { + defaultMessage: 'You can only use one clause in the search bar', + } +); +// for now limit the search bar to snapshot, repository and policyName queries +const searchSchema: SchemaType = { + strict: true, + fields: { + snapshot: { + type: 'string', + }, + repository: { + type: 'string', + }, + policyName: { + type: 'string', + }, + }, +}; + +interface Props { + listParams: SnapshotListParams; + setListParams: (listParams: SnapshotListParams) => void; + reload: () => void; + selectedItems: SnapshotDetails[]; + onSnapshotDeleted: (snapshotsDeleted: Array<{ snapshot: string; repository: string }>) => void; + repositories: string[]; +} + +export const SnapshotSearchBar: React.FunctionComponent = ({ + listParams, + setListParams, + reload, + selectedItems, + onSnapshotDeleted, + repositories, +}) => { + const [cachedListParams, setCachedListParams] = useState(listParams); + // send the request after the user has stopped typing + useDebounce( + () => { + setListParams(cachedListParams); + }, + SEARCH_DEBOUNCE_VALUE_MS, + [cachedListParams] + ); + + const deleteButton = selectedItems.length ? ( + + {( + deleteSnapshotPrompt: ( + ids: Array<{ snapshot: string; repository: string }>, + onSuccess?: (snapshotsDeleted: Array<{ snapshot: string; repository: string }>) => void + ) => void + ) => { + return ( + + deleteSnapshotPrompt( + selectedItems.map(({ snapshot, repository }) => ({ snapshot, repository })), + onSnapshotDeleted + ) + } + color="danger" + data-test-subj="srSnapshotListBulkDeleteActionButton" + > + + + ); + }} + + ) : ( + [] + ); + const searchFilters: SearchFilterConfig[] = [ + { + type: 'field_value_selection' as const, + field: 'repository', + name: i18n.translate('xpack.snapshotRestore.snapshotList.table.repositoryFilterLabel', { + defaultMessage: 'Repository', + }), + operator: 'exact', + multiSelect: false, + options: repositories.map((repository) => ({ + value: repository, + view: repository, + })), + }, + ]; + const reloadButton = ( + + + + ); + + const [query, setQuery] = useState(getQueryFromListParams(listParams)); + const [error, setError] = useState(null); + + const onSearchBarChange = (args: EuiSearchBarOnChangeArgs) => { + const { query: changedQuery, error: queryError } = args; + if (queryError) { + setError(queryError); + } else if (changedQuery) { + setError(null); + setQuery(changedQuery); + if (changedQuery.ast.clauses.length > 1) { + setError({ name: onlyOneClauseMessage, message: onlyOneClauseMessage }); + } else { + setCachedListParams(getListParams(listParams, changedQuery)); + } + } + }; + + return ( + <> + + + {error ? ( + <> + + } + /> + + + ) : null} + + ); +}; diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/snapshot_table/snapshot_table.tsx b/x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/components/snapshot_table.tsx similarity index 71% rename from x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/snapshot_table/snapshot_table.tsx rename to x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/components/snapshot_table.tsx index 47f8d9b833e40..5db702fcbd963 100644 --- a/x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/snapshot_table/snapshot_table.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/components/snapshot_table.tsx @@ -7,34 +7,28 @@ import React, { useState } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiTableSortingType } from '@elastic/eui/src/components/basic_table/table_types'; + import { - EuiButton, - EuiInMemoryTable, EuiLink, - Query, EuiLoadingSpinner, EuiToolTip, EuiButtonIcon, + Criteria, + EuiBasicTable, } from '@elastic/eui'; - import { SnapshotDetails } from '../../../../../../common/types'; -import { UseRequestResponse } from '../../../../../shared_imports'; +import { UseRequestResponse, reactRouterNavigate } from '../../../../../shared_imports'; import { SNAPSHOT_STATE, UIM_SNAPSHOT_SHOW_DETAILS_CLICK } from '../../../../constants'; import { useServices } from '../../../../app_context'; -import { linkToRepository, linkToRestoreSnapshot } from '../../../../services/navigation'; +import { + linkToRepository, + linkToRestoreSnapshot, + linkToSnapshot as openSnapshotDetailsUrl, +} from '../../../../services/navigation'; +import { SnapshotListParams, SortDirection, SortField } from '../../../../lib'; import { DataPlaceholder, FormattedDateTime, SnapshotDeleteProvider } from '../../../../components'; - -import { reactRouterNavigate } from '../../../../../../../../../src/plugins/kibana_react/public'; - -interface Props { - snapshots: SnapshotDetails[]; - repositories: string[]; - reload: UseRequestResponse['resendRequest']; - openSnapshotDetailsUrl: (repositoryName: string, snapshotId: string) => string; - repositoryFilter?: string; - policyFilter?: string; - onSnapshotDeleted: (snapshotsDeleted: Array<{ snapshot: string; repository: string }>) => void; -} +import { SnapshotSearchBar } from './snapshot_search_bar'; const getLastSuccessfulManagedSnapshot = ( snapshots: SnapshotDetails[] @@ -51,15 +45,28 @@ const getLastSuccessfulManagedSnapshot = ( return successfulSnapshots[0]; }; -export const SnapshotTable: React.FunctionComponent = ({ - snapshots, - repositories, - reload, - openSnapshotDetailsUrl, - onSnapshotDeleted, - repositoryFilter, - policyFilter, -}) => { +interface Props { + snapshots: SnapshotDetails[]; + repositories: string[]; + reload: UseRequestResponse['resendRequest']; + onSnapshotDeleted: (snapshotsDeleted: Array<{ snapshot: string; repository: string }>) => void; + listParams: SnapshotListParams; + setListParams: (listParams: SnapshotListParams) => void; + totalItemCount: number; + isLoading: boolean; +} + +export const SnapshotTable: React.FunctionComponent = (props: Props) => { + const { + snapshots, + repositories, + reload, + onSnapshotDeleted, + listParams, + setListParams, + totalItemCount, + isLoading, + } = props; const { i18n, uiMetricService, history } = useServices(); const [selectedItems, setSelectedItems] = useState([]); @@ -71,7 +78,7 @@ export const SnapshotTable: React.FunctionComponent = ({ name: i18n.translate('xpack.snapshotRestore.snapshotList.table.snapshotColumnTitle', { defaultMessage: 'Snapshot', }), - truncateText: true, + truncateText: false, sortable: true, render: (snapshotId: string, snapshot: SnapshotDetails) => ( = ({ name: i18n.translate('xpack.snapshotRestore.snapshotList.table.repositoryColumnTitle', { defaultMessage: 'Repository', }), - truncateText: true, + truncateText: false, sortable: true, render: (repositoryName: string) => ( = ({ name: i18n.translate('xpack.snapshotRestore.snapshotList.table.startTimeColumnTitle', { defaultMessage: 'Date created', }), - truncateText: true, + truncateText: false, sortable: true, render: (startTimeInMillis: number) => ( @@ -263,30 +270,20 @@ export const SnapshotTable: React.FunctionComponent = ({ }, ]; - // By default, we'll display the most recent snapshots at the top of the table. - const sorting = { + const sorting: EuiTableSortingType = { sort: { - field: 'startTimeInMillis', - direction: 'desc' as const, + field: listParams.sortField as keyof SnapshotDetails, + direction: listParams.sortDirection, }, }; const pagination = { - initialPageSize: 20, + pageIndex: listParams.pageIndex, + pageSize: listParams.pageSize, + totalItemCount, pageSizeOptions: [10, 20, 50], }; - const searchSchema = { - fields: { - repository: { - type: 'string', - }, - policyName: { - type: 'string', - }, - }, - }; - const selection = { onSelectionChange: (newSelectedItems: SnapshotDetails[]) => setSelectedItems(newSelectedItems), selectable: ({ snapshot }: SnapshotDetails) => @@ -306,103 +303,44 @@ export const SnapshotTable: React.FunctionComponent = ({ }, }; - const search = { - toolsLeft: selectedItems.length ? ( - - {( - deleteSnapshotPrompt: ( - ids: Array<{ snapshot: string; repository: string }>, - onSuccess?: (snapshotsDeleted: Array<{ snapshot: string; repository: string }>) => void - ) => void - ) => { - return ( - - deleteSnapshotPrompt( - selectedItems.map(({ snapshot, repository }) => ({ snapshot, repository })), - onSnapshotDeleted - ) - } - color="danger" - data-test-subj="srSnapshotListBulkDeleteActionButton" - > - - - ); - }} - - ) : undefined, - toolsRight: ( - - - - ), - box: { - incremental: true, - schema: searchSchema, - }, - filters: [ - { - type: 'field_value_selection' as const, - field: 'repository', - name: i18n.translate('xpack.snapshotRestore.snapshotList.table.repositoryFilterLabel', { - defaultMessage: 'Repository', - }), - multiSelect: false, - options: repositories.map((repository) => ({ - value: repository, - view: repository, - })), - }, - ], - defaultQuery: policyFilter - ? Query.parse(`policyName="${policyFilter}"`, { - schema: { - ...searchSchema, - strict: true, - }, - }) - : repositoryFilter - ? Query.parse(`repository="${repositoryFilter}"`, { - schema: { - ...searchSchema, - strict: true, - }, - }) - : '', - }; - return ( - ({ - 'data-test-subj': 'row', - })} - cellProps={() => ({ - 'data-test-subj': 'cell', - })} - data-test-subj="snapshotTable" - /> + <> + + ) => { + const { page: { index, size } = {}, sort: { field, direction } = {} } = criteria; + + setListParams({ + ...listParams, + sortField: (field as SortField) ?? listParams.sortField, + sortDirection: (direction as SortDirection) ?? listParams.sortDirection, + pageIndex: index ?? listParams.pageIndex, + pageSize: size ?? listParams.pageSize, + }); + }} + loading={isLoading} + isSelectable={true} + selection={selection} + pagination={pagination} + rowProps={() => ({ + 'data-test-subj': 'row', + })} + cellProps={() => ({ + 'data-test-subj': 'cell', + })} + data-test-subj="snapshotTable" + /> + ); }; diff --git a/x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/snapshot_list.tsx b/x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/snapshot_list.tsx index 92c03d1be936d..da7ec42f746a3 100644 --- a/x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/snapshot_list.tsx +++ b/x-pack/plugins/snapshot_restore/public/application/sections/home/snapshot_list/snapshot_list.tsx @@ -5,37 +5,26 @@ * 2.0. */ -import React, { Fragment, useState, useEffect } from 'react'; +import React, { useState, useEffect } from 'react'; import { parse } from 'query-string'; import { FormattedMessage } from '@kbn/i18n/react'; import { RouteComponentProps } from 'react-router-dom'; -import { - EuiPageContent, - EuiButton, - EuiCallOut, - EuiLink, - EuiEmptyPrompt, - EuiSpacer, - EuiIcon, -} from '@elastic/eui'; +import { EuiCallOut, EuiLink, EuiSpacer } from '@elastic/eui'; -import { APP_SLM_CLUSTER_PRIVILEGES, SNAPSHOT_LIST_MAX_SIZE } from '../../../../../common'; -import { WithPrivileges, PageLoading, PageError, Error } from '../../../../shared_imports'; +import { PageLoading, PageError, Error, reactRouterNavigate } from '../../../../shared_imports'; import { BASE_PATH, UIM_SNAPSHOT_LIST_LOAD } from '../../../constants'; import { useLoadSnapshots } from '../../../services/http'; -import { - linkToRepositories, - linkToAddRepository, - linkToPolicies, - linkToAddPolicy, - linkToSnapshot, -} from '../../../services/navigation'; -import { useCore, useServices } from '../../../app_context'; -import { useDecodedParams } from '../../../lib'; -import { SnapshotDetails } from './snapshot_details'; -import { SnapshotTable } from './snapshot_table'; +import { linkToRepositories } from '../../../services/navigation'; +import { useServices } from '../../../app_context'; +import { useDecodedParams, SnapshotListParams, DEFAULT_SNAPSHOT_LIST_PARAMS } from '../../../lib'; -import { reactRouterNavigate } from '../../../../../../../../src/plugins/kibana_react/public'; +import { SnapshotDetails } from './snapshot_details'; +import { + SnapshotTable, + RepositoryEmptyPrompt, + SnapshotEmptyPrompt, + RepositoryError, +} from './components'; interface MatchParams { repositoryName?: string; @@ -47,22 +36,22 @@ export const SnapshotList: React.FunctionComponent { const { repositoryName, snapshotId } = useDecodedParams(); + const [listParams, setListParams] = useState(DEFAULT_SNAPSHOT_LIST_PARAMS); const { error, + isInitialRequest, isLoading, - data: { snapshots = [], repositories = [], policies = [], errors = {} }, + data: { + snapshots = [], + repositories = [], + policies = [], + errors = {}, + total: totalSnapshotsCount, + }, resendRequest: reload, - } = useLoadSnapshots(); + } = useLoadSnapshots(listParams); - const { uiMetricService, i18n } = useServices(); - const { docLinks } = useCore(); - - const openSnapshotDetailsUrl = ( - repositoryNameToOpen: string, - snapshotIdToOpen: string - ): string => { - return linkToSnapshot(repositoryNameToOpen, snapshotIdToOpen); - }; + const { uiMetricService } = useServices(); const closeSnapshotDetails = () => { history.push(`${BASE_PATH}/snapshots`); @@ -86,22 +75,32 @@ export const SnapshotList: React.FunctionComponent(undefined); - const [filteredPolicy, setFilteredPolicy] = useState(undefined); useEffect(() => { if (search) { const parsedParams = parse(search.replace(/^\?/, ''), { sort: false }); const { repository, policy } = parsedParams; - if (policy && policy !== filteredPolicy) { - setFilteredPolicy(String(policy)); + if (policy) { + setListParams((prev: SnapshotListParams) => ({ + ...prev, + searchField: 'policyName', + searchValue: String(policy), + searchMatch: 'must', + searchOperator: 'exact', + })); history.replace(`${BASE_PATH}/snapshots`); - } else if (repository && repository !== filteredRepository) { - setFilteredRepository(String(repository)); + } else if (repository) { + setListParams((prev: SnapshotListParams) => ({ + ...prev, + searchField: 'repository', + searchValue: String(repository), + searchMatch: 'must', + searchOperator: 'exact', + })); history.replace(`${BASE_PATH}/snapshots`); } } - }, [filteredPolicy, filteredRepository, history, search]); + }, [listParams, history, search]); // Track component loaded useEffect(() => { @@ -110,7 +109,8 @@ export const SnapshotList: React.FunctionComponent @@ -134,190 +134,11 @@ export const SnapshotList: React.FunctionComponent ); } else if (Object.keys(errors).length && repositories.length === 0) { - content = ( - - - - - } - body={ -

- - - - ), - }} - /> -

- } - /> -
- ); + content = ; } else if (repositories.length === 0) { - content = ( - - - - - } - body={ - <> -

- -

-

- - - -

- - } - data-test-subj="emptyPrompt" - /> -
- ); - } else if (snapshots.length === 0) { - content = ( - - - - - } - body={ - `cluster.${name}`)} - > - {({ hasPrivileges }) => - hasPrivileges ? ( - -

- - - - ), - }} - /> -

-

- {policies.length === 0 ? ( - - - - ) : ( - - - - )} -

-
- ) : ( - -

- -

-

- - {' '} - - -

-
- ) - } -
- } - data-test-subj="emptyPrompt" - /> -
- ); + content = ; + } else if (totalSnapshotsCount === 0 && !listParams.searchField && !isLoading) { + content = ; } else { const repositoryErrorsWarning = Object.keys(errors).length ? ( <> @@ -351,53 +172,19 @@ export const SnapshotList: React.FunctionComponent ) : null; - const maxSnapshotsWarning = snapshots.length === SNAPSHOT_LIST_MAX_SIZE && ( - <> - - - -
- ), - }} - /> -
- - - ); - content = (
{repositoryErrorsWarning} - {maxSnapshotsWarning} -
); diff --git a/x-pack/plugins/snapshot_restore/public/application/services/http/snapshot_requests.ts b/x-pack/plugins/snapshot_restore/public/application/services/http/snapshot_requests.ts index 3d64dc96958de..c02d0f053f783 100644 --- a/x-pack/plugins/snapshot_restore/public/application/services/http/snapshot_requests.ts +++ b/x-pack/plugins/snapshot_restore/public/application/services/http/snapshot_requests.ts @@ -5,8 +5,10 @@ * 2.0. */ -import { API_BASE_PATH } from '../../../../common/constants'; +import { HttpFetchQuery } from 'kibana/public'; +import { API_BASE_PATH } from '../../../../common'; import { UIM_SNAPSHOT_DELETE, UIM_SNAPSHOT_DELETE_MANY } from '../../constants'; +import { SnapshotListParams } from '../../lib'; import { UiMetricService } from '../ui_metric'; import { sendRequest, useRequest } from './use_request'; @@ -18,11 +20,12 @@ export const setUiMetricServiceSnapshot = (_uiMetricService: UiMetricService) => }; // End hack -export const useLoadSnapshots = () => +export const useLoadSnapshots = (query: SnapshotListParams) => useRequest({ path: `${API_BASE_PATH}snapshots`, method: 'get', initialData: [], + query: query as unknown as HttpFetchQuery, }); export const useLoadSnapshot = (repositoryName: string, snapshotId: string) => diff --git a/x-pack/plugins/snapshot_restore/public/shared_imports.ts b/x-pack/plugins/snapshot_restore/public/shared_imports.ts index d1b9f37703c0c..a3cda90d26f2a 100644 --- a/x-pack/plugins/snapshot_restore/public/shared_imports.ts +++ b/x-pack/plugins/snapshot_restore/public/shared_imports.ts @@ -26,3 +26,5 @@ export { } from '../../../../src/plugins/es_ui_shared/public'; export { APP_WRAPPER_CLASS } from '../../../../src/core/public'; + +export { reactRouterNavigate } from '../../../../src/plugins/kibana_react/public'; diff --git a/x-pack/plugins/snapshot_restore/server/lib/get_snapshot_search_wildcard.test.ts b/x-pack/plugins/snapshot_restore/server/lib/get_snapshot_search_wildcard.test.ts new file mode 100644 index 0000000000000..d3e5c604d22ad --- /dev/null +++ b/x-pack/plugins/snapshot_restore/server/lib/get_snapshot_search_wildcard.test.ts @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getSnapshotSearchWildcard } from './get_snapshot_search_wildcard'; + +describe('getSnapshotSearchWildcard', () => { + it('exact match search converts to a wildcard without *', () => { + const searchParams = { + field: 'snapshot', + value: 'testSearch', + operator: 'exact', + match: 'must', + }; + const wildcard = getSnapshotSearchWildcard(searchParams); + expect(wildcard).toEqual('testSearch'); + }); + + it('partial match search converts to a wildcard with *', () => { + const searchParams = { field: 'snapshot', value: 'testSearch', operator: 'eq', match: 'must' }; + const wildcard = getSnapshotSearchWildcard(searchParams); + expect(wildcard).toEqual('*testSearch*'); + }); + + it('excluding search converts to "all, except" wildcard (exact match)', () => { + const searchParams = { + field: 'snapshot', + value: 'testSearch', + operator: 'exact', + match: 'must_not', + }; + const wildcard = getSnapshotSearchWildcard(searchParams); + expect(wildcard).toEqual('*,-testSearch'); + }); + + it('excluding search converts to "all, except" wildcard (partial match)', () => { + const searchParams = { + field: 'snapshot', + value: 'testSearch', + operator: 'eq', + match: 'must_not', + }; + const wildcard = getSnapshotSearchWildcard(searchParams); + expect(wildcard).toEqual('*,-*testSearch*'); + }); + + it('excluding search for policy name converts to "all,_none, except" wildcard', () => { + const searchParams = { + field: 'policyName', + value: 'testSearch', + operator: 'exact', + match: 'must_not', + }; + const wildcard = getSnapshotSearchWildcard(searchParams); + expect(wildcard).toEqual('*,_none,-testSearch'); + }); +}); diff --git a/x-pack/plugins/snapshot_restore/server/lib/get_snapshot_search_wildcard.ts b/x-pack/plugins/snapshot_restore/server/lib/get_snapshot_search_wildcard.ts new file mode 100644 index 0000000000000..df8926d785712 --- /dev/null +++ b/x-pack/plugins/snapshot_restore/server/lib/get_snapshot_search_wildcard.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +interface SearchParams { + field: string; + value: string; + match?: string; + operator?: string; +} + +export const getSnapshotSearchWildcard = ({ + field, + value, + match, + operator, +}: SearchParams): string => { + // if the operator is NOT for exact match, convert to *value* wildcard that matches any substring + value = operator === 'exact' ? value : `*${value}*`; + + // ES API new "-"("except") wildcard removes matching items from a list of already selected items + // To find all items not containing the search value, use "*,-{searchValue}" + // When searching for policy name, also add "_none" to find snapshots without a policy as well + const excludingWildcard = field === 'policyName' ? `*,_none,-${value}` : `*,-${value}`; + + return match === 'must_not' ? excludingWildcard : value; +}; diff --git a/x-pack/plugins/snapshot_restore/server/routes/api/snapshots.test.ts b/x-pack/plugins/snapshot_restore/server/routes/api/snapshots.test.ts index f71c5ec9ffc08..4ecd34a43adb9 100644 --- a/x-pack/plugins/snapshot_restore/server/routes/api/snapshots.test.ts +++ b/x-pack/plugins/snapshot_restore/server/routes/api/snapshots.test.ts @@ -51,6 +51,10 @@ describe('[Snapshot and Restore API Routes] Snapshots', () => { const mockRequest: RequestMock = { method: 'get', path: addBasePath('snapshots'), + query: { + sortField: 'startTimeInMillis', + sortDirection: 'desc', + }, }; const mockSnapshotGetManagedRepositoryEsResponse = { diff --git a/x-pack/plugins/snapshot_restore/server/routes/api/snapshots.ts b/x-pack/plugins/snapshot_restore/server/routes/api/snapshots.ts index 6838ae2700f3a..4de0c3011fed5 100644 --- a/x-pack/plugins/snapshot_restore/server/routes/api/snapshots.ts +++ b/x-pack/plugins/snapshot_restore/server/routes/api/snapshots.ts @@ -7,11 +7,36 @@ import { schema, TypeOf } from '@kbn/config-schema'; import type { SnapshotDetailsEs } from '../../../common/types'; -import { SNAPSHOT_LIST_MAX_SIZE } from '../../../common/constants'; import { deserializeSnapshotDetails } from '../../../common/lib'; import type { RouteDependencies } from '../../types'; import { getManagedRepositoryName } from '../../lib'; import { addBasePath } from '../helpers'; +import { snapshotListSchema } from './validate_schemas'; +import { getSnapshotSearchWildcard } from '../../lib/get_snapshot_search_wildcard'; + +const sortFieldToESParams = { + snapshot: 'name', + repository: 'repository', + indices: 'index_count', + startTimeInMillis: 'start_time', + durationInMillis: 'duration', + 'shards.total': 'shard_count', + 'shards.failed': 'failed_shard_count', +}; + +const isSearchingForNonExistentRepository = ( + repositories: string[], + value: string, + match?: string, + operator?: string +): boolean => { + // only check if searching for an exact match (repository=test) + if (match === 'must' && operator === 'exact') { + return !(repositories || []).includes(value); + } + // otherwise we will use a wildcard, so allow the request + return false; +}; export function registerSnapshotsRoutes({ router, @@ -20,9 +45,18 @@ export function registerSnapshotsRoutes({ }: RouteDependencies) { // GET all snapshots router.get( - { path: addBasePath('snapshots'), validate: false }, + { path: addBasePath('snapshots'), validate: { query: snapshotListSchema } }, license.guardApiRoute(async (ctx, req, res) => { const { client: clusterClient } = ctx.core.elasticsearch; + const sortField = + sortFieldToESParams[(req.query as TypeOf).sortField]; + const sortDirection = (req.query as TypeOf).sortDirection; + const pageIndex = (req.query as TypeOf).pageIndex; + const pageSize = (req.query as TypeOf).pageSize; + const searchField = (req.query as TypeOf).searchField; + const searchValue = (req.query as TypeOf).searchValue; + const searchMatch = (req.query as TypeOf).searchMatch; + const searchOperator = (req.query as TypeOf).searchOperator; const managedRepository = await getManagedRepositoryName(clusterClient.asCurrentUser); @@ -55,18 +89,60 @@ export function registerSnapshotsRoutes({ return handleEsError({ error: e, response: res }); } + // if the search is for a repository name with exact match (repository=test) + // and that repository doesn't exist, ES request throws an error + // that is why we return an empty snapshots array instead of sending an ES request + if ( + searchField === 'repository' && + isSearchingForNonExistentRepository(repositories, searchValue!, searchMatch, searchOperator) + ) { + return res.ok({ + body: { + snapshots: [], + policies, + repositories, + errors: [], + total: 0, + }, + }); + } try { // If any of these repositories 504 they will cost the request significant time. const { body: fetchedSnapshots } = await clusterClient.asCurrentUser.snapshot.get({ - repository: '_all', - snapshot: '_all', + repository: + searchField === 'repository' + ? getSnapshotSearchWildcard({ + field: searchField, + value: searchValue!, + match: searchMatch, + operator: searchOperator, + }) + : '_all', ignore_unavailable: true, // Allow request to succeed even if some snapshots are unavailable. - // @ts-expect-error @elastic/elasticsearch "desc" is a new param - order: 'desc', - // TODO We are temporarily hard-coding the maximum number of snapshots returned - // in order to prevent an unusable UI for users with large number of snapshots - // In the near future, this will be resolved with server-side pagination - size: SNAPSHOT_LIST_MAX_SIZE, + snapshot: + searchField === 'snapshot' + ? getSnapshotSearchWildcard({ + field: searchField, + value: searchValue!, + match: searchMatch, + operator: searchOperator, + }) + : '_all', + // @ts-expect-error @elastic/elasticsearch new API params + // https://github.com/elastic/elasticsearch-specification/issues/845 + slm_policy_filter: + searchField === 'policyName' + ? getSnapshotSearchWildcard({ + field: searchField, + value: searchValue!, + match: searchMatch, + operator: searchOperator, + }) + : '*,_none', + order: sortDirection, + sort: sortField, + size: pageSize, + offset: pageIndex * pageSize, }); // Decorate each snapshot with the repository with which it's associated. @@ -79,8 +155,10 @@ export function registerSnapshotsRoutes({ snapshots: snapshots || [], policies, repositories, - // @ts-expect-error @elastic/elasticsearch "failures" is a new field in the response + // @ts-expect-error @elastic/elasticsearch https://github.com/elastic/elasticsearch-specification/issues/845 errors: fetchedSnapshots?.failures, + // @ts-expect-error @elastic/elasticsearch "total" is a new field in the response + total: fetchedSnapshots?.total, }, }); } catch (e) { @@ -170,7 +248,7 @@ export function registerSnapshotsRoutes({ const snapshots = req.body; try { - // We intentially perform deletion requests sequentially (blocking) instead of in parallel (non-blocking) + // We intentionally perform deletion requests sequentially (blocking) instead of in parallel (non-blocking) // because there can only be one snapshot deletion task performed at a time (ES restriction). for (let i = 0; i < snapshots.length; i++) { const { snapshot, repository } = snapshots[i]; diff --git a/x-pack/plugins/snapshot_restore/server/routes/api/validate_schemas.ts b/x-pack/plugins/snapshot_restore/server/routes/api/validate_schemas.ts index af31466c2cefe..e93ee2b3d78ca 100644 --- a/x-pack/plugins/snapshot_restore/server/routes/api/validate_schemas.ts +++ b/x-pack/plugins/snapshot_restore/server/routes/api/validate_schemas.ts @@ -26,6 +26,31 @@ const snapshotRetentionSchema = schema.object({ minCount: schema.maybe(schema.oneOf([schema.number(), schema.literal('')])), }); +export const snapshotListSchema = schema.object({ + sortField: schema.oneOf([ + schema.literal('snapshot'), + schema.literal('repository'), + schema.literal('indices'), + schema.literal('durationInMillis'), + schema.literal('startTimeInMillis'), + schema.literal('shards.total'), + schema.literal('shards.failed'), + ]), + sortDirection: schema.oneOf([schema.literal('desc'), schema.literal('asc')]), + pageIndex: schema.number(), + pageSize: schema.number(), + searchField: schema.maybe( + schema.oneOf([ + schema.literal('snapshot'), + schema.literal('repository'), + schema.literal('policyName'), + ]) + ), + searchValue: schema.maybe(schema.string()), + searchMatch: schema.maybe(schema.oneOf([schema.literal('must'), schema.literal('must_not')])), + searchOperator: schema.maybe(schema.oneOf([schema.literal('eq'), schema.literal('exact')])), +}); + export const policySchema = schema.object({ name: schema.string(), snapshotName: schema.string(), diff --git a/x-pack/plugins/snapshot_restore/server/routes/helpers.ts b/x-pack/plugins/snapshot_restore/server/routes/helpers.ts index 1f49d2f3cabfb..e73db4d992ff2 100644 --- a/x-pack/plugins/snapshot_restore/server/routes/helpers.ts +++ b/x-pack/plugins/snapshot_restore/server/routes/helpers.ts @@ -5,6 +5,6 @@ * 2.0. */ -import { API_BASE_PATH } from '../../common/constants'; +import { API_BASE_PATH } from '../../common'; export const addBasePath = (uri: string): string => API_BASE_PATH + uri; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index c941cbd9ddf80..3df5e4ee6c48a 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -23900,9 +23900,6 @@ "xpack.snapshotRestore.snapshotList.table.snapshotColumnTitle": "スナップショット", "xpack.snapshotRestore.snapshotList.table.startTimeColumnTitle": "日付が作成されました", "xpack.snapshotRestore.snapshots.breadcrumbTitle": "スナップショット", - "xpack.snapshotRestore.snapshotsList.maxSnapshotsDisplayedDescription": "表示可能なスナップショットの最大数に達しました。スナップショットをすべて表示するには、{docLink}を使用してください。", - "xpack.snapshotRestore.snapshotsList.maxSnapshotsDisplayedDocLinkText": "Elasticsearch API", - "xpack.snapshotRestore.snapshotsList.maxSnapshotsDisplayedTitle": "スナップショットの一覧を表示できません。", "xpack.snapshotRestore.snapshotState.completeLabel": "スナップショット完了", "xpack.snapshotRestore.snapshotState.failedLabel": "スナップショット失敗", "xpack.snapshotRestore.snapshotState.incompatibleLabel": "互換性のないバージョン", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index e9e9f02c8fe99..d9af3edb8101d 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -24302,9 +24302,6 @@ "xpack.snapshotRestore.snapshotList.table.snapshotColumnTitle": "快照", "xpack.snapshotRestore.snapshotList.table.startTimeColumnTitle": "创建日期", "xpack.snapshotRestore.snapshots.breadcrumbTitle": "快照", - "xpack.snapshotRestore.snapshotsList.maxSnapshotsDisplayedDescription": "已达到最大可查看快照数目。要查看您的所有快照,请使用{docLink}。", - "xpack.snapshotRestore.snapshotsList.maxSnapshotsDisplayedDocLinkText": "Elasticsearch API", - "xpack.snapshotRestore.snapshotsList.maxSnapshotsDisplayedTitle": "无法显示快照的完整列表", "xpack.snapshotRestore.snapshotState.completeLabel": "快照完成", "xpack.snapshotRestore.snapshotState.failedLabel": "快照失败", "xpack.snapshotRestore.snapshotState.incompatibleLabel": "不兼容版本", diff --git a/x-pack/test/api_integration/apis/management/snapshot_restore/index.ts b/x-pack/test/api_integration/apis/management/snapshot_restore/index.ts index 4d39ff1494f89..db5dbc9735e66 100644 --- a/x-pack/test/api_integration/apis/management/snapshot_restore/index.ts +++ b/x-pack/test/api_integration/apis/management/snapshot_restore/index.ts @@ -9,6 +9,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('Snapshot and Restore', () => { - loadTestFile(require.resolve('./snapshot_restore')); + loadTestFile(require.resolve('./policies')); + loadTestFile(require.resolve('./snapshots')); }); } diff --git a/x-pack/test/api_integration/apis/management/snapshot_restore/lib/elasticsearch.ts b/x-pack/test/api_integration/apis/management/snapshot_restore/lib/elasticsearch.ts index 9b4d39a3b10b3..a59c90fe29132 100644 --- a/x-pack/test/api_integration/apis/management/snapshot_restore/lib/elasticsearch.ts +++ b/x-pack/test/api_integration/apis/management/snapshot_restore/lib/elasticsearch.ts @@ -7,9 +7,10 @@ import { FtrProviderContext } from '../../../../ftr_provider_context'; -interface SlmPolicy { +export interface SlmPolicy { + policyName: string; + // snapshot name name: string; - snapshotName: string; schedule: string; repository: string; isManagedPolicy: boolean; @@ -29,23 +30,22 @@ interface SlmPolicy { } /** - * Helpers to create and delete SLM policies on the Elasticsearch instance + * Helpers to create and delete SLM policies, repositories and snapshots on the Elasticsearch instance * during our tests. - * @param {ElasticsearchClient} es The Elasticsearch client instance */ export const registerEsHelpers = (getService: FtrProviderContext['getService']) => { let policiesCreated: string[] = []; const es = getService('es'); - const createRepository = (repoName: string) => { + const createRepository = (repoName: string, repoPath?: string) => { return es.snapshot .createRepository({ repository: repoName, body: { type: 'fs', settings: { - location: '/tmp/', + location: repoPath ?? '/tmp/repo', }, }, verify: false, @@ -55,12 +55,12 @@ export const registerEsHelpers = (getService: FtrProviderContext['getService']) const createPolicy = (policy: SlmPolicy, cachePolicy?: boolean) => { if (cachePolicy) { - policiesCreated.push(policy.name); + policiesCreated.push(policy.policyName); } return es.slm .putLifecycle({ - policy_id: policy.name, + policy_id: policy.policyName, // TODO: bring {@link SlmPolicy} in line with {@link PutSnapshotLifecycleRequest['body']} // @ts-expect-error body: policy, @@ -90,11 +90,34 @@ export const registerEsHelpers = (getService: FtrProviderContext['getService']) console.log(`[Cleanup error] Error deleting ES resources: ${err.message}`); }); + const executePolicy = (policyName: string) => { + return es.slm.executeLifecycle({ policy_id: policyName }).then(({ body }) => body); + }; + + const createSnapshot = (snapshotName: string, repositoryName: string) => { + return es.snapshot + .create({ snapshot: snapshotName, repository: repositoryName }) + .then(({ body }) => body); + }; + + const deleteSnapshots = (repositoryName: string) => { + es.snapshot + .delete({ repository: repositoryName, snapshot: '*' }) + .then(() => {}) + .catch((err) => { + // eslint-disable-next-line no-console + console.log(`[Cleanup error] Error deleting snapshots: ${err.message}`); + }); + }; + return { createRepository, createPolicy, deletePolicy, cleanupPolicies, getPolicy, + executePolicy, + createSnapshot, + deleteSnapshots, }; }; diff --git a/x-pack/test/api_integration/apis/management/snapshot_restore/lib/index.ts b/x-pack/test/api_integration/apis/management/snapshot_restore/lib/index.ts index 27a4d9c59cff0..a9721c5856598 100644 --- a/x-pack/test/api_integration/apis/management/snapshot_restore/lib/index.ts +++ b/x-pack/test/api_integration/apis/management/snapshot_restore/lib/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export { registerEsHelpers } from './elasticsearch'; +export { registerEsHelpers, SlmPolicy } from './elasticsearch'; diff --git a/x-pack/test/api_integration/apis/management/snapshot_restore/snapshot_restore.ts b/x-pack/test/api_integration/apis/management/snapshot_restore/policies.ts similarity index 95% rename from x-pack/test/api_integration/apis/management/snapshot_restore/snapshot_restore.ts rename to x-pack/test/api_integration/apis/management/snapshot_restore/policies.ts index a6ac2d057c84e..e0734680887d2 100644 --- a/x-pack/test/api_integration/apis/management/snapshot_restore/snapshot_restore.ts +++ b/x-pack/test/api_integration/apis/management/snapshot_restore/policies.ts @@ -19,7 +19,7 @@ export default function ({ getService }: FtrProviderContext) { const { createRepository, createPolicy, deletePolicy, cleanupPolicies, getPolicy } = registerEsHelpers(getService); - describe('Snapshot Lifecycle Management', function () { + describe('SLM policies', function () { this.tags(['skipCloud']); // file system repositories are not supported in cloud before(async () => { @@ -134,9 +134,8 @@ export default function ({ getService }: FtrProviderContext) { describe('Update', () => { const POLICY_NAME = 'test_update_policy'; + const SNAPSHOT_NAME = 'my_snapshot'; const POLICY = { - name: POLICY_NAME, - snapshotName: 'my_snapshot', schedule: '0 30 1 * * ?', repository: REPO_NAME, config: { @@ -159,7 +158,7 @@ export default function ({ getService }: FtrProviderContext) { before(async () => { // Create SLM policy that can be used to test PUT request try { - await createPolicy(POLICY, true); + await createPolicy({ ...POLICY, policyName: POLICY_NAME, name: SNAPSHOT_NAME }, true); } catch (err) { // eslint-disable-next-line no-console console.log('[Setup error] Error creating policy'); @@ -175,6 +174,8 @@ export default function ({ getService }: FtrProviderContext) { .set('kbn-xsrf', 'xxx') .send({ ...POLICY, + name: POLICY_NAME, + snapshotName: SNAPSHOT_NAME, schedule: '0 0 0 ? * 7', }) .expect(200); @@ -212,7 +213,7 @@ export default function ({ getService }: FtrProviderContext) { const { body } = await supertest .put(uri) .set('kbn-xsrf', 'xxx') - .send(requiredFields) + .send({ ...requiredFields, name: POLICY_NAME, snapshotName: SNAPSHOT_NAME }) .expect(200); expect(body).to.eql({ diff --git a/x-pack/test/api_integration/apis/management/snapshot_restore/snapshots.ts b/x-pack/test/api_integration/apis/management/snapshot_restore/snapshots.ts new file mode 100644 index 0000000000000..1677013dd5e7e --- /dev/null +++ b/x-pack/test/api_integration/apis/management/snapshot_restore/snapshots.ts @@ -0,0 +1,729 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; + +import { FtrProviderContext } from '../../../ftr_provider_context'; +import { registerEsHelpers, SlmPolicy } from './lib'; +import { SnapshotDetails } from '../../../../../plugins/snapshot_restore/common/types'; + +const REPO_NAME_1 = 'test_repo_1'; +const REPO_NAME_2 = 'test_another_repo_2'; +const REPO_PATH_1 = '/tmp/repo_1'; +const REPO_PATH_2 = '/tmp/repo_2'; +// SLM policies to test policyName filter +const POLICY_NAME_1 = 'test_policy_1'; +const POLICY_NAME_2 = 'test_another_policy_2'; +const POLICY_SNAPSHOT_NAME_1 = 'backup_snapshot'; +const POLICY_SNAPSHOT_NAME_2 = 'a_snapshot'; +// snapshots created without SLM policies +const BATCH_SIZE_1 = 3; +const BATCH_SIZE_2 = 5; +const BATCH_SNAPSHOT_NAME_1 = 'another_snapshot'; +const BATCH_SNAPSHOT_NAME_2 = 'xyz_another_snapshot'; +// total count consists of both batches' sizes + 2 snapshots created by 2 SLM policies (one each) +const SNAPSHOT_COUNT = BATCH_SIZE_1 + BATCH_SIZE_2 + 2; +// API defaults used in the UI +const PAGE_INDEX = 0; +const PAGE_SIZE = 20; +const SORT_FIELD = 'startTimeInMillis'; +const SORT_DIRECTION = 'desc'; + +interface ApiParams { + pageIndex?: number; + pageSize?: number; + + sortField?: string; + sortDirection?: string; + + searchField?: string; + searchValue?: string; + searchMatch?: string; + searchOperator?: string; +} +const getApiPath = ({ + pageIndex, + pageSize, + sortField, + sortDirection, + searchField, + searchValue, + searchMatch, + searchOperator, +}: ApiParams): string => { + let path = `/api/snapshot_restore/snapshots?sortField=${sortField ?? SORT_FIELD}&sortDirection=${ + sortDirection ?? SORT_DIRECTION + }&pageIndex=${pageIndex ?? PAGE_INDEX}&pageSize=${pageSize ?? PAGE_SIZE}`; + // all 4 parameters should be used at the same time to configure the correct search request + if (searchField && searchValue && searchMatch && searchOperator) { + path = `${path}&searchField=${searchField}&searchValue=${searchValue}&searchMatch=${searchMatch}&searchOperator=${searchOperator}`; + } + return path; +}; +const getPolicyBody = (policy: Partial): SlmPolicy => { + return { + policyName: 'default_policy', + name: 'default_snapshot', + schedule: '0 30 1 * * ?', + repository: 'default_repo', + isManagedPolicy: false, + config: { + indices: ['default_index'], + ignoreUnavailable: true, + }, + ...policy, + }; +}; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + const { + createSnapshot, + createRepository, + createPolicy, + executePolicy, + cleanupPolicies, + deleteSnapshots, + } = registerEsHelpers(getService); + + describe('Snapshots', function () { + this.tags(['skipCloud']); // file system repositories are not supported in cloud + + // names of snapshots created by SLM policies have random suffixes, save full names for tests + let snapshotName1: string; + let snapshotName2: string; + + before(async () => { + /* + * This setup creates following repos, SLM policies and snapshots: + * Repo 1 "test_repo_1" with 5 snapshots + * "backup_snapshot..." (created by SLM policy "test_policy_1") + * "a_snapshot..." (created by SLM policy "test_another_policy_2") + * "another_snapshot_0" to "another_snapshot_2" (no SLM policy) + * + * Repo 2 "test_another_repo_2" with 5 snapshots + * "xyz_another_snapshot_0" to "xyz_another_snapshot_4" (no SLM policy) + */ + try { + await createRepository(REPO_NAME_1, REPO_PATH_1); + await createRepository(REPO_NAME_2, REPO_PATH_2); + await createPolicy( + getPolicyBody({ + policyName: POLICY_NAME_1, + repository: REPO_NAME_1, + name: POLICY_SNAPSHOT_NAME_1, + }), + true + ); + await createPolicy( + getPolicyBody({ + policyName: POLICY_NAME_2, + repository: REPO_NAME_1, + name: POLICY_SNAPSHOT_NAME_2, + }), + true + ); + ({ snapshot_name: snapshotName1 } = await executePolicy(POLICY_NAME_1)); + // a short timeout to let the 1st snapshot start, otherwise the sorting by start time might be flaky + await new Promise((resolve) => setTimeout(resolve, 2000)); + ({ snapshot_name: snapshotName2 } = await executePolicy(POLICY_NAME_2)); + for (let i = 0; i < BATCH_SIZE_1; i++) { + await createSnapshot(`${BATCH_SNAPSHOT_NAME_1}_${i}`, REPO_NAME_1); + } + for (let i = 0; i < BATCH_SIZE_2; i++) { + await createSnapshot(`${BATCH_SNAPSHOT_NAME_2}_${i}`, REPO_NAME_2); + } + } catch (err) { + // eslint-disable-next-line no-console + console.log('[Setup error] Error creating snapshots'); + throw err; + } + }); + + after(async () => { + await cleanupPolicies(); + await deleteSnapshots(REPO_NAME_1); + await deleteSnapshots(REPO_NAME_2); + }); + + describe('pagination', () => { + it('returns pageSize number of snapshots', async () => { + const pageSize = 7; + const { + body: { total, snapshots }, + } = await supertest + .get( + getApiPath({ + pageSize, + }) + ) + .set('kbn-xsrf', 'xxx') + .send(); + expect(total).to.eql(SNAPSHOT_COUNT); + expect(snapshots.length).to.eql(pageSize); + }); + + it('returns next page of snapshots', async () => { + const pageSize = 3; + let pageIndex = 0; + const { + body: { snapshots: firstPageSnapshots }, + } = await supertest + .get( + getApiPath({ + pageIndex, + pageSize, + }) + ) + .set('kbn-xsrf', 'xxx') + .send(); + + const firstPageSnapshotName = firstPageSnapshots[0].snapshot; + expect(firstPageSnapshots.length).to.eql(pageSize); + + pageIndex = 1; + const { + body: { snapshots: secondPageSnapshots }, + } = await supertest + .get( + getApiPath({ + pageIndex, + pageSize, + }) + ) + .set('kbn-xsrf', 'xxx') + .send(); + + const secondPageSnapshotName = secondPageSnapshots[0].snapshot; + expect(secondPageSnapshots.length).to.eql(pageSize); + expect(secondPageSnapshotName).to.not.eql(firstPageSnapshotName); + }); + }); + + describe('sorting', () => { + it('sorts by snapshot name (asc)', async () => { + const { + body: { snapshots }, + } = await supertest + .get( + getApiPath({ + sortField: 'snapshot', + sortDirection: 'asc', + }) + ) + .set('kbn-xsrf', 'xxx') + .send(); + + /* + * snapshots name in asc order: + * "a_snapshot...", "another_snapshot...", "backup_snapshot...", "xyz_another_snapshot..." + */ + const snapshotName = snapshots[0].snapshot; + // snapshotName2 is "a_snapshot..." + expect(snapshotName).to.eql(snapshotName2); + }); + + it('sorts by snapshot name (desc)', async () => { + const { + body: { snapshots }, + } = await supertest + .get( + getApiPath({ + sortField: 'snapshot', + sortDirection: 'desc', + }) + ) + .set('kbn-xsrf', 'xxx') + .send(); + /* + * snapshots name in desc order: + * "xyz_another_snapshot...", "backup_snapshot...", "another_snapshot...", "a_snapshot..." + */ + const snapshotName = snapshots[0].snapshot; + expect(snapshotName).to.eql('xyz_another_snapshot_4'); + }); + + it('sorts by repository name (asc)', async () => { + const { + body: { snapshots }, + } = await supertest + .get( + getApiPath({ + sortField: 'repository', + sortDirection: 'asc', + }) + ) + .set('kbn-xsrf', 'xxx') + .send(); + // repositories in asc order: "test_another_repo_2", "test_repo_1" + const repositoryName = snapshots[0].repository; + expect(repositoryName).to.eql(REPO_NAME_2); // "test_another_repo_2" + }); + + it('sorts by repository name (desc)', async () => { + const { + body: { snapshots }, + } = await supertest + .get( + getApiPath({ + sortField: 'repository', + sortDirection: 'desc', + }) + ) + .set('kbn-xsrf', 'xxx') + .send(); + // repositories in desc order: "test_repo_1", "test_another_repo_2" + const repositoryName = snapshots[0].repository; + expect(repositoryName).to.eql(REPO_NAME_1); // "test_repo_1" + }); + + it('sorts by startTimeInMillis (asc)', async () => { + const { + body: { snapshots }, + } = await supertest + .get( + getApiPath({ + sortField: 'startTimeInMillis', + sortDirection: 'asc', + }) + ) + .set('kbn-xsrf', 'xxx') + .send(); + const snapshotName = snapshots[0].snapshot; + // the 1st snapshot that was created during this test setup + expect(snapshotName).to.eql(snapshotName1); + }); + + it('sorts by startTimeInMillis (desc)', async () => { + const { + body: { snapshots }, + } = await supertest + .get( + getApiPath({ + sortField: 'startTimeInMillis', + sortDirection: 'desc', + }) + ) + .set('kbn-xsrf', 'xxx') + .send(); + const snapshotName = snapshots[0].snapshot; + // the last snapshot that was created during this test setup + expect(snapshotName).to.eql('xyz_another_snapshot_4'); + }); + + // these properties are only tested as being accepted by the API + const sortFields = ['indices', 'durationInMillis', 'shards.total', 'shards.failed']; + sortFields.forEach((sortField: string) => { + it(`allows sorting by ${sortField} (asc)`, async () => { + await supertest + .get( + getApiPath({ + sortField, + sortDirection: 'asc', + }) + ) + .set('kbn-xsrf', 'xxx') + .send() + .expect(200); + }); + + it(`allows sorting by ${sortField} (desc)`, async () => { + await supertest + .get( + getApiPath({ + sortField, + sortDirection: 'desc', + }) + ) + .set('kbn-xsrf', 'xxx') + .send() + .expect(200); + }); + }); + }); + + describe('search', () => { + describe('snapshot name', () => { + it('exact match', async () => { + // list snapshots with the name "another_snapshot_2" + const searchField = 'snapshot'; + const searchValue = 'another_snapshot_2'; + const searchMatch = 'must'; + const searchOperator = 'exact'; + const { + body: { snapshots }, + } = await supertest + .get( + getApiPath({ + searchField, + searchValue, + searchMatch, + searchOperator, + }) + ) + .set('kbn-xsrf', 'xxx') + .send(); + + expect(snapshots.length).to.eql(1); + expect(snapshots[0].snapshot).to.eql('another_snapshot_2'); + }); + + it('partial match', async () => { + // list snapshots with the name containing with "another" + const searchField = 'snapshot'; + const searchValue = 'another'; + const searchMatch = 'must'; + const searchOperator = 'eq'; + const { + body: { snapshots }, + } = await supertest + .get( + getApiPath({ + searchField, + searchValue, + searchMatch, + searchOperator, + }) + ) + .set('kbn-xsrf', 'xxx') + .send(); + + // both batches created snapshots containing "another" in the name + expect(snapshots.length).to.eql(BATCH_SIZE_1 + BATCH_SIZE_2); + const snapshotNamesContainSearch = snapshots.every((snapshot: SnapshotDetails) => + snapshot.snapshot.includes('another') + ); + expect(snapshotNamesContainSearch).to.eql(true); + }); + + it('excluding search with exact match', async () => { + // list snapshots with the name not "another_snapshot_2" + const searchField = 'snapshot'; + const searchValue = 'another_snapshot_2'; + const searchMatch = 'must_not'; + const searchOperator = 'exact'; + const { + body: { snapshots }, + } = await supertest + .get( + getApiPath({ + searchField, + searchValue, + searchMatch, + searchOperator, + }) + ) + .set('kbn-xsrf', 'xxx') + .send(); + + expect(snapshots.length).to.eql(SNAPSHOT_COUNT - 1); + const snapshotIsExcluded = snapshots.every( + (snapshot: SnapshotDetails) => snapshot.snapshot !== 'another_snapshot_2' + ); + expect(snapshotIsExcluded).to.eql(true); + }); + + it('excluding search with partial match', async () => { + // list snapshots with the name not starting with "another" + const searchField = 'snapshot'; + const searchValue = 'another'; + const searchMatch = 'must_not'; + const searchOperator = 'eq'; + const { + body: { snapshots }, + } = await supertest + .get( + getApiPath({ + searchField, + searchValue, + searchMatch, + searchOperator, + }) + ) + .set('kbn-xsrf', 'xxx') + .send(); + + // both batches created snapshots with names containing "another" + expect(snapshots.length).to.eql(SNAPSHOT_COUNT - BATCH_SIZE_1 - BATCH_SIZE_2); + const snapshotsAreExcluded = snapshots.every( + (snapshot: SnapshotDetails) => !snapshot.snapshot.includes('another') + ); + expect(snapshotsAreExcluded).to.eql(true); + }); + }); + + describe('repository name', () => { + it('search for non-existent repository returns an empty snapshot array', async () => { + // search for non-existent repository + const searchField = 'repository'; + const searchValue = 'non-existent'; + const searchMatch = 'must'; + const searchOperator = 'exact'; + const { + body: { snapshots }, + } = await supertest + .get( + getApiPath({ + searchField, + searchValue, + searchMatch, + searchOperator, + }) + ) + .set('kbn-xsrf', 'xxx') + .send(); + + expect(snapshots.length).to.eql(0); + }); + + it('exact match', async () => { + // list snapshots from repository "test_repo_1" + const searchField = 'repository'; + const searchValue = REPO_NAME_1; + const searchMatch = 'must'; + const searchOperator = 'exact'; + const { + body: { snapshots }, + } = await supertest + .get( + getApiPath({ + searchField, + searchValue, + searchMatch, + searchOperator, + }) + ) + .set('kbn-xsrf', 'xxx') + .send(); + + // repo 1 contains snapshots from batch 1 and 2 snapshots created by 2 SLM policies + expect(snapshots.length).to.eql(BATCH_SIZE_1 + 2); + const repositoryNameMatches = snapshots.every( + (snapshot: SnapshotDetails) => snapshot.repository === REPO_NAME_1 + ); + expect(repositoryNameMatches).to.eql(true); + }); + + it('partial match', async () => { + // list snapshots from repository with the name containing "another" + // i.e. snapshots from repo 2 + const searchField = 'repository'; + const searchValue = 'another'; + const searchMatch = 'must'; + const searchOperator = 'eq'; + const { + body: { snapshots }, + } = await supertest + .get( + getApiPath({ + searchField, + searchValue, + searchMatch, + searchOperator, + }) + ) + .set('kbn-xsrf', 'xxx') + .send(); + + // repo 2 only contains snapshots created by batch 2 + expect(snapshots.length).to.eql(BATCH_SIZE_2); + const repositoryNameMatches = snapshots.every((snapshot: SnapshotDetails) => + snapshot.repository.includes('another') + ); + expect(repositoryNameMatches).to.eql(true); + }); + + it('excluding search with exact match', async () => { + // list snapshots from repositories with the name not "test_repo_1" + const searchField = 'repository'; + const searchValue = REPO_NAME_1; + const searchMatch = 'must_not'; + const searchOperator = 'exact'; + const { + body: { snapshots }, + } = await supertest + .get( + getApiPath({ + searchField, + searchValue, + searchMatch, + searchOperator, + }) + ) + .set('kbn-xsrf', 'xxx') + .send(); + + // snapshots not in repo 1 are only snapshots created in batch 2 + expect(snapshots.length).to.eql(BATCH_SIZE_2); + const repositoryNameMatches = snapshots.every( + (snapshot: SnapshotDetails) => snapshot.repository !== REPO_NAME_1 + ); + expect(repositoryNameMatches).to.eql(true); + }); + + it('excluding search with partial match', async () => { + // list snapshots from repository with the name not containing "test" + const searchField = 'repository'; + const searchValue = 'test'; + const searchMatch = 'must_not'; + const searchOperator = 'eq'; + const { + body: { snapshots }, + } = await supertest + .get( + getApiPath({ + searchField, + searchValue, + searchMatch, + searchOperator, + }) + ) + .set('kbn-xsrf', 'xxx') + .send(); + + expect(snapshots.length).to.eql(0); + }); + }); + + describe('policy name', () => { + it('search for non-existent policy returns an empty snapshot array', async () => { + // search for non-existent policy + const searchField = 'policyName'; + const searchValue = 'non-existent'; + const searchMatch = 'must'; + const searchOperator = 'exact'; + const { + body: { snapshots }, + } = await supertest + .get( + getApiPath({ + searchField, + searchValue, + searchMatch, + searchOperator, + }) + ) + .set('kbn-xsrf', 'xxx') + .send(); + + expect(snapshots.length).to.eql(0); + }); + + it('exact match', async () => { + // list snapshots created by the policy "test_policy_1" + const searchField = 'policyName'; + const searchValue = POLICY_NAME_1; + const searchMatch = 'must'; + const searchOperator = 'exact'; + const { + body: { snapshots }, + } = await supertest + .get( + getApiPath({ + searchField, + searchValue, + searchMatch, + searchOperator, + }) + ) + .set('kbn-xsrf', 'xxx') + .send(); + + expect(snapshots.length).to.eql(1); + expect(snapshots[0].policyName).to.eql(POLICY_NAME_1); + }); + + it('partial match', async () => { + // list snapshots created by the policy with the name containing "another" + const searchField = 'policyName'; + const searchValue = 'another'; + const searchMatch = 'must'; + const searchOperator = 'eq'; + const { + body: { snapshots }, + } = await supertest + .get( + getApiPath({ + searchField, + searchValue, + searchMatch, + searchOperator, + }) + ) + .set('kbn-xsrf', 'xxx') + .send(); + + // 1 snapshot was created by the policy "test_another_policy_2" + expect(snapshots.length).to.eql(1); + const policyNameMatches = snapshots.every((snapshot: SnapshotDetails) => + snapshot.policyName!.includes('another') + ); + expect(policyNameMatches).to.eql(true); + }); + + it('excluding search with exact match', async () => { + // list snapshots created by the policy with the name not "test_policy_1" + const searchField = 'policyName'; + const searchValue = POLICY_NAME_1; + const searchMatch = 'must_not'; + const searchOperator = 'exact'; + const { + body: { snapshots }, + } = await supertest + .get( + getApiPath({ + searchField, + searchValue, + searchMatch, + searchOperator, + }) + ) + .set('kbn-xsrf', 'xxx') + .send(); + + // only 1 snapshot was created by policy 1 + // search results should also contain snapshots without SLM policy + expect(snapshots.length).to.eql(SNAPSHOT_COUNT - 1); + const snapshotsExcluded = snapshots.every( + (snapshot: SnapshotDetails) => (snapshot.policyName ?? '') !== POLICY_NAME_1 + ); + expect(snapshotsExcluded).to.eql(true); + }); + + it('excluding search with partial match', async () => { + // list snapshots created by the policy with the name not containing "another" + const searchField = 'policyName'; + const searchValue = 'another'; + const searchMatch = 'must_not'; + const searchOperator = 'eq'; + const { + body: { snapshots }, + } = await supertest + .get( + getApiPath({ + searchField, + searchValue, + searchMatch, + searchOperator, + }) + ) + .set('kbn-xsrf', 'xxx') + .send(); + + // only 1 snapshot was created by SLM policy containing "another" in the name + // search results should also contain snapshots without SLM policy + expect(snapshots.length).to.eql(SNAPSHOT_COUNT - 1); + const snapshotsExcluded = snapshots.every( + (snapshot: SnapshotDetails) => !(snapshot.policyName ?? '').includes('another') + ); + expect(snapshotsExcluded).to.eql(true); + }); + }); + }); + }); +} diff --git a/x-pack/test/api_integration/config.ts b/x-pack/test/api_integration/config.ts index 3690f661c621c..678f7a0d3d929 100644 --- a/x-pack/test/api_integration/config.ts +++ b/x-pack/test/api_integration/config.ts @@ -41,6 +41,7 @@ export async function getApiIntegrationConfig({ readConfigFile }: FtrConfigProvi serverArgs: [ ...xPackFunctionalTestsConfig.get('esTestCluster.serverArgs'), 'node.attr.name=apiIntegrationTestNode', + 'path.repo=/tmp/repo,/tmp/repo_1,/tmp/repo_2', ], }, }; From 83739b08ca0779f9697958e105cb3fbf3ff497d0 Mon Sep 17 00:00:00 2001 From: Kyle Pollich Date: Mon, 18 Oct 2021 15:20:24 -0400 Subject: [PATCH 010/204] [Fleet] Address Package Policy upgrade UX review (#115414) * Fix package update button + icon * Adjust order of modal of states based on UX review * Clarify integration/agent policies in integration UI policy table --- .../integrations/sections/epm/screens/detail/index.tsx | 2 +- .../epm/screens/detail/policies/package_policies.tsx | 2 +- .../sections/epm/screens/detail/settings/update_button.tsx | 7 +++---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx index 881fc566c932d..6e3eba19c52e3 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx @@ -452,7 +452,7 @@ export function Detail() { name: ( ), isSelected: panel === 'policies', diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx index d6dc7e8440dae..69487454dcb94 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx @@ -211,7 +211,7 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps { field: 'packagePolicy.name', name: i18n.translate('xpack.fleet.epm.packageDetails.integrationList.name', { - defaultMessage: 'Integration', + defaultMessage: 'Integration Policy', }), render(_, { packagePolicy }) { return ; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/update_button.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/update_button.tsx index 2acd5634b1e5f..b5a8394fa2cb2 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/update_button.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/settings/update_button.tsx @@ -154,6 +154,7 @@ export const UpdateButton: React.FunctionComponent = ({ return; } + setIsUpdateModalVisible(false); setIsUpgradingPackagePolicies(true); await installPackage({ name, version, title }); @@ -166,7 +167,6 @@ export const UpdateButton: React.FunctionComponent = ({ ); setIsUpgradingPackagePolicies(false); - setIsUpdateModalVisible(false); notifications.toasts.addSuccess({ title: toMountPoint( @@ -285,15 +285,14 @@ export const UpdateButton: React.FunctionComponent = ({ setIsUpdateModalVisible(true) : handleClickUpdate } > From 3f5ad2ab9463067c1f0a9661ab315af74c5f4c19 Mon Sep 17 00:00:00 2001 From: Ignacio Rivas Date: Mon, 18 Oct 2021 21:07:02 +0100 Subject: [PATCH 011/204] Fix test (#115362) --- .../helpers/http_requests.ts | 9 ++++ .../home/indices_tab.helpers.ts | 19 ++++--- .../home/indices_tab.test.ts | 50 ++++++++----------- 3 files changed, 42 insertions(+), 36 deletions(-) diff --git a/x-pack/plugins/index_management/__jest__/client_integration/helpers/http_requests.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/http_requests.ts index 58edb1aae80e2..b9f1191da8af7 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/helpers/http_requests.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/http_requests.ts @@ -28,6 +28,14 @@ const registerHttpRequestMockHelpers = (server: SinonFakeServer) => { ]); }; + const setReloadIndicesResponse = (response: HttpResponse = []) => { + server.respondWith('POST', `${API_BASE_PATH}/indices/reload`, [ + 200, + { 'Content-Type': 'application/json' }, + JSON.stringify(response), + ]); + }; + const setLoadDataStreamsResponse = (response: HttpResponse = []) => { server.respondWith('GET', `${API_BASE_PATH}/data_streams`, [ 200, @@ -118,6 +126,7 @@ const registerHttpRequestMockHelpers = (server: SinonFakeServer) => { return { setLoadTemplatesResponse, setLoadIndicesResponse, + setReloadIndicesResponse, setLoadDataStreamsResponse, setLoadDataStreamResponse, setDeleteDataStreamResponse, diff --git a/x-pack/plugins/index_management/__jest__/client_integration/home/indices_tab.helpers.ts b/x-pack/plugins/index_management/__jest__/client_integration/home/indices_tab.helpers.ts index 900f7ddbf084b..2576b5f92b7b2 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/home/indices_tab.helpers.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/home/indices_tab.helpers.ts @@ -47,9 +47,12 @@ export const setup = async (overridingDependencies: any = {}): Promise { - const { find } = testBed; - const contextMenu = find('indexContextMenu'); - contextMenu.find(`button[data-test-subj="${optionDataTestSubject}"]`).simulate('click'); + const { find, component } = testBed; + + await act(async () => { + find(`indexContextMenu.${optionDataTestSubject}`).simulate('click'); + }); + component.update(); }; const clickIncludeHiddenIndicesToggle = () => { @@ -57,9 +60,13 @@ export const setup = async (overridingDependencies: any = {}): Promise { - const { find } = testBed; - find('indexActionsContextMenuButton').simulate('click'); + const clickManageContextMenuButton = async () => { + const { find, component } = testBed; + + await act(async () => { + find('indexActionsContextMenuButton').simulate('click'); + }); + component.update(); }; const getIncludeHiddenIndicesToggleStatus = () => { diff --git a/x-pack/plugins/index_management/__jest__/client_integration/home/indices_tab.test.ts b/x-pack/plugins/index_management/__jest__/client_integration/home/indices_tab.test.ts index e23c1a59eb135..f95ea373d58b4 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/home/indices_tab.test.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/home/indices_tab.test.ts @@ -10,7 +10,7 @@ import { act } from 'react-dom/test-utils'; import { API_BASE_PATH } from '../../../common/constants'; import { setupEnvironment, nextTick } from '../helpers'; import { IndicesTestBed, setup } from './indices_tab.helpers'; -import { createDataStreamPayload } from './data_streams_tab.helpers'; +import { createDataStreamPayload, createNonDataStreamIndex } from './data_streams_tab.helpers'; /** * The below import is required to avoid a console error warn from the "brace" package @@ -23,9 +23,14 @@ import { createMemoryHistory } from 'history'; stubWebWorker(); // unhandled promise rejection https://github.com/elastic/kibana/issues/112699 -describe.skip('', () => { - const { server, httpRequestsMockHelpers } = setupEnvironment(); +describe('', () => { let testBed: IndicesTestBed; + let server: ReturnType['server']; + let httpRequestsMockHelpers: ReturnType['httpRequestsMockHelpers']; + + beforeEach(() => { + ({ server, httpRequestsMockHelpers } = setupEnvironment()); + }); afterAll(() => { server.restore(); @@ -108,19 +113,9 @@ describe.skip('', () => { describe('index detail panel with % character in index name', () => { const indexName = 'test%'; + beforeEach(async () => { - const index = { - health: 'green', - status: 'open', - primary: 1, - replica: 1, - documents: 10000, - documents_deleted: 100, - size: '156kb', - primary_size: '156kb', - name: indexName, - }; - httpRequestsMockHelpers.setLoadIndicesResponse([index]); + httpRequestsMockHelpers.setLoadIndicesResponse([createNonDataStreamIndex(indexName)]); testBed = await setup(); const { component, find } = testBed; @@ -165,20 +160,11 @@ describe.skip('', () => { describe('index actions', () => { const indexName = 'testIndex'; + beforeEach(async () => { - const index = { - health: 'green', - status: 'open', - primary: 1, - replica: 1, - documents: 10000, - documents_deleted: 100, - size: '156kb', - primary_size: '156kb', - name: indexName, - }; - - httpRequestsMockHelpers.setLoadIndicesResponse([index]); + httpRequestsMockHelpers.setLoadIndicesResponse([createNonDataStreamIndex(indexName)]); + httpRequestsMockHelpers.setReloadIndicesResponse({ indexNames: [indexName] }); + testBed = await setup(); const { find, component } = testBed; component.update(); @@ -188,11 +174,15 @@ describe.skip('', () => { test('should be able to flush index', async () => { const { actions } = testBed; + await actions.clickManageContextMenuButton(); await actions.clickContextMenuOption('flushIndexMenuButton'); - const latestRequest = server.requests[server.requests.length - 1]; - expect(latestRequest.url).toBe(`${API_BASE_PATH}/indices/flush`); + const requestsCount = server.requests.length; + expect(server.requests[requestsCount - 2].url).toBe(`${API_BASE_PATH}/indices/flush`); + // After the indices are flushed, we imediately reload them. So we need to expect to see + // a reload server call also. + expect(server.requests[requestsCount - 1].url).toBe(`${API_BASE_PATH}/indices/reload`); }); }); }); From 70c57bca08aa8d8331a26d461a79be26b19b66aa Mon Sep 17 00:00:00 2001 From: DeDe Morton Date: Mon, 18 Oct 2021 13:58:07 -0700 Subject: [PATCH 012/204] Update doc links to Fleet/Agent docs (#115289) --- docs/getting-started/quick-start-guide.asciidoc | 2 +- docs/osquery/osquery.asciidoc | 2 +- docs/setup/connect-to-elasticsearch.asciidoc | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/getting-started/quick-start-guide.asciidoc b/docs/getting-started/quick-start-guide.asciidoc index d614aece5b425..2bddd9bf61452 100644 --- a/docs/getting-started/quick-start-guide.asciidoc +++ b/docs/getting-started/quick-start-guide.asciidoc @@ -138,7 +138,7 @@ image::images/dashboard_sampleDataAddFilter_7.15.0.png[The [eCommerce] Revenue D [[quick-start-whats-next]] == What's next? -*Add your own data.* Ready to add your own data? Go to {fleet-guide}/fleet-quick-start.html[Quick start: Get logs and metrics into the Elastic Stack] to learn how to ingest your data, or go to <> and learn about all the other ways you can add data. +*Add your own data.* Ready to add your own data? Go to {observability-guide}/ingest-logs-metrics-uptime.html[Ingest logs, metrics, and uptime data with {agent}], or go to <> and learn about all the other ways you can add data. *Explore your own data in Discover.* Ready to learn more about exploring your data in *Discover*? Go to <>. diff --git a/docs/osquery/osquery.asciidoc b/docs/osquery/osquery.asciidoc index 1e4e6604a7c70..a4f3c80463143 100644 --- a/docs/osquery/osquery.asciidoc +++ b/docs/osquery/osquery.asciidoc @@ -365,7 +365,7 @@ The following is an example of an **error response** for an undefined action que == System requirements * {fleet-guide}/fleet-overview.html[Fleet] is enabled on your cluster, and -one or more {fleet-guide}/elastic-agent-installation-configuration.html[Elastic Agents] is enrolled. +one or more {fleet-guide}/elastic-agent-installation.html[Elastic Agents] is enrolled. * The https://docs.elastic.co/en/integrations/osquery_manager[*Osquery Manager*] integration has been added and configured for an agent policy through Fleet. diff --git a/docs/setup/connect-to-elasticsearch.asciidoc b/docs/setup/connect-to-elasticsearch.asciidoc index c7f745648cbe9..ad38ac1710fd5 100644 --- a/docs/setup/connect-to-elasticsearch.asciidoc +++ b/docs/setup/connect-to-elasticsearch.asciidoc @@ -47,7 +47,7 @@ so you can quickly get insights into your data, and {fleet} mode offers several image::images/addData_fleet_7.15.0.png[Add data using Fleet] To get started, refer to -{fleet-guide}/fleet-quick-start.html[Quick start: Get logs and metrics into the Elastic Stack]. +{observability-guide}/ingest-logs-metrics-uptime.html[Ingest logs, metrics, and uptime data with {agent}]. [discrete] [[upload-data-kibana]] From 7e1acab9dd3c3504fe0ef58be29547919d7b3a26 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Mon, 18 Oct 2021 23:09:04 +0200 Subject: [PATCH 013/204] [Exploratory View] Added step level filtering/breakdowns (#115182) Co-authored-by: Dominique Clarke Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../configurations/constants/constants.ts | 3 + .../constants/field_names/synthetics.ts | 2 + .../configurations/constants/labels.ts | 14 ++++ .../configurations/lens_attributes.ts | 16 ++++- .../synthetics/data_distribution_config.ts | 6 +- .../synthetics/field_formats.ts | 14 ++++ .../synthetics/kpi_over_time_config.ts | 54 +++++++++++++- .../test_data/sample_attribute_cwv.ts | 3 +- .../series_editor/breakdown/breakdowns.tsx | 49 +++++++++++-- .../columns/incomplete_badge.tsx | 9 ++- .../columns/report_definition_col.tsx | 70 ++++++++++++++++--- .../columns/report_definition_field.tsx | 47 +++++++++---- .../series_editor/expanded_series_row.tsx | 6 +- .../shared/exploratory_view/types.ts | 10 ++- .../field_value_combobox.tsx | 2 + .../shared/field_value_suggestions/index.tsx | 5 +- .../shared/field_value_suggestions/types.ts | 1 + .../common/header/action_menu_content.tsx | 5 +- 18 files changed, 268 insertions(+), 48 deletions(-) diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/constants/constants.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/constants/constants.ts index e4473b183d729..c12e67bc9b1ae 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/constants/constants.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/constants/constants.ts @@ -49,7 +49,9 @@ import { MONITORS_DURATION_LABEL, PAGE_LOAD_TIME_LABEL, LABELS_FIELD, + STEP_NAME_LABEL, } from './labels'; +import { SYNTHETICS_STEP_NAME } from './field_names/synthetics'; export const DEFAULT_TIME = { from: 'now-1h', to: 'now' }; @@ -77,6 +79,7 @@ export const FieldLabels: Record = { 'monitor.id': MONITOR_ID_LABEL, 'monitor.status': MONITOR_STATUS_LABEL, 'monitor.duration.us': MONITORS_DURATION_LABEL, + [SYNTHETICS_STEP_NAME]: STEP_NAME_LABEL, 'agent.hostname': AGENT_HOST_LABEL, 'host.hostname': HOST_NAME_LABEL, diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/constants/field_names/synthetics.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/constants/field_names/synthetics.ts index eff73d242de75..0f28648552728 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/constants/field_names/synthetics.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/constants/field_names/synthetics.ts @@ -11,3 +11,5 @@ export const SYNTHETICS_LCP = 'browser.experience.lcp.us'; export const SYNTHETICS_FCP = 'browser.experience.fcp.us'; export const SYNTHETICS_DOCUMENT_ONLOAD = 'browser.experience.load.us'; export const SYNTHETICS_DCL = 'browser.experience.dcl.us'; +export const SYNTHETICS_STEP_NAME = 'synthetics.step.name.keyword'; +export const SYNTHETICS_STEP_DURATION = 'synthetics.step.duration.us'; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/constants/labels.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/constants/labels.ts index cdaa89fc71389..599f846af2ff9 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/constants/labels.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/constants/labels.ts @@ -231,6 +231,20 @@ export const MONITORS_DURATION_LABEL = i18n.translate( } ); +export const STEP_DURATION_LABEL = i18n.translate( + 'xpack.observability.expView.fieldLabels.stepDurationLabel', + { + defaultMessage: 'Step duration', + } +); + +export const STEP_NAME_LABEL = i18n.translate( + 'xpack.observability.expView.fieldLabels.stepNameLabel', + { + defaultMessage: 'Step name', + } +); + export const WEB_APPLICATION_LABEL = i18n.translate( 'xpack.observability.expView.fieldLabels.webApplication', { diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts index 38c9ecc06491d..a31bef7c9c214 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts @@ -194,10 +194,12 @@ export class LensAttributes { label, sourceField, columnType, + columnFilter, operationType, }: { sourceField: string; columnType?: string; + columnFilter?: ColumnFilter; operationType?: string; label?: string; seriesConfig: SeriesConfig; @@ -214,6 +216,7 @@ export class LensAttributes { operationType, label, seriesConfig, + columnFilter, }); } if (operationType?.includes('th')) { @@ -228,11 +231,13 @@ export class LensAttributes { label, seriesConfig, operationType, + columnFilter, }: { sourceField: string; operationType: 'average' | 'median' | 'sum' | 'unique_count'; label?: string; seriesConfig: SeriesConfig; + columnFilter?: ColumnFilter; }): | AvgIndexPatternColumn | MedianIndexPatternColumn @@ -247,6 +252,7 @@ export class LensAttributes { operationType: capitalize(operationType), }, }), + filter: columnFilter, operationType, }; } @@ -391,6 +397,7 @@ export class LensAttributes { return this.getNumberColumn({ sourceField: fieldName, columnType, + columnFilter: columnFilters?.[0], operationType, label: columnLabel || label, seriesConfig: layerConfig.seriesConfig, @@ -447,10 +454,10 @@ export class LensAttributes { return this.getColumnBasedOnType({ sourceField, - operationType: breakdown === PERCENTILE ? PERCENTILE_RANKS[0] : operationType, label, layerConfig, colIndex: 0, + operationType: breakdown === PERCENTILE ? PERCENTILE_RANKS[0] : operationType, }); } @@ -629,7 +636,12 @@ export class LensAttributes { [`y-axis-column-${layerId}`]: { ...mainYAxis, label, - filter: { query: columnFilter, language: 'kuery' }, + filter: { + query: mainYAxis.filter + ? `${columnFilter} and ${mainYAxis.filter.query}` + : columnFilter, + language: 'kuery', + }, ...(timeShift ? { timeShift } : {}), }, ...(breakdown && sourceField !== USE_BREAK_DOWN_COLUMN && breakdown !== PERCENTILE diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/synthetics/data_distribution_config.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/synthetics/data_distribution_config.ts index da90f45d15201..fb44da8e4327f 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/synthetics/data_distribution_config.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/synthetics/data_distribution_config.ts @@ -29,6 +29,7 @@ import { SYNTHETICS_FCP, SYNTHETICS_LCP, } from '../constants/field_names/synthetics'; +import { buildExistsFilter } from '../utils'; export function getSyntheticsDistributionConfig({ series, @@ -58,7 +59,10 @@ export function getSyntheticsDistributionConfig({ 'url.port', ], baseFilters: [], - definitionFields: ['monitor.name', 'url.full'], + definitionFields: [ + { field: 'monitor.name', nested: 'synthetics.step.name.keyword', singleSelection: true }, + { field: 'url.full', filters: buildExistsFilter('summary.up', indexPattern) }, + ], metricOptions: [ { label: MONITORS_DURATION_LABEL, diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/synthetics/field_formats.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/synthetics/field_formats.ts index 5f8a6a28ca81d..f3e3fe0817845 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/synthetics/field_formats.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/synthetics/field_formats.ts @@ -11,6 +11,7 @@ import { SYNTHETICS_DOCUMENT_ONLOAD, SYNTHETICS_FCP, SYNTHETICS_LCP, + SYNTHETICS_STEP_DURATION, } from '../constants/field_names/synthetics'; export const syntheticsFieldFormats: FieldFormat[] = [ @@ -27,6 +28,19 @@ export const syntheticsFieldFormats: FieldFormat[] = [ }, }, }, + { + field: SYNTHETICS_STEP_DURATION, + format: { + id: 'duration', + params: { + inputFormat: 'microseconds', + outputFormat: 'humanizePrecise', + outputPrecision: 1, + showSuffix: true, + useShortSuffix: true, + }, + }, + }, { field: SYNTHETICS_LCP, format: { diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/synthetics/kpi_over_time_config.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/synthetics/kpi_over_time_config.ts index 6df9cdcd0503a..8951ffcda63d8 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/synthetics/kpi_over_time_config.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/synthetics/kpi_over_time_config.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { ConfigProps, SeriesConfig } from '../../types'; +import { ColumnFilter, ConfigProps, SeriesConfig } from '../../types'; import { FieldLabels, OPERATION_COLUMN, @@ -21,6 +21,7 @@ import { FCP_LABEL, LCP_LABEL, MONITORS_DURATION_LABEL, + STEP_DURATION_LABEL, UP_LABEL, } from '../constants/labels'; import { @@ -30,10 +31,26 @@ import { SYNTHETICS_DOCUMENT_ONLOAD, SYNTHETICS_FCP, SYNTHETICS_LCP, + SYNTHETICS_STEP_DURATION, + SYNTHETICS_STEP_NAME, } from '../constants/field_names/synthetics'; +import { buildExistsFilter } from '../utils'; const SUMMARY_UP = 'summary.up'; const SUMMARY_DOWN = 'summary.down'; +export const isStepLevelMetric = (metric?: string) => { + if (!metric) { + return false; + } + return [ + SYNTHETICS_LCP, + SYNTHETICS_FCP, + SYNTHETICS_CLS, + SYNTHETICS_DCL, + SYNTHETICS_STEP_DURATION, + SYNTHETICS_DOCUMENT_ONLOAD, + ].includes(metric); +}; export function getSyntheticsKPIConfig({ indexPattern }: ConfigProps): SeriesConfig { return { reportType: ReportTypes.KPI, @@ -50,10 +67,19 @@ export function getSyntheticsKPIConfig({ indexPattern }: ConfigProps): SeriesCon ], hasOperationType: false, filterFields: ['observer.geo.name', 'monitor.type', 'tags'], - breakdownFields: ['observer.geo.name', 'monitor.type', 'monitor.name', PERCENTILE], + breakdownFields: [ + 'observer.geo.name', + 'monitor.type', + 'monitor.name', + SYNTHETICS_STEP_NAME, + PERCENTILE, + ], baseFilters: [], palette: { type: 'palette', name: 'status' }, - definitionFields: ['monitor.name', 'url.full'], + definitionFields: [ + { field: 'monitor.name', nested: SYNTHETICS_STEP_NAME, singleSelection: true }, + { field: 'url.full', filters: buildExistsFilter('summary.up', indexPattern) }, + ], metricOptions: [ { label: MONITORS_DURATION_LABEL, @@ -73,37 +99,59 @@ export function getSyntheticsKPIConfig({ indexPattern }: ConfigProps): SeriesCon label: DOWN_LABEL, columnType: OPERATION_COLUMN, }, + { + label: STEP_DURATION_LABEL, + field: SYNTHETICS_STEP_DURATION, + id: SYNTHETICS_STEP_DURATION, + columnType: OPERATION_COLUMN, + columnFilters: [STEP_END_FILTER], + }, { label: LCP_LABEL, field: SYNTHETICS_LCP, id: SYNTHETICS_LCP, columnType: OPERATION_COLUMN, + columnFilters: [STEP_METRIC_FILTER], }, { label: FCP_LABEL, field: SYNTHETICS_FCP, id: SYNTHETICS_FCP, columnType: OPERATION_COLUMN, + columnFilters: [STEP_METRIC_FILTER], }, { label: DCL_LABEL, field: SYNTHETICS_DCL, id: SYNTHETICS_DCL, columnType: OPERATION_COLUMN, + columnFilters: [STEP_METRIC_FILTER], }, { label: DOCUMENT_ONLOAD_LABEL, field: SYNTHETICS_DOCUMENT_ONLOAD, id: SYNTHETICS_DOCUMENT_ONLOAD, columnType: OPERATION_COLUMN, + columnFilters: [STEP_METRIC_FILTER], }, { label: CLS_LABEL, field: SYNTHETICS_CLS, id: SYNTHETICS_CLS, columnType: OPERATION_COLUMN, + columnFilters: [STEP_METRIC_FILTER], }, ], labels: { ...FieldLabels, [SUMMARY_UP]: UP_LABEL, [SUMMARY_DOWN]: DOWN_LABEL }, }; } + +const STEP_METRIC_FILTER: ColumnFilter = { + language: 'kuery', + query: `synthetics.type: step/metrics`, +}; + +const STEP_END_FILTER: ColumnFilter = { + language: 'kuery', + query: `synthetics.type: step/end`, +}; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/test_data/sample_attribute_cwv.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/test_data/sample_attribute_cwv.ts index adc6d4bb14462..4563509eeb19a 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/test_data/sample_attribute_cwv.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/test_data/sample_attribute_cwv.ts @@ -77,7 +77,8 @@ export const sampleAttributeCoreWebVital = { dataType: 'number', filter: { language: 'kuery', - query: 'transaction.type: page-load and processor.event: transaction', + query: + 'transaction.type: page-load and processor.event: transaction and transaction.marks.agent.largestContentfulPaint < 2500', }, isBucketed: false, label: 'Good', diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/breakdown/breakdowns.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/breakdown/breakdowns.tsx index a235cbd8852ad..f30a80f87ebb7 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/breakdown/breakdowns.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/breakdown/breakdowns.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React from 'react'; +import React, { useEffect } from 'react'; import styled from 'styled-components'; import { EuiSuperSelect, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -17,6 +17,8 @@ import { PERCENTILE, } from '../../configurations/constants'; import { SeriesConfig, SeriesUrl } from '../../types'; +import { SYNTHETICS_STEP_NAME } from '../../configurations/constants/field_names/synthetics'; +import { isStepLevelMetric } from '../../configurations/synthetics/kpi_over_time_config'; interface Props { seriesId: number; @@ -51,6 +53,18 @@ export function Breakdowns({ seriesConfig, seriesId, series }: Props) { } }; + useEffect(() => { + if ( + !isStepLevelMetric(series.selectedMetricField) && + selectedBreakdown === SYNTHETICS_STEP_NAME + ) { + setSeries(seriesId, { + ...series, + breakdown: undefined, + }); + } + }); + if (!seriesConfig) { return null; } @@ -71,11 +85,26 @@ export function Breakdowns({ seriesConfig, seriesId, series }: Props) { } const options = items - .map(({ id, label }) => ({ - inputDisplay: label, - value: id, - dropdownDisplay: label, - })) + .map(({ id, label }) => { + if (id === SYNTHETICS_STEP_NAME && !isStepLevelMetric(series.selectedMetricField)) { + return { + inputDisplay: label, + value: id, + dropdownDisplay: ( + + <>{label} + + ), + disabled: true, + }; + } else { + return { + inputDisplay: label, + value: id, + dropdownDisplay: label, + }; + } + }) .filter(({ value }) => !(value === PERCENTILE && isRecordsMetric)); let valueOfSelected = @@ -121,6 +150,14 @@ export const BREAKDOWN_WARNING = i18n.translate('xpack.observability.exp.breakDo defaultMessage: 'Breakdowns can be applied to only one series at a time.', }); +export const BREAKDOWN_UNAVAILABLE = i18n.translate( + 'xpack.observability.exp.breakDownFilter.unavailable', + { + defaultMessage: + 'Step name breakdown is not available for monitor duration metric. Use step duration metric to breakdown by step name.', + } +); + const Wrapper = styled.span` .euiToolTipAnchor { width: 100%; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/incomplete_badge.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/incomplete_badge.tsx index 4e1c385921908..8e64f4bcea680 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/incomplete_badge.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/incomplete_badge.tsx @@ -31,7 +31,14 @@ export function IncompleteBadge({ seriesConfig, series }: Props) { const incompleteDefinition = isEmpty(reportDefinitions) ? i18n.translate('xpack.observability.overview.exploratoryView.missingReportDefinition', { defaultMessage: 'Missing {reportDefinition}', - values: { reportDefinition: labels?.[definitionFields[0]] }, + values: { + reportDefinition: + labels?.[ + typeof definitionFields[0] === 'string' + ? definitionFields[0] + : definitionFields[0].field + ], + }, }) : ''; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/report_definition_col.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/report_definition_col.tsx index fbd7c34303d94..a665ec1999133 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/report_definition_col.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/report_definition_col.tsx @@ -6,10 +6,13 @@ */ import React from 'react'; +import { isEmpty } from 'lodash'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { useSeriesStorage } from '../../hooks/use_series_storage'; import { SeriesConfig, SeriesUrl } from '../../types'; import { ReportDefinitionField } from './report_definition_field'; +import { isStepLevelMetric } from '../../configurations/synthetics/kpi_over_time_config'; +import { SYNTHETICS_STEP_NAME } from '../../configurations/constants/field_names/synthetics'; export function ReportDefinitionCol({ seriesId, @@ -41,19 +44,64 @@ export function ReportDefinitionCol({ } }; + const hasFieldDataSelected = (field: string) => { + return !isEmpty(series.reportDefinitions?.[field]); + }; + return ( - {definitionFields.map((field) => ( - - - - ))} + {definitionFields.map((field) => { + const fieldStr = typeof field === 'string' ? field : field.field; + const singleSelection = typeof field !== 'string' && field.singleSelection; + const nestedField = typeof field !== 'string' && field.nested; + const filters = typeof field !== 'string' ? field.filters : undefined; + + const isNonStepMetric = !isStepLevelMetric(series.selectedMetricField); + + const hideNestedStep = nestedField === SYNTHETICS_STEP_NAME && isNonStepMetric; + + if (hideNestedStep && nestedField && selectedReportDefinitions[nestedField]?.length > 0) { + setSeries(seriesId, { + ...series, + reportDefinitions: { ...selectedReportDefinitions, [nestedField]: [] }, + }); + } + + let nestedFieldElement; + + if (nestedField && hasFieldDataSelected(fieldStr) && !hideNestedStep) { + nestedFieldElement = ( + + + + ); + } + + return ( + <> + + + + {nestedFieldElement} + + ); + })} ); } diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/report_definition_field.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/report_definition_field.tsx index 01f36e85c03ae..f3e0eb767d336 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/report_definition_field.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/report_definition_field.tsx @@ -7,7 +7,7 @@ import React, { useMemo } from 'react'; import { isEmpty } from 'lodash'; -import { ExistsFilter } from '@kbn/es-query'; +import { ExistsFilter, PhraseFilter } from '@kbn/es-query'; import FieldValueSuggestions from '../../../field_value_suggestions'; import { useAppIndexPatternContext } from '../../hooks/use_app_index_pattern'; import { ESFilter } from '../../../../../../../../../src/core/types/elasticsearch'; @@ -19,32 +19,49 @@ import { ALL_VALUES_SELECTED } from '../../../field_value_suggestions/field_valu interface Props { seriesId: number; series: SeriesUrl; - field: string; + singleSelection?: boolean; + keepHistory?: boolean; + field: string | { field: string; nested: string }; seriesConfig: SeriesConfig; onChange: (field: string, value?: string[]) => void; + filters?: Array; } -export function ReportDefinitionField({ series, field, seriesConfig, onChange }: Props) { +export function ReportDefinitionField({ + singleSelection, + keepHistory, + series, + field: fieldProp, + seriesConfig, + onChange, + filters, +}: Props) { const { indexPattern } = useAppIndexPatternContext(series.dataType); + const field = typeof fieldProp === 'string' ? fieldProp : fieldProp.field; + const { reportDefinitions: selectedReportDefinitions = {} } = series; const { labels, baseFilters, definitionFields } = seriesConfig; const queryFilters = useMemo(() => { const filtersN: ESFilter[] = []; - (baseFilters ?? []).forEach((qFilter: PersistableFilter | ExistsFilter) => { - if (qFilter.query) { - filtersN.push(qFilter.query); - } - const existFilter = qFilter as ExistsFilter; - if (existFilter.query.exists) { - filtersN.push({ exists: existFilter.query.exists }); - } - }); + (baseFilters ?? []) + .concat(filters ?? []) + .forEach((qFilter: PersistableFilter | ExistsFilter) => { + if (qFilter.query) { + filtersN.push(qFilter.query); + } + const existFilter = qFilter as ExistsFilter; + if (existFilter.query.exists) { + filtersN.push({ exists: existFilter.query.exists }); + } + }); if (!isEmpty(selectedReportDefinitions)) { - definitionFields.forEach((fieldT) => { + definitionFields.forEach((fieldObj) => { + const fieldT = typeof fieldObj === 'string' ? fieldObj : fieldObj.field; + if (indexPattern && selectedReportDefinitions?.[fieldT] && fieldT !== field) { const values = selectedReportDefinitions?.[fieldT]; if (!values.includes(ALL_VALUES_SELECTED)) { @@ -65,7 +82,7 @@ export function ReportDefinitionField({ series, field, seriesConfig, onChange }: return ( ); } diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/expanded_series_row.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/expanded_series_row.tsx index 180be1ac0414f..12e0ceca20649 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/expanded_series_row.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/expanded_series_row.tsx @@ -48,13 +48,13 @@ export function ExpandedSeriesRow(seriesProps: Props) { return (
- - + + - + diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts index 001664cf12783..acd49fc25588e 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts @@ -56,7 +56,15 @@ export interface SeriesConfig { filterFields: Array; seriesTypes: SeriesType[]; baseFilters?: Array; - definitionFields: string[]; + definitionFields: Array< + | string + | { + field: string; + nested?: string; + singleSelection?: boolean; + filters?: Array; + } + >; metricOptions?: MetricOption[]; labels: Record; hasOperationType: boolean; diff --git a/x-pack/plugins/observability/public/components/shared/field_value_suggestions/field_value_combobox.tsx b/x-pack/plugins/observability/public/components/shared/field_value_suggestions/field_value_combobox.tsx index 0735df53888aa..e04d5463d5494 100644 --- a/x-pack/plugins/observability/public/components/shared/field_value_suggestions/field_value_combobox.tsx +++ b/x-pack/plugins/observability/public/components/shared/field_value_suggestions/field_value_combobox.tsx @@ -42,6 +42,7 @@ export function FieldValueCombobox({ usePrependLabel = true, compressed = true, required = true, + singleSelection = false, allowAllValuesSelection, onChange: onSelectionChange, }: FieldValueSelectionProps) { @@ -68,6 +69,7 @@ export function FieldValueCombobox({ const comboBox = ( ); diff --git a/x-pack/plugins/observability/public/components/shared/field_value_suggestions/types.ts b/x-pack/plugins/observability/public/components/shared/field_value_suggestions/types.ts index 6f6d520a83154..95b24aa69b1e7 100644 --- a/x-pack/plugins/observability/public/components/shared/field_value_suggestions/types.ts +++ b/x-pack/plugins/observability/public/components/shared/field_value_suggestions/types.ts @@ -29,6 +29,7 @@ interface CommonProps { allowAllValuesSelection?: boolean; cardinalityField?: string; required?: boolean; + keepHistory?: boolean; } export type FieldValueSuggestionsProps = CommonProps & { diff --git a/x-pack/plugins/uptime/public/components/common/header/action_menu_content.tsx b/x-pack/plugins/uptime/public/components/common/header/action_menu_content.tsx index 21ef3428696e9..bcfbf18c93cf5 100644 --- a/x-pack/plugins/uptime/public/components/common/header/action_menu_content.tsx +++ b/x-pack/plugins/uptime/public/components/common/header/action_menu_content.tsx @@ -56,9 +56,8 @@ export function ActionMenuContent(): React.ReactElement { time: { from: dateRangeStart, to: dateRangeEnd }, breakdown: monitorId ? 'observer.geo.name' : 'monitor.type', reportDefinitions: { - 'monitor.name': selectedMonitor?.monitor?.name - ? [selectedMonitor?.monitor?.name] - : ['ALL_VALUES'], + 'monitor.name': selectedMonitor?.monitor?.name ? [selectedMonitor?.monitor?.name] : [], + 'url.full': ['ALL_VALUES'], }, name: monitorId ? `${monitorId}-response-duration` : 'All monitors response duration', }, From 85d7115d4a3051846655e46575b3558530491fd0 Mon Sep 17 00:00:00 2001 From: Rich Kuzsma <62522248+richkuz@users.noreply.github.com> Date: Mon, 18 Oct 2021 17:19:00 -0400 Subject: [PATCH 014/204] Document edge cases for enterpriseSearch.host (#115446) Fixes https://github.com/elastic/enterprise-search-team/issues/517 --- docs/setup/settings.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index 16fa8eb734204..4802a4da8182c 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -275,7 +275,7 @@ that the {kib} server uses to perform maintenance on the {kib} index at startup. is an alternative to `elasticsearch.username` and `elasticsearch.password`. | `enterpriseSearch.host` - | The URL of your Enterprise Search instance + | The http(s) URL of your Enterprise Search instance. For example, in a local self-managed setup, set this to `http://localhost:3002`. Authentication between Kibana and the Enterprise Search host URL, such as via OAuth, is not supported. You can also {enterprise-search-ref}/configure-ssl-tls.html#configure-ssl-tls-in-kibana[configure Kibana to trust your Enterprise Search TLS certificate authority]. | `interpreter.enableInVisualize` | Enables use of interpreter in Visualize. *Default: `true`* From eb5ffff7d09f53880d24f5d6d43289afe1eee2d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ester=20Mart=C3=AD=20Vilaseca?= Date: Mon, 18 Oct 2021 23:25:01 +0200 Subject: [PATCH 015/204] Clean angular from moved code, global state and legacy shims (#115420) --- .../public/alerts/alert_form.test.tsx | 18 +- .../public/angular/angular_i18n/directive.ts | 103 ------ .../public/angular/angular_i18n/filter.ts | 19 - .../public/angular/angular_i18n/index.ts | 15 - .../public/angular/angular_i18n/provider.ts | 25 -- .../helpers/format_angular_http_error.ts | 43 --- .../public/angular/top_nav/angular_config.tsx | 349 ------------------ .../public/angular/top_nav/index.ts | 10 - .../public/angular/top_nav/kbn_top_nav.d.ts | 16 - .../public/angular/top_nav/kbn_top_nav.js | 119 ------ .../contexts/global_state_context.tsx | 25 +- .../plugins/monitoring/public/legacy_shims.ts | 25 +- x-pack/plugins/monitoring/public/url_state.ts | 34 -- 13 files changed, 18 insertions(+), 783 deletions(-) delete mode 100644 x-pack/plugins/monitoring/public/angular/angular_i18n/directive.ts delete mode 100644 x-pack/plugins/monitoring/public/angular/angular_i18n/filter.ts delete mode 100644 x-pack/plugins/monitoring/public/angular/angular_i18n/index.ts delete mode 100644 x-pack/plugins/monitoring/public/angular/angular_i18n/provider.ts delete mode 100644 x-pack/plugins/monitoring/public/angular/helpers/format_angular_http_error.ts delete mode 100644 x-pack/plugins/monitoring/public/angular/top_nav/angular_config.tsx delete mode 100644 x-pack/plugins/monitoring/public/angular/top_nav/index.ts delete mode 100644 x-pack/plugins/monitoring/public/angular/top_nav/kbn_top_nav.d.ts delete mode 100644 x-pack/plugins/monitoring/public/angular/top_nav/kbn_top_nav.js diff --git a/x-pack/plugins/monitoring/public/alerts/alert_form.test.tsx b/x-pack/plugins/monitoring/public/alerts/alert_form.test.tsx index 8752a4118fb6a..1a44beab260cb 100644 --- a/x-pack/plugins/monitoring/public/alerts/alert_form.test.tsx +++ b/x-pack/plugins/monitoring/public/alerts/alert_form.test.tsx @@ -50,17 +50,13 @@ const initLegacyShims = () => { ruleTypeRegistry: ruleTypeRegistryMock.create(), }; const data = { query: { timefilter: { timefilter: {} } } } as any; - const ngInjector = {} as angular.auto.IInjectorService; - Legacy.init( - { - core: coreMock.createStart(), - data, - isCloud: false, - triggersActionsUi, - usageCollection: {}, - } as any, - ngInjector - ); + Legacy.init({ + core: coreMock.createStart(), + data, + isCloud: false, + triggersActionsUi, + usageCollection: {}, + } as any); }; const ALERTS_FEATURE_ID = 'alerts'; diff --git a/x-pack/plugins/monitoring/public/angular/angular_i18n/directive.ts b/x-pack/plugins/monitoring/public/angular/angular_i18n/directive.ts deleted file mode 100644 index 1aaff99a6a5c1..0000000000000 --- a/x-pack/plugins/monitoring/public/angular/angular_i18n/directive.ts +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { IDirective, IRootElementService, IScope } from 'angular'; - -import { I18nServiceType } from './provider'; - -interface I18nScope extends IScope { - values?: Record; - defaultMessage: string; - id: string; -} - -const HTML_KEY_PREFIX = 'html_'; -const PLACEHOLDER_SEPARATOR = '@I18N@'; - -export const i18nDirective: [string, string, typeof i18nDirectiveFn] = [ - 'i18n', - '$sanitize', - i18nDirectiveFn, -]; - -function i18nDirectiveFn( - i18n: I18nServiceType, - $sanitize: (html: string) => string -): IDirective { - return { - restrict: 'A', - scope: { - id: '@i18nId', - defaultMessage: '@i18nDefaultMessage', - values: ' { - setContent($element, $scope, $sanitize, i18n); - }); - } else { - setContent($element, $scope, $sanitize, i18n); - } - }, - }; -} - -function setContent( - $element: IRootElementService, - $scope: I18nScope, - $sanitize: (html: string) => string, - i18n: I18nServiceType -) { - const originalValues = $scope.values; - const valuesWithPlaceholders = {} as Record; - let hasValuesWithPlaceholders = false; - - // If we have values with the keys that start with HTML_KEY_PREFIX we should replace - // them with special placeholders that later on will be inserted as HTML - // into the DOM, the rest of the content will be treated as text. We don't - // sanitize values at this stage as some of the values can be excluded from - // the translated string (e.g. not used by ICU conditional statements). - if (originalValues) { - for (const [key, value] of Object.entries(originalValues)) { - if (key.startsWith(HTML_KEY_PREFIX)) { - valuesWithPlaceholders[ - key.slice(HTML_KEY_PREFIX.length) - ] = `${PLACEHOLDER_SEPARATOR}${key}${PLACEHOLDER_SEPARATOR}`; - - hasValuesWithPlaceholders = true; - } else { - valuesWithPlaceholders[key] = value; - } - } - } - - const label = i18n($scope.id, { - values: valuesWithPlaceholders, - defaultMessage: $scope.defaultMessage, - }); - - // If there are no placeholders to replace treat everything as text, otherwise - // insert label piece by piece replacing every placeholder with corresponding - // sanitized HTML content. - if (!hasValuesWithPlaceholders) { - $element.text(label); - } else { - $element.empty(); - for (const contentOrPlaceholder of label.split(PLACEHOLDER_SEPARATOR)) { - if (!contentOrPlaceholder) { - continue; - } - - $element.append( - originalValues!.hasOwnProperty(contentOrPlaceholder) - ? $sanitize(originalValues![contentOrPlaceholder]) - : document.createTextNode(contentOrPlaceholder) - ); - } - } -} diff --git a/x-pack/plugins/monitoring/public/angular/angular_i18n/filter.ts b/x-pack/plugins/monitoring/public/angular/angular_i18n/filter.ts deleted file mode 100644 index e4e553fa47b6f..0000000000000 --- a/x-pack/plugins/monitoring/public/angular/angular_i18n/filter.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { I18nServiceType } from './provider'; - -export const i18nFilter: [string, typeof i18nFilterFn] = ['i18n', i18nFilterFn]; - -function i18nFilterFn(i18n: I18nServiceType) { - return (id: string, { defaultMessage = '', values = {} } = {}) => { - return i18n(id, { - values, - defaultMessage, - }); - }; -} diff --git a/x-pack/plugins/monitoring/public/angular/angular_i18n/index.ts b/x-pack/plugins/monitoring/public/angular/angular_i18n/index.ts deleted file mode 100644 index 8915c96e59be0..0000000000000 --- a/x-pack/plugins/monitoring/public/angular/angular_i18n/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export { I18nProvider } from './provider'; - -export { i18nFilter } from './filter'; -export { i18nDirective } from './directive'; - -// re-export types: https://github.com/babel/babel-loader/issues/603 -import { I18nServiceType as _I18nServiceType } from './provider'; -export type I18nServiceType = _I18nServiceType; diff --git a/x-pack/plugins/monitoring/public/angular/angular_i18n/provider.ts b/x-pack/plugins/monitoring/public/angular/angular_i18n/provider.ts deleted file mode 100644 index b1da1bad6e399..0000000000000 --- a/x-pack/plugins/monitoring/public/angular/angular_i18n/provider.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; - -export type I18nServiceType = ReturnType; - -export class I18nProvider implements angular.IServiceProvider { - public addTranslation = i18n.addTranslation; - public getTranslation = i18n.getTranslation; - public setLocale = i18n.setLocale; - public getLocale = i18n.getLocale; - public setDefaultLocale = i18n.setDefaultLocale; - public getDefaultLocale = i18n.getDefaultLocale; - public setFormats = i18n.setFormats; - public getFormats = i18n.getFormats; - public getRegisteredLocales = i18n.getRegisteredLocales; - public init = i18n.init; - public load = i18n.load; - public $get = () => i18n.translate; -} diff --git a/x-pack/plugins/monitoring/public/angular/helpers/format_angular_http_error.ts b/x-pack/plugins/monitoring/public/angular/helpers/format_angular_http_error.ts deleted file mode 100644 index abdcf157a3c86..0000000000000 --- a/x-pack/plugins/monitoring/public/angular/helpers/format_angular_http_error.ts +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; -import type { IHttpResponse } from 'angular'; - -type AngularHttpError = IHttpResponse<{ message: string }>; - -export function isAngularHttpError(error: any): error is AngularHttpError { - return ( - error && - typeof error.status === 'number' && - typeof error.statusText === 'string' && - error.data && - typeof error.data.message === 'string' - ); -} - -export function formatAngularHttpError(error: AngularHttpError) { - // is an Angular $http "error object" - if (error.status === -1) { - // status = -1 indicates that the request was failed to reach the server - return i18n.translate('xpack.monitoring.notify.fatalError.unavailableServerErrorMessage', { - defaultMessage: - 'An HTTP request has failed to connect. ' + - 'Please check if the Kibana server is running and that your browser has a working connection, ' + - 'or contact your system administrator.', - }); - } - - return i18n.translate('xpack.monitoring.notify.fatalError.errorStatusMessage', { - defaultMessage: 'Error {errStatus} {errStatusText}: {errMessage}', - values: { - errStatus: error.status, - errStatusText: error.statusText, - errMessage: error.data.message, - }, - }); -} diff --git a/x-pack/plugins/monitoring/public/angular/top_nav/angular_config.tsx b/x-pack/plugins/monitoring/public/angular/top_nav/angular_config.tsx deleted file mode 100644 index 9c2e931d24a94..0000000000000 --- a/x-pack/plugins/monitoring/public/angular/top_nav/angular_config.tsx +++ /dev/null @@ -1,349 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { - ICompileProvider, - IHttpProvider, - IHttpService, - ILocationProvider, - IModule, - IRootScopeService, - IRequestConfig, -} from 'angular'; -import $ from 'jquery'; -import { set } from '@elastic/safer-lodash-set'; -import { get } from 'lodash'; -import * as Rx from 'rxjs'; -import { ChromeBreadcrumb, EnvironmentMode, PackageInfo } from 'kibana/public'; -import { History } from 'history'; - -import { CoreStart } from 'kibana/public'; -import { formatAngularHttpError, isAngularHttpError } from '../helpers/format_angular_http_error'; - -export interface RouteConfiguration { - controller?: string | ((...args: any[]) => void); - redirectTo?: string; - resolveRedirectTo?: (...args: any[]) => void; - reloadOnSearch?: boolean; - reloadOnUrl?: boolean; - outerAngularWrapperRoute?: boolean; - resolve?: object; - template?: string; - k7Breadcrumbs?: (...args: any[]) => ChromeBreadcrumb[]; - requireUICapability?: string; -} - -function isSystemApiRequest(request: IRequestConfig) { - const { headers } = request; - return headers && !!headers['kbn-system-request']; -} - -/** - * Detects whether a given angular route is a dummy route that doesn't - * require any action. There are two ways this can happen: - * If `outerAngularWrapperRoute` is set on the route config object, - * it means the local application service set up this route on the outer angular - * and the internal routes will handle the hooks. - * - * If angular did not detect a route and it is the local angular, we are currently - * navigating away from a URL controlled by a local angular router and the - * application will get unmounted. In this case the outer router will handle - * the hooks. - * @param $route Injected $route dependency - * @param isLocalAngular Flag whether this is the local angular router - */ -function isDummyRoute($route: any, isLocalAngular: boolean) { - return ( - ($route.current && $route.current.$$route && $route.current.$$route.outerAngularWrapperRoute) || - (!$route.current && isLocalAngular) - ); -} - -export const configureAppAngularModule = ( - angularModule: IModule, - newPlatform: { - core: CoreStart; - readonly env: { - mode: Readonly; - packageInfo: Readonly; - }; - }, - isLocalAngular: boolean, - getHistory?: () => History -) => { - const core = 'core' in newPlatform ? newPlatform.core : newPlatform; - const packageInfo = newPlatform.env.packageInfo; - - angularModule - .value('kbnVersion', packageInfo.version) - .value('buildNum', packageInfo.buildNum) - .value('buildSha', packageInfo.buildSha) - .value('esUrl', getEsUrl(core)) - .value('uiCapabilities', core.application.capabilities) - .config(setupCompileProvider(newPlatform.env.mode.dev)) - .config(setupLocationProvider()) - .config($setupXsrfRequestInterceptor(packageInfo.version)) - .run(capture$httpLoadingCount(core)) - .run(digestOnHashChange(getHistory)) - .run($setupBreadcrumbsAutoClear(core, isLocalAngular)) - .run($setupBadgeAutoClear(core, isLocalAngular)) - .run($setupHelpExtensionAutoClear(core, isLocalAngular)) - .run($setupUICapabilityRedirect(core)); -}; - -const getEsUrl = (newPlatform: CoreStart) => { - const a = document.createElement('a'); - a.href = newPlatform.http.basePath.prepend('/elasticsearch'); - const protocolPort = /https/.test(a.protocol) ? 443 : 80; - const port = a.port || protocolPort; - return { - host: a.hostname, - port, - protocol: a.protocol, - pathname: a.pathname, - }; -}; - -const digestOnHashChange = (getHistory?: () => History) => ($rootScope: IRootScopeService) => { - if (!getHistory) return; - const unlisten = getHistory().listen(() => { - // dispatch synthetic hash change event to update hash history objects and angular routing - // this is necessary because hash updates triggered by using popState won't trigger this event naturally. - // this has to happen in the next tick to not change the existing timing of angular digest cycles. - setTimeout(() => { - window.dispatchEvent(new HashChangeEvent('hashchange')); - }, 0); - }); - $rootScope.$on('$destroy', unlisten); -}; - -const setupCompileProvider = (devMode: boolean) => ($compileProvider: ICompileProvider) => { - if (!devMode) { - $compileProvider.debugInfoEnabled(false); - } -}; - -const setupLocationProvider = () => ($locationProvider: ILocationProvider) => { - $locationProvider.html5Mode({ - enabled: false, - requireBase: false, - rewriteLinks: false, - }); - - $locationProvider.hashPrefix(''); -}; - -export const $setupXsrfRequestInterceptor = (version: string) => { - // Configure jQuery prefilter - $.ajaxPrefilter(({ kbnXsrfToken = true }: any, originalOptions, jqXHR) => { - if (kbnXsrfToken) { - jqXHR.setRequestHeader('kbn-version', version); - } - }); - - return ($httpProvider: IHttpProvider) => { - // Configure $httpProvider interceptor - $httpProvider.interceptors.push(() => { - return { - request(opts) { - const { kbnXsrfToken = true } = opts as any; - if (kbnXsrfToken) { - set(opts, ['headers', 'kbn-version'], version); - } - return opts; - }, - }; - }); - }; -}; - -/** - * Injected into angular module by ui/chrome angular integration - * and adds a root-level watcher that will capture the count of - * active $http requests on each digest loop and expose the count to - * the core.loadingCount api - */ -const capture$httpLoadingCount = - (newPlatform: CoreStart) => ($rootScope: IRootScopeService, $http: IHttpService) => { - newPlatform.http.addLoadingCountSource( - new Rx.Observable((observer) => { - const unwatch = $rootScope.$watch(() => { - const reqs = $http.pendingRequests || []; - observer.next(reqs.filter((req) => !isSystemApiRequest(req)).length); - }); - - return unwatch; - }) - ); - }; - -/** - * integrates with angular to automatically redirect to home if required - * capability is not met - */ -const $setupUICapabilityRedirect = - (newPlatform: CoreStart) => ($rootScope: IRootScopeService, $injector: any) => { - const isKibanaAppRoute = window.location.pathname.endsWith('/app/kibana'); - // this feature only works within kibana app for now after everything is - // switched to the application service, this can be changed to handle all - // apps. - if (!isKibanaAppRoute) { - return; - } - $rootScope.$on( - '$routeChangeStart', - (event, { $$route: route }: { $$route?: RouteConfiguration } = {}) => { - if (!route || !route.requireUICapability) { - return; - } - - if (!get(newPlatform.application.capabilities, route.requireUICapability)) { - $injector.get('$location').url('/home'); - event.preventDefault(); - } - } - ); - }; - -/** - * internal angular run function that will be called when angular bootstraps and - * lets us integrate with the angular router so that we can automatically clear - * the breadcrumbs if we switch to a Kibana app that does not use breadcrumbs correctly - */ -const $setupBreadcrumbsAutoClear = - (newPlatform: CoreStart, isLocalAngular: boolean) => - ($rootScope: IRootScopeService, $injector: any) => { - // A flag used to determine if we should automatically - // clear the breadcrumbs between angular route changes. - let breadcrumbSetSinceRouteChange = false; - const $route = $injector.has('$route') ? $injector.get('$route') : {}; - - // reset breadcrumbSetSinceRouteChange any time the breadcrumbs change, even - // if it was done directly through the new platform - newPlatform.chrome.getBreadcrumbs$().subscribe({ - next() { - breadcrumbSetSinceRouteChange = true; - }, - }); - - $rootScope.$on('$routeChangeStart', () => { - breadcrumbSetSinceRouteChange = false; - }); - - $rootScope.$on('$routeChangeSuccess', () => { - if (isDummyRoute($route, isLocalAngular)) { - return; - } - const current = $route.current || {}; - - if (breadcrumbSetSinceRouteChange || (current.$$route && current.$$route.redirectTo)) { - return; - } - - const k7BreadcrumbsProvider = current.k7Breadcrumbs; - if (!k7BreadcrumbsProvider) { - newPlatform.chrome.setBreadcrumbs([]); - return; - } - - try { - newPlatform.chrome.setBreadcrumbs($injector.invoke(k7BreadcrumbsProvider)); - } catch (error) { - if (isAngularHttpError(error)) { - error = formatAngularHttpError(error); - } - newPlatform.fatalErrors.add(error, 'location'); - } - }); - }; - -/** - * internal angular run function that will be called when angular bootstraps and - * lets us integrate with the angular router so that we can automatically clear - * the badge if we switch to a Kibana app that does not use the badge correctly - */ -const $setupBadgeAutoClear = - (newPlatform: CoreStart, isLocalAngular: boolean) => - ($rootScope: IRootScopeService, $injector: any) => { - // A flag used to determine if we should automatically - // clear the badge between angular route changes. - let badgeSetSinceRouteChange = false; - const $route = $injector.has('$route') ? $injector.get('$route') : {}; - - $rootScope.$on('$routeChangeStart', () => { - badgeSetSinceRouteChange = false; - }); - - $rootScope.$on('$routeChangeSuccess', () => { - if (isDummyRoute($route, isLocalAngular)) { - return; - } - const current = $route.current || {}; - - if (badgeSetSinceRouteChange || (current.$$route && current.$$route.redirectTo)) { - return; - } - - const badgeProvider = current.badge; - if (!badgeProvider) { - newPlatform.chrome.setBadge(undefined); - return; - } - - try { - newPlatform.chrome.setBadge($injector.invoke(badgeProvider)); - } catch (error) { - if (isAngularHttpError(error)) { - error = formatAngularHttpError(error); - } - newPlatform.fatalErrors.add(error, 'location'); - } - }); - }; - -/** - * internal angular run function that will be called when angular bootstraps and - * lets us integrate with the angular router so that we can automatically clear - * the helpExtension if we switch to a Kibana app that does not set its own - * helpExtension - */ -const $setupHelpExtensionAutoClear = - (newPlatform: CoreStart, isLocalAngular: boolean) => - ($rootScope: IRootScopeService, $injector: any) => { - /** - * reset helpExtensionSetSinceRouteChange any time the helpExtension changes, even - * if it was done directly through the new platform - */ - let helpExtensionSetSinceRouteChange = false; - newPlatform.chrome.getHelpExtension$().subscribe({ - next() { - helpExtensionSetSinceRouteChange = true; - }, - }); - - const $route = $injector.has('$route') ? $injector.get('$route') : {}; - - $rootScope.$on('$routeChangeStart', () => { - if (isDummyRoute($route, isLocalAngular)) { - return; - } - helpExtensionSetSinceRouteChange = false; - }); - - $rootScope.$on('$routeChangeSuccess', () => { - if (isDummyRoute($route, isLocalAngular)) { - return; - } - const current = $route.current || {}; - - if (helpExtensionSetSinceRouteChange || (current.$$route && current.$$route.redirectTo)) { - return; - } - - newPlatform.chrome.setHelpExtension(current.helpExtension); - }); - }; diff --git a/x-pack/plugins/monitoring/public/angular/top_nav/index.ts b/x-pack/plugins/monitoring/public/angular/top_nav/index.ts deleted file mode 100644 index b3501e4cbad1f..0000000000000 --- a/x-pack/plugins/monitoring/public/angular/top_nav/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export * from './angular_config'; -// @ts-ignore -export { createTopNavDirective, createTopNavHelper, loadKbnTopNavDirectives } from './kbn_top_nav'; diff --git a/x-pack/plugins/monitoring/public/angular/top_nav/kbn_top_nav.d.ts b/x-pack/plugins/monitoring/public/angular/top_nav/kbn_top_nav.d.ts deleted file mode 100644 index 0cff77241bb9c..0000000000000 --- a/x-pack/plugins/monitoring/public/angular/top_nav/kbn_top_nav.d.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { Injectable, IDirectiveFactory, IScope, IAttributes, IController } from 'angular'; - -export const createTopNavDirective: Injectable< - IDirectiveFactory ->; -export const createTopNavHelper: ( - options: unknown -) => Injectable>; -export function loadKbnTopNavDirectives(navUi: unknown): void; diff --git a/x-pack/plugins/monitoring/public/angular/top_nav/kbn_top_nav.js b/x-pack/plugins/monitoring/public/angular/top_nav/kbn_top_nav.js deleted file mode 100644 index 6edcca6aa714a..0000000000000 --- a/x-pack/plugins/monitoring/public/angular/top_nav/kbn_top_nav.js +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import angular from 'angular'; -import 'ngreact'; - -export function createTopNavDirective() { - return { - restrict: 'E', - template: '', - compile: (elem) => { - const child = document.createElement('kbn-top-nav-helper'); - - // Copy attributes to the child directive - for (const attr of elem[0].attributes) { - child.setAttribute(attr.name, attr.value); - } - - // Add a special attribute that will change every time that one - // of the config array's disableButton function return value changes. - child.setAttribute('disabled-buttons', 'disabledButtons'); - - // Append helper directive - elem.append(child); - - const linkFn = ($scope, _, $attr) => { - // Watch config changes - $scope.$watch( - () => { - const config = $scope.$eval($attr.config) || []; - return config.map((item) => { - // Copy key into id, as it's a reserved react propery. - // This is done for Angular directive backward compatibility. - // In React only id is recognized. - if (item.key && !item.id) { - item.id = item.key; - } - - // Watch the disableButton functions - if (typeof item.disableButton === 'function') { - return item.disableButton(); - } - return item.disableButton; - }); - }, - (newVal) => { - $scope.disabledButtons = newVal; - }, - true - ); - }; - - return linkFn; - }, - }; -} - -export const createTopNavHelper = - ({ TopNavMenu }) => - (reactDirective) => { - return reactDirective(TopNavMenu, [ - ['config', { watchDepth: 'value' }], - ['setMenuMountPoint', { watchDepth: 'reference' }], - ['disabledButtons', { watchDepth: 'reference' }], - - ['query', { watchDepth: 'reference' }], - ['savedQuery', { watchDepth: 'reference' }], - ['intl', { watchDepth: 'reference' }], - - ['onQuerySubmit', { watchDepth: 'reference' }], - ['onFiltersUpdated', { watchDepth: 'reference' }], - ['onRefreshChange', { watchDepth: 'reference' }], - ['onClearSavedQuery', { watchDepth: 'reference' }], - ['onSaved', { watchDepth: 'reference' }], - ['onSavedQueryUpdated', { watchDepth: 'reference' }], - ['onSavedQueryIdChange', { watchDepth: 'reference' }], - - ['indexPatterns', { watchDepth: 'collection' }], - ['filters', { watchDepth: 'collection' }], - - // All modifiers default to true. - // Set to false to hide subcomponents. - 'showSearchBar', - 'showQueryBar', - 'showQueryInput', - 'showSaveQuery', - 'showDatePicker', - 'showFilterBar', - - 'appName', - 'screenTitle', - 'dateRangeFrom', - 'dateRangeTo', - 'savedQueryId', - 'isRefreshPaused', - 'refreshInterval', - 'disableAutoFocus', - 'showAutoRefreshOnly', - - // temporary flag to use the stateful components - 'useDefaultBehaviors', - ]); - }; - -let isLoaded = false; - -export function loadKbnTopNavDirectives(navUi) { - if (!isLoaded) { - isLoaded = true; - angular - .module('kibana') - .directive('kbnTopNav', createTopNavDirective) - .directive('kbnTopNavHelper', createTopNavHelper(navUi)); - } -} diff --git a/x-pack/plugins/monitoring/public/application/contexts/global_state_context.tsx b/x-pack/plugins/monitoring/public/application/contexts/global_state_context.tsx index e6638b4c4fede..cc8619dbc7ad2 100644 --- a/x-pack/plugins/monitoring/public/application/contexts/global_state_context.tsx +++ b/x-pack/plugins/monitoring/public/application/contexts/global_state_context.tsx @@ -32,31 +32,8 @@ export const GlobalStateProvider: React.FC = ({ toasts, children, }) => { - // TODO: remove fakeAngularRootScope and fakeAngularLocation when angular is removed - const fakeAngularRootScope: Partial = { - $on: - (name: string, listener: (event: ng.IAngularEvent, ...args: any[]) => any): (() => void) => - () => {}, - $applyAsync: () => {}, - }; - - const fakeAngularLocation: Partial = { - search: () => { - return {} as any; - }, - replace: () => { - return {} as any; - }, - }; - const localState: State = {}; - const state = new GlobalState( - query, - toasts, - fakeAngularRootScope, - fakeAngularLocation, - localState as { [key: string]: unknown } - ); + const state = new GlobalState(query, toasts, localState as { [key: string]: unknown }); const initialState: any = state.getState(); for (const key in initialState) { diff --git a/x-pack/plugins/monitoring/public/legacy_shims.ts b/x-pack/plugins/monitoring/public/legacy_shims.ts index 72d50aac1dbb8..48484421839bd 100644 --- a/x-pack/plugins/monitoring/public/legacy_shims.ts +++ b/x-pack/plugins/monitoring/public/legacy_shims.ts @@ -44,7 +44,7 @@ const angularNoop = () => { export interface IShims { toastNotifications: CoreStart['notifications']['toasts']; capabilities: CoreStart['application']['capabilities']; - getAngularInjector: typeof angularNoop | (() => angular.auto.IInjectorService); + getAngularInjector: typeof angularNoop; getBasePath: () => string; getInjected: (name: string, defaultValue?: unknown) => unknown; breadcrumbs: { @@ -73,23 +73,18 @@ export interface IShims { export class Legacy { private static _shims: IShims; - public static init( - { - core, - data, - isCloud, - triggersActionsUi, - usageCollection, - appMountParameters, - }: MonitoringStartPluginDependencies, - ngInjector?: angular.auto.IInjectorService - ) { + public static init({ + core, + data, + isCloud, + triggersActionsUi, + usageCollection, + appMountParameters, + }: MonitoringStartPluginDependencies) { this._shims = { toastNotifications: core.notifications.toasts, capabilities: core.application.capabilities, - getAngularInjector: ngInjector - ? (): angular.auto.IInjectorService => ngInjector - : angularNoop, + getAngularInjector: angularNoop, getBasePath: (): string => core.http.basePath.get(), getInjected: (name: string, defaultValue?: unknown): string | unknown => core.injectedMetadata.getInjectedVar(name, defaultValue), diff --git a/x-pack/plugins/monitoring/public/url_state.ts b/x-pack/plugins/monitoring/public/url_state.ts index 25086411c65a3..8f89df732b800 100644 --- a/x-pack/plugins/monitoring/public/url_state.ts +++ b/x-pack/plugins/monitoring/public/url_state.ts @@ -4,7 +4,6 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - import { Subscription } from 'rxjs'; import { History, createHashHistory } from 'history'; import { MonitoringStartPluginDependencies } from './types'; @@ -27,10 +26,6 @@ import { withNotifyOnErrors, } from '../../../../src/plugins/kibana_utils/public'; -interface Route { - params: { _g: unknown }; -} - interface RawObject { [key: string]: unknown; } @@ -57,7 +52,6 @@ export interface MonitoringAppStateTransitions { const GLOBAL_STATE_KEY = '_g'; const objectEquals = (objA: any, objB: any) => JSON.stringify(objA) === JSON.stringify(objB); -// TODO: clean all angular references after angular is removed export class GlobalState { private readonly stateSyncRef: ISyncStateRef; private readonly stateContainer: StateContainer< @@ -70,13 +64,10 @@ export class GlobalState { private readonly timefilterRef: MonitoringStartPluginDependencies['data']['query']['timefilter']['timefilter']; private lastAssignedState: MonitoringAppState = {}; - private lastKnownGlobalState?: string; constructor( queryService: MonitoringStartPluginDependencies['data']['query'], toasts: MonitoringStartPluginDependencies['core']['notifications']['toasts'], - rootScope: Partial, - ngLocation: Partial, externalState: RawObject ) { this.timefilterRef = queryService.timefilter.timefilter; @@ -102,9 +93,6 @@ export class GlobalState { this.stateContainerChangeSub = this.stateContainer.state$.subscribe(() => { this.lastAssignedState = this.getState(); - if (!this.stateContainer.get() && this.lastKnownGlobalState) { - ngLocation.search?.(`${GLOBAL_STATE_KEY}=${this.lastKnownGlobalState}`).replace(); - } // TODO: check if this is not needed after https://github.com/elastic/kibana/pull/109132 is merged if (Legacy.isInitializated()) { @@ -112,15 +100,11 @@ export class GlobalState { } this.syncExternalState(externalState); - rootScope.$applyAsync?.(); }); this.syncQueryStateWithUrlManager = syncQueryStateWithUrl(queryService, this.stateStorage); this.stateSyncRef.start(); - this.startHashSync(rootScope, ngLocation); this.lastAssignedState = this.getState(); - - rootScope.$on?.('$destroy', () => this.destroy()); } private syncExternalState(externalState: { [key: string]: unknown }) { @@ -137,24 +121,6 @@ export class GlobalState { } } - private startHashSync( - rootScope: Partial, - ngLocation: Partial - ) { - rootScope.$on?.( - '$routeChangeStart', - (_: { preventDefault: () => void }, newState: Route, oldState: Route) => { - const currentGlobalState = oldState?.params?._g; - const nextGlobalState = newState?.params?._g; - if (!nextGlobalState && currentGlobalState && typeof currentGlobalState === 'string') { - newState.params._g = currentGlobalState; - ngLocation.search?.(`${GLOBAL_STATE_KEY}=${currentGlobalState}`).replace(); - } - this.lastKnownGlobalState = (nextGlobalState || currentGlobalState) as string; - } - ); - } - public setState(state?: { [key: string]: unknown }) { const currentAppState = this.getState(); const newAppState = { ...currentAppState, ...state }; From 70bd56a04fd30e0ac8fb0c55ddfec7e3c11d9349 Mon Sep 17 00:00:00 2001 From: Vadim Yakhin Date: Mon, 18 Oct 2021 14:50:59 -0700 Subject: [PATCH 016/204] [Fleet, App search] Add App Search ingestion methods to the unified integrations view (#115433) * Add a new category for Web crawler * Add App Search integrations * Fix isBeta flag for Web Crawler It's already GA --- .../custom_integrations/common/index.ts | 1 + .../enterprise_search/server/integrations.ts | 63 +++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/src/plugins/custom_integrations/common/index.ts b/src/plugins/custom_integrations/common/index.ts index 944ac6ba3e6ee..98148bb22c816 100755 --- a/src/plugins/custom_integrations/common/index.ts +++ b/src/plugins/custom_integrations/common/index.ts @@ -49,6 +49,7 @@ export const INTEGRATION_CATEGORY_DISPLAY = { project_management: 'Project Management', software_development: 'Software Development', upload_file: 'Upload a file', + website_search: 'Website Search', }; /** diff --git a/x-pack/plugins/enterprise_search/server/integrations.ts b/x-pack/plugins/enterprise_search/server/integrations.ts index 48909261243e8..eee5cdc3aaec3 100644 --- a/x-pack/plugins/enterprise_search/server/integrations.ts +++ b/x-pack/plugins/enterprise_search/server/integrations.ts @@ -301,4 +301,67 @@ export const registerEnterpriseSearchIntegrations = ( ...integration, }); }); + + customIntegrations.registerCustomIntegration({ + id: 'app_search_web_crawler', + title: i18n.translate('xpack.enterpriseSearch.appSearch.integrations.webCrawlerName', { + defaultMessage: 'Web Crawler', + }), + description: i18n.translate( + 'xpack.enterpriseSearch.appSearch.integrations.webCrawlerDescription', + { + defaultMessage: "Add search to your website with App Search's web crawler.", + } + ), + categories: ['website_search'], + uiInternalPath: '/app/enterprise_search/app_search/engines/new?method=crawler', + icons: [ + { + type: 'eui', + src: 'globe', + }, + ], + shipper: 'enterprise_search', + isBeta: false, + }); + + customIntegrations.registerCustomIntegration({ + id: 'app_search_json', + title: i18n.translate('xpack.enterpriseSearch.appSearch.integrations.jsonName', { + defaultMessage: 'JSON', + }), + description: i18n.translate('xpack.enterpriseSearch.appSearch.integrations.jsonDescription', { + defaultMessage: 'Search over your JSON data with App Search.', + }), + categories: ['upload_file'], + uiInternalPath: '/app/enterprise_search/app_search/engines/new?method=json', + icons: [ + { + type: 'eui', + src: 'exportAction', + }, + ], + shipper: 'enterprise_search', + isBeta: false, + }); + + customIntegrations.registerCustomIntegration({ + id: 'app_search_api', + title: i18n.translate('xpack.enterpriseSearch.appSearch.integrations.apiName', { + defaultMessage: 'API', + }), + description: i18n.translate('xpack.enterpriseSearch.appSearch.integrations.apiDescription', { + defaultMessage: "Add search to your application with App Search's robust APIs.", + }), + categories: ['custom'], + uiInternalPath: '/app/enterprise_search/app_search/engines/new?method=api', + icons: [ + { + type: 'eui', + src: 'editorCodeBlock', + }, + ], + shipper: 'enterprise_search', + isBeta: false, + }); }; From 62f057dee12a9f2f2693c249ddbf3f5e93ee6ca8 Mon Sep 17 00:00:00 2001 From: Walter Rafelsberger Date: Tue, 19 Oct 2021 00:12:07 +0200 Subject: [PATCH 017/204] [ML] APM Correlations: Get trace samples tab overall distribution via APM endpoint. (#114615) This creates an APM API endpoint that fetches data for the latency distribution chart in the trace samples tab on the transactions page. Previously, this data was fetched via the custom Kibana search strategies used for APM Correlations which causes issues in load balancing setups. --- .../failed_transactions_correlations/types.ts | 13 +- .../latency_correlations/types.ts | 12 +- .../apm/common/search_strategies/types.ts | 12 +- .../latency_correlations.test.tsx | 5 +- .../distribution/index.test.tsx | 59 +++------ .../distribution/index.tsx | 79 +++++++++--- .../apm/public/hooks/use_search_strategy.ts | 10 +- .../get_overall_latency_distribution.ts | 121 ++++++++++++++++++ .../latency/get_percentile_threshold_value.ts | 53 ++++++++ .../plugins/apm/server/lib/latency/types.ts | 23 ++++ ...ransactions_correlations_search_service.ts | 31 ++--- .../failed_transactions_correlations/index.ts | 6 +- .../latency_correlations/index.ts | 6 +- .../latency_correlations_search_service.ts | 31 ++--- .../field_stats/get_field_stats.test.ts | 4 +- .../queries/get_query_with_params.test.ts | 12 +- .../queries/get_query_with_params.ts | 17 +-- .../queries/get_request_base.test.ts | 4 + .../queries/query_correlation.test.ts | 4 +- .../queries/query_field_candidates.test.ts | 4 +- .../queries/query_field_value_pairs.test.ts | 4 +- .../queries/query_fractions.test.ts | 4 +- .../queries/query_histogram.test.ts | 4 +- .../query_histogram_range_steps.test.ts | 4 +- .../queries/query_histogram_range_steps.ts | 6 +- .../query_histograms_generator.test.ts | 4 +- .../queries/query_percentiles.test.ts | 4 +- .../queries/query_ranges.test.ts | 4 +- .../search_strategy_provider.test.ts | 4 +- .../search_strategy_provider.ts | 104 +++++++++++---- .../get_global_apm_server_route_repository.ts | 2 + .../apm/server/routes/latency_distribution.ts | 63 +++++++++ .../tests/correlations/failed_transactions.ts | 4 +- .../tests/correlations/latency.ts | 23 ++-- .../test/apm_api_integration/tests/index.ts | 4 + .../latency_overall_distribution.ts | 65 ++++++++++ 36 files changed, 590 insertions(+), 219 deletions(-) create mode 100644 x-pack/plugins/apm/server/lib/latency/get_overall_latency_distribution.ts create mode 100644 x-pack/plugins/apm/server/lib/latency/get_percentile_threshold_value.ts create mode 100644 x-pack/plugins/apm/server/lib/latency/types.ts create mode 100644 x-pack/plugins/apm/server/routes/latency_distribution.ts create mode 100644 x-pack/test/apm_api_integration/tests/transactions/latency_overall_distribution.ts diff --git a/x-pack/plugins/apm/common/search_strategies/failed_transactions_correlations/types.ts b/x-pack/plugins/apm/common/search_strategies/failed_transactions_correlations/types.ts index 266d7246c35d4..28ce2ff24b961 100644 --- a/x-pack/plugins/apm/common/search_strategies/failed_transactions_correlations/types.ts +++ b/x-pack/plugins/apm/common/search_strategies/failed_transactions_correlations/types.ts @@ -5,12 +5,7 @@ * 2.0. */ -import { - FieldValuePair, - HistogramItem, - RawResponseBase, - SearchStrategyClientParams, -} from '../types'; +import { FieldValuePair, HistogramItem } from '../types'; import { FAILED_TRANSACTIONS_IMPACT_THRESHOLD } from './constants'; import { FieldStats } from '../field_stats_types'; @@ -33,11 +28,7 @@ export interface FailedTransactionsCorrelationsParams { percentileThreshold: number; } -export type FailedTransactionsCorrelationsRequestParams = - FailedTransactionsCorrelationsParams & SearchStrategyClientParams; - -export interface FailedTransactionsCorrelationsRawResponse - extends RawResponseBase { +export interface FailedTransactionsCorrelationsRawResponse { log: string[]; failedTransactionsCorrelations?: FailedTransactionsCorrelation[]; percentileThresholdValue?: number; diff --git a/x-pack/plugins/apm/common/search_strategies/latency_correlations/types.ts b/x-pack/plugins/apm/common/search_strategies/latency_correlations/types.ts index 2eb2b37159459..ea74175a3dacb 100644 --- a/x-pack/plugins/apm/common/search_strategies/latency_correlations/types.ts +++ b/x-pack/plugins/apm/common/search_strategies/latency_correlations/types.ts @@ -5,12 +5,7 @@ * 2.0. */ -import { - FieldValuePair, - HistogramItem, - RawResponseBase, - SearchStrategyClientParams, -} from '../types'; +import { FieldValuePair, HistogramItem } from '../types'; import { FieldStats } from '../field_stats_types'; export interface LatencyCorrelation extends FieldValuePair { @@ -33,10 +28,7 @@ export interface LatencyCorrelationsParams { analyzeCorrelations: boolean; } -export type LatencyCorrelationsRequestParams = LatencyCorrelationsParams & - SearchStrategyClientParams; - -export interface LatencyCorrelationsRawResponse extends RawResponseBase { +export interface LatencyCorrelationsRawResponse { log: string[]; overallHistogram?: HistogramItem[]; percentileThresholdValue?: number; diff --git a/x-pack/plugins/apm/common/search_strategies/types.ts b/x-pack/plugins/apm/common/search_strategies/types.ts index d7c6eab1f07c1..ff925f70fc9b0 100644 --- a/x-pack/plugins/apm/common/search_strategies/types.ts +++ b/x-pack/plugins/apm/common/search_strategies/types.ts @@ -31,16 +31,26 @@ export interface RawResponseBase { took: number; } -export interface SearchStrategyClientParams { +export interface SearchStrategyClientParamsBase { environment: string; kuery: string; serviceName?: string; transactionName?: string; transactionType?: string; +} + +export interface RawSearchStrategyClientParams + extends SearchStrategyClientParamsBase { start?: string; end?: string; } +export interface SearchStrategyClientParams + extends SearchStrategyClientParamsBase { + start: number; + end: number; +} + export interface SearchStrategyServerParams { index: string; includeFrozen?: boolean; diff --git a/x-pack/plugins/apm/public/components/app/correlations/latency_correlations.test.tsx b/x-pack/plugins/apm/public/components/app/correlations/latency_correlations.test.tsx index 9956452c565b3..918f94e64ef09 100644 --- a/x-pack/plugins/apm/public/components/app/correlations/latency_correlations.test.tsx +++ b/x-pack/plugins/apm/public/components/app/correlations/latency_correlations.test.tsx @@ -19,6 +19,7 @@ import type { IKibanaSearchResponse } from 'src/plugins/data/public'; import { EuiThemeProvider } from 'src/plugins/kibana_react/common'; import { createKibanaReactContext } from 'src/plugins/kibana_react/public'; import type { LatencyCorrelationsRawResponse } from '../../../../common/search_strategies/latency_correlations/types'; +import type { RawResponseBase } from '../../../../common/search_strategies/types'; import { MockUrlParamsContextProvider } from '../../../context/url_params_context/mock_url_params_context_provider'; import { ApmPluginContextValue } from '../../../context/apm_plugin/apm_plugin_context'; import { @@ -34,7 +35,9 @@ function Wrapper({ dataSearchResponse, }: { children?: ReactNode; - dataSearchResponse: IKibanaSearchResponse; + dataSearchResponse: IKibanaSearchResponse< + LatencyCorrelationsRawResponse & RawResponseBase + >; }) { const mockDataSearch = jest.fn(() => of(dataSearchResponse)); diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/distribution/index.test.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/distribution/index.test.tsx index bd0ff4c87c3be..0e9639de4aa74 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/distribution/index.test.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/distribution/index.test.tsx @@ -8,43 +8,24 @@ import { render, screen, waitFor } from '@testing-library/react'; import { createMemoryHistory } from 'history'; import React, { ReactNode } from 'react'; -import { of } from 'rxjs'; import { CoreStart } from 'kibana/public'; import { merge } from 'lodash'; -import { dataPluginMock } from 'src/plugins/data/public/mocks'; -import type { IKibanaSearchResponse } from 'src/plugins/data/public'; import { EuiThemeProvider } from 'src/plugins/kibana_react/common'; import { createKibanaReactContext } from 'src/plugins/kibana_react/public'; -import type { LatencyCorrelationsRawResponse } from '../../../../../common/search_strategies/latency_correlations/types'; import { MockUrlParamsContextProvider } from '../../../../context/url_params_context/mock_url_params_context_provider'; import { ApmPluginContextValue } from '../../../../context/apm_plugin/apm_plugin_context'; import { mockApmPluginContextValue, MockApmPluginContextWrapper, } from '../../../../context/apm_plugin/mock_apm_plugin_context'; +import * as useFetcherModule from '../../../../hooks/use_fetcher'; import { fromQuery } from '../../../shared/Links/url_helpers'; import { getFormattedSelection, TransactionDistribution } from './index'; -function Wrapper({ - children, - dataSearchResponse, -}: { - children?: ReactNode; - dataSearchResponse: IKibanaSearchResponse; -}) { - const mockDataSearch = jest.fn(() => of(dataSearchResponse)); - - const dataPluginMockStart = dataPluginMock.createStartContract(); +function Wrapper({ children }: { children?: ReactNode }) { const KibanaReactContext = createKibanaReactContext({ - data: { - ...dataPluginMockStart, - search: { - ...dataPluginMockStart.search, - search: mockDataSearch, - }, - }, usageCollection: { reportUiCounter: () => {} }, } as Partial); @@ -105,18 +86,14 @@ describe('transaction_details/distribution', () => { describe('TransactionDistribution', () => { it('shows loading indicator when the service is running and returned no results yet', async () => { + jest.spyOn(useFetcherModule, 'useFetcher').mockImplementation(() => ({ + data: {}, + refetch: () => {}, + status: useFetcherModule.FETCH_STATUS.LOADING, + })); + render( - + { }); it("doesn't show loading indicator when the service isn't running", async () => { + jest.spyOn(useFetcherModule, 'useFetcher').mockImplementation(() => ({ + data: { percentileThresholdValue: 1234, overallHistogram: [] }, + refetch: () => {}, + status: useFetcherModule.FETCH_STATUS.SUCCESS, + })); + render( - + { + if (serviceName && environment && start && end) { + return callApmApi({ + endpoint: 'GET /internal/apm/latency/overall_distribution', + params: { + query: { + serviceName, + transactionName, + transactionType, + kuery, + environment, + start, + end, + percentileThreshold: DEFAULT_PERCENTILE_THRESHOLD, + }, + }, + }); + } + }, + [ + serviceName, + transactionName, + transactionType, + kuery, + environment, + start, + end, + ] ); + const overallHistogram = + data.overallHistogram === undefined && status !== FETCH_STATUS.LOADING + ? [] + : data.overallHistogram; + const hasData = + Array.isArray(overallHistogram) && overallHistogram.length > 0; + useEffect(() => { - if (isErrorMessage(progress.error)) { + if (isErrorMessage(error)) { notifications.toasts.addDanger({ title: i18n.translate( 'xpack.apm.transactionDetails.distribution.errorTitle', @@ -119,10 +156,10 @@ export function TransactionDistribution({ defaultMessage: 'An error occurred fetching the distribution', } ), - text: progress.error.toString(), + text: error.toString(), }); } - }, [progress.error, notifications.toasts]); + }, [error, notifications.toasts]); const trackApmEvent = useUiTracker({ app: 'apm' }); @@ -213,7 +250,7 @@ export function TransactionDistribution({ data={transactionDistributionChartData} markerCurrentTransaction={markerCurrentTransaction} markerPercentile={DEFAULT_PERCENTILE_THRESHOLD} - markerValue={response.percentileThresholdValue ?? 0} + markerValue={data.percentileThresholdValue ?? 0} onChartSelection={onTrackedChartSelection as BrushEndListener} hasData={hasData} selection={selection} diff --git a/x-pack/plugins/apm/public/hooks/use_search_strategy.ts b/x-pack/plugins/apm/public/hooks/use_search_strategy.ts index ca8d28b106f84..275eddb68ae00 100644 --- a/x-pack/plugins/apm/public/hooks/use_search_strategy.ts +++ b/x-pack/plugins/apm/public/hooks/use_search_strategy.ts @@ -16,7 +16,7 @@ import { } from '../../../../../src/plugins/data/public'; import { useKibana } from '../../../../../src/plugins/kibana_react/public'; -import type { SearchStrategyClientParams } from '../../common/search_strategies/types'; +import type { RawSearchStrategyClientParams } from '../../common/search_strategies/types'; import type { RawResponseBase } from '../../common/search_strategies/types'; import type { LatencyCorrelationsParams, @@ -77,13 +77,15 @@ interface SearchStrategyReturnBase { export function useSearchStrategy( searchStrategyName: typeof APM_SEARCH_STRATEGIES.APM_LATENCY_CORRELATIONS, searchStrategyParams: LatencyCorrelationsParams -): SearchStrategyReturnBase; +): SearchStrategyReturnBase; // Function overload for Failed Transactions Correlations export function useSearchStrategy( searchStrategyName: typeof APM_SEARCH_STRATEGIES.APM_FAILED_TRANSACTIONS_CORRELATIONS, searchStrategyParams: FailedTransactionsCorrelationsParams -): SearchStrategyReturnBase; +): SearchStrategyReturnBase< + FailedTransactionsCorrelationsRawResponse & RawResponseBase +>; export function useSearchStrategy< TRawResponse extends RawResponseBase, @@ -145,7 +147,7 @@ export function useSearchStrategy< // Submit the search request using the `data.search` service. searchSubscription$.current = data.search .search< - IKibanaSearchRequest, + IKibanaSearchRequest, IKibanaSearchResponse >(request, { strategy: searchStrategyName, diff --git a/x-pack/plugins/apm/server/lib/latency/get_overall_latency_distribution.ts b/x-pack/plugins/apm/server/lib/latency/get_overall_latency_distribution.ts new file mode 100644 index 0000000000000..39470869488c3 --- /dev/null +++ b/x-pack/plugins/apm/server/lib/latency/get_overall_latency_distribution.ts @@ -0,0 +1,121 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { estypes } from '@elastic/elasticsearch'; + +import { ProcessorEvent } from '../../../common/processor_event'; + +import { withApmSpan } from '../../utils/with_apm_span'; + +import { + getHistogramIntervalRequest, + getHistogramRangeSteps, +} from '../search_strategies/queries/query_histogram_range_steps'; +import { getTransactionDurationRangesRequest } from '../search_strategies/queries/query_ranges'; + +import { getPercentileThresholdValue } from './get_percentile_threshold_value'; +import type { + OverallLatencyDistributionOptions, + OverallLatencyDistributionResponse, +} from './types'; + +export async function getOverallLatencyDistribution( + options: OverallLatencyDistributionOptions +) { + return withApmSpan('get_overall_latency_distribution', async () => { + const overallLatencyDistribution: OverallLatencyDistributionResponse = { + log: [], + }; + + const { setup, ...rawParams } = options; + const { apmEventClient } = setup; + const params = { + // pass on an empty index because we're using only the body attribute + // of the request body getters we're reusing from search strategies. + index: '', + ...rawParams, + }; + + // #1: get 95th percentile to be displayed as a marker in the log log chart + overallLatencyDistribution.percentileThresholdValue = + await getPercentileThresholdValue(options); + + // finish early if we weren't able to identify the percentileThresholdValue. + if (!overallLatencyDistribution.percentileThresholdValue) { + return overallLatencyDistribution; + } + + // #2: get histogram range steps + const steps = 100; + + const { body: histogramIntervalRequestBody } = + getHistogramIntervalRequest(params); + + const histogramIntervalResponse = (await apmEventClient.search( + 'get_histogram_interval', + { + // TODO: add support for metrics + apm: { events: [ProcessorEvent.transaction] }, + body: histogramIntervalRequestBody, + } + )) as { + aggregations?: { + transaction_duration_min: estypes.AggregationsValueAggregate; + transaction_duration_max: estypes.AggregationsValueAggregate; + }; + hits: { total: estypes.SearchTotalHits }; + }; + + if ( + !histogramIntervalResponse.aggregations || + histogramIntervalResponse.hits.total.value === 0 + ) { + return overallLatencyDistribution; + } + + const min = + histogramIntervalResponse.aggregations.transaction_duration_min.value; + const max = + histogramIntervalResponse.aggregations.transaction_duration_max.value * 2; + + const histogramRangeSteps = getHistogramRangeSteps(min, max, steps); + + // #3: get histogram chart data + const { body: transactionDurationRangesRequestBody } = + getTransactionDurationRangesRequest(params, histogramRangeSteps); + + const transactionDurationRangesResponse = (await apmEventClient.search( + 'get_transaction_duration_ranges', + { + // TODO: add support for metrics + apm: { events: [ProcessorEvent.transaction] }, + body: transactionDurationRangesRequestBody, + } + )) as { + aggregations?: { + logspace_ranges: estypes.AggregationsMultiBucketAggregate<{ + from: number; + doc_count: number; + }>; + }; + }; + + if (!transactionDurationRangesResponse.aggregations) { + return overallLatencyDistribution; + } + + overallLatencyDistribution.overallHistogram = + transactionDurationRangesResponse.aggregations.logspace_ranges.buckets + .map((d) => ({ + key: d.from, + doc_count: d.doc_count, + })) + .filter((d) => d.key !== undefined); + + return overallLatencyDistribution; + }); +} diff --git a/x-pack/plugins/apm/server/lib/latency/get_percentile_threshold_value.ts b/x-pack/plugins/apm/server/lib/latency/get_percentile_threshold_value.ts new file mode 100644 index 0000000000000..0d417a370e0b6 --- /dev/null +++ b/x-pack/plugins/apm/server/lib/latency/get_percentile_threshold_value.ts @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { estypes } from '@elastic/elasticsearch'; + +import { ProcessorEvent } from '../../../common/processor_event'; + +import { getTransactionDurationPercentilesRequest } from '../search_strategies/queries/query_percentiles'; + +import type { OverallLatencyDistributionOptions } from './types'; + +export async function getPercentileThresholdValue( + options: OverallLatencyDistributionOptions +) { + const { setup, percentileThreshold, ...rawParams } = options; + const { apmEventClient } = setup; + const params = { + // pass on an empty index because we're using only the body attribute + // of the request body getters we're reusing from search strategies. + index: '', + ...rawParams, + }; + + const { body: transactionDurationPercentilesRequestBody } = + getTransactionDurationPercentilesRequest(params, [percentileThreshold]); + + const transactionDurationPercentilesResponse = (await apmEventClient.search( + 'get_transaction_duration_percentiles', + { + // TODO: add support for metrics + apm: { events: [ProcessorEvent.transaction] }, + body: transactionDurationPercentilesRequestBody, + } + )) as { + aggregations?: { + transaction_duration_percentiles: estypes.AggregationsTDigestPercentilesAggregate; + }; + }; + + if (!transactionDurationPercentilesResponse.aggregations) { + return; + } + + const percentilesResponseThresholds = + transactionDurationPercentilesResponse.aggregations + .transaction_duration_percentiles?.values ?? {}; + + return percentilesResponseThresholds[`${percentileThreshold}.0`]; +} diff --git a/x-pack/plugins/apm/server/lib/latency/types.ts b/x-pack/plugins/apm/server/lib/latency/types.ts new file mode 100644 index 0000000000000..8dad1a39bd159 --- /dev/null +++ b/x-pack/plugins/apm/server/lib/latency/types.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Setup } from '../helpers/setup_request'; +import { CorrelationsOptions } from '../search_strategies/queries/get_filters'; + +export interface OverallLatencyDistributionOptions extends CorrelationsOptions { + percentileThreshold: number; + setup: Setup; +} + +export interface OverallLatencyDistributionResponse { + log: string[]; + percentileThresholdValue?: number; + overallHistogram?: Array<{ + key: number; + doc_count: number; + }>; +} diff --git a/x-pack/plugins/apm/server/lib/search_strategies/failed_transactions_correlations/failed_transactions_correlations_search_service.ts b/x-pack/plugins/apm/server/lib/search_strategies/failed_transactions_correlations/failed_transactions_correlations_search_service.ts index af5e535abdc3f..efc28ce98e5e0 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/failed_transactions_correlations/failed_transactions_correlations_search_service.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/failed_transactions_correlations/failed_transactions_correlations_search_service.ts @@ -9,17 +9,15 @@ import { chunk } from 'lodash'; import type { ElasticsearchClient } from 'src/core/server'; -import type { ISearchStrategy } from '../../../../../../../src/plugins/data/server'; -import { - IKibanaSearchRequest, - IKibanaSearchResponse, -} from '../../../../../../../src/plugins/data/common'; - import { EVENT_OUTCOME } from '../../../../common/elasticsearch_fieldnames'; import { EventOutcome } from '../../../../common/event_outcome'; -import type { SearchStrategyServerParams } from '../../../../common/search_strategies/types'; import type { - FailedTransactionsCorrelationsRequestParams, + SearchStrategyClientParams, + SearchStrategyServerParams, + RawResponseBase, +} from '../../../../common/search_strategies/types'; +import type { + FailedTransactionsCorrelationsParams, FailedTransactionsCorrelationsRawResponse, } from '../../../../common/search_strategies/failed_transactions_correlations/types'; import type { ApmIndicesConfig } from '../../settings/apm_indices/get_apm_indices'; @@ -38,22 +36,18 @@ import { failedTransactionsCorrelationsSearchServiceStateProvider } from './fail import { ERROR_CORRELATION_THRESHOLD } from '../constants'; import { fetchFieldsStats } from '../queries/field_stats/get_fields_stats'; -export type FailedTransactionsCorrelationsSearchServiceProvider = +type FailedTransactionsCorrelationsSearchServiceProvider = SearchServiceProvider< - FailedTransactionsCorrelationsRequestParams, - FailedTransactionsCorrelationsRawResponse + FailedTransactionsCorrelationsParams & SearchStrategyClientParams, + FailedTransactionsCorrelationsRawResponse & RawResponseBase >; -export type FailedTransactionsCorrelationsSearchStrategy = ISearchStrategy< - IKibanaSearchRequest, - IKibanaSearchResponse ->; - export const failedTransactionsCorrelationsSearchServiceProvider: FailedTransactionsCorrelationsSearchServiceProvider = ( esClient: ElasticsearchClient, getApmIndices: () => Promise, - searchServiceParams: FailedTransactionsCorrelationsRequestParams, + searchServiceParams: FailedTransactionsCorrelationsParams & + SearchStrategyClientParams, includeFrozen: boolean ) => { const { addLogMessage, getLogMessages } = searchServiceLogProvider(); @@ -63,7 +57,8 @@ export const failedTransactionsCorrelationsSearchServiceProvider: FailedTransact async function fetchErrorCorrelations() { try { const indices = await getApmIndices(); - const params: FailedTransactionsCorrelationsRequestParams & + const params: FailedTransactionsCorrelationsParams & + SearchStrategyClientParams & SearchStrategyServerParams = { ...searchServiceParams, index: indices.transaction, diff --git a/x-pack/plugins/apm/server/lib/search_strategies/failed_transactions_correlations/index.ts b/x-pack/plugins/apm/server/lib/search_strategies/failed_transactions_correlations/index.ts index ec91165cb481b..4763cd994d309 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/failed_transactions_correlations/index.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/failed_transactions_correlations/index.ts @@ -5,8 +5,4 @@ * 2.0. */ -export { - failedTransactionsCorrelationsSearchServiceProvider, - FailedTransactionsCorrelationsSearchServiceProvider, - FailedTransactionsCorrelationsSearchStrategy, -} from './failed_transactions_correlations_search_service'; +export { failedTransactionsCorrelationsSearchServiceProvider } from './failed_transactions_correlations_search_service'; diff --git a/x-pack/plugins/apm/server/lib/search_strategies/latency_correlations/index.ts b/x-pack/plugins/apm/server/lib/search_strategies/latency_correlations/index.ts index 073bb122896ff..040aa5a7e424e 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/latency_correlations/index.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/latency_correlations/index.ts @@ -5,8 +5,4 @@ * 2.0. */ -export { - latencyCorrelationsSearchServiceProvider, - LatencyCorrelationsSearchServiceProvider, - LatencyCorrelationsSearchStrategy, -} from './latency_correlations_search_service'; +export { latencyCorrelationsSearchServiceProvider } from './latency_correlations_search_service'; diff --git a/x-pack/plugins/apm/server/lib/search_strategies/latency_correlations/latency_correlations_search_service.ts b/x-pack/plugins/apm/server/lib/search_strategies/latency_correlations/latency_correlations_search_service.ts index 4862f7dd1de1a..f170818d018d4 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/latency_correlations/latency_correlations_search_service.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/latency_correlations/latency_correlations_search_service.ts @@ -8,15 +8,13 @@ import { range } from 'lodash'; import type { ElasticsearchClient } from 'src/core/server'; -import type { ISearchStrategy } from '../../../../../../../src/plugins/data/server'; -import { - IKibanaSearchRequest, - IKibanaSearchResponse, -} from '../../../../../../../src/plugins/data/common'; - -import type { SearchStrategyServerParams } from '../../../../common/search_strategies/types'; import type { - LatencyCorrelationsRequestParams, + RawResponseBase, + SearchStrategyClientParams, + SearchStrategyServerParams, +} from '../../../../common/search_strategies/types'; +import type { + LatencyCorrelationsParams, LatencyCorrelationsRawResponse, } from '../../../../common/search_strategies/latency_correlations/types'; @@ -38,21 +36,16 @@ import type { SearchServiceProvider } from '../search_strategy_provider'; import { latencyCorrelationsSearchServiceStateProvider } from './latency_correlations_search_service_state'; import { fetchFieldsStats } from '../queries/field_stats/get_fields_stats'; -export type LatencyCorrelationsSearchServiceProvider = SearchServiceProvider< - LatencyCorrelationsRequestParams, - LatencyCorrelationsRawResponse ->; - -export type LatencyCorrelationsSearchStrategy = ISearchStrategy< - IKibanaSearchRequest, - IKibanaSearchResponse +type LatencyCorrelationsSearchServiceProvider = SearchServiceProvider< + LatencyCorrelationsParams & SearchStrategyClientParams, + LatencyCorrelationsRawResponse & RawResponseBase >; export const latencyCorrelationsSearchServiceProvider: LatencyCorrelationsSearchServiceProvider = ( esClient: ElasticsearchClient, getApmIndices: () => Promise, - searchServiceParams: LatencyCorrelationsRequestParams, + searchServiceParams: LatencyCorrelationsParams & SearchStrategyClientParams, includeFrozen: boolean ) => { const { addLogMessage, getLogMessages } = searchServiceLogProvider(); @@ -61,7 +54,9 @@ export const latencyCorrelationsSearchServiceProvider: LatencyCorrelationsSearch async function fetchCorrelations() { let params: - | (LatencyCorrelationsRequestParams & SearchStrategyServerParams) + | (LatencyCorrelationsParams & + SearchStrategyClientParams & + SearchStrategyServerParams) | undefined; try { diff --git a/x-pack/plugins/apm/server/lib/search_strategies/queries/field_stats/get_field_stats.test.ts b/x-pack/plugins/apm/server/lib/search_strategies/queries/field_stats/get_field_stats.test.ts index deb89ace47c5d..d3cee1c4ca596 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/queries/field_stats/get_field_stats.test.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/queries/field_stats/get_field_stats.test.ts @@ -15,8 +15,8 @@ import { fetchFieldsStats } from './get_fields_stats'; const params = { index: 'apm-*', - start: '2020', - end: '2021', + start: 1577836800000, + end: 1609459200000, includeFrozen: false, environment: ENVIRONMENT_ALL.value, kuery: '', diff --git a/x-pack/plugins/apm/server/lib/search_strategies/queries/get_query_with_params.test.ts b/x-pack/plugins/apm/server/lib/search_strategies/queries/get_query_with_params.test.ts index c77b4df78f865..9d0441e513198 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/queries/get_query_with_params.test.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/queries/get_query_with_params.test.ts @@ -14,8 +14,8 @@ describe('correlations', () => { const query = getQueryWithParams({ params: { index: 'apm-*', - start: '2020', - end: '2021', + start: 1577836800000, + end: 1609459200000, includeFrozen: false, environment: ENVIRONMENT_ALL.value, kuery: '', @@ -45,8 +45,8 @@ describe('correlations', () => { index: 'apm-*', serviceName: 'actualServiceName', transactionName: 'actualTransactionName', - start: '2020', - end: '2021', + start: 1577836800000, + end: 1609459200000, environment: 'dev', kuery: '', includeFrozen: false, @@ -93,8 +93,8 @@ describe('correlations', () => { const query = getQueryWithParams({ params: { index: 'apm-*', - start: '2020', - end: '2021', + start: 1577836800000, + end: 1609459200000, includeFrozen: false, environment: ENVIRONMENT_ALL.value, kuery: '', diff --git a/x-pack/plugins/apm/server/lib/search_strategies/queries/get_query_with_params.ts b/x-pack/plugins/apm/server/lib/search_strategies/queries/get_query_with_params.ts index f00c89503f103..31a98b0a6bb18 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/queries/get_query_with_params.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/queries/get_query_with_params.ts @@ -6,15 +6,10 @@ */ import type { estypes } from '@elastic/elasticsearch'; -import { getOrElse } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import * as t from 'io-ts'; -import { failure } from 'io-ts/lib/PathReporter'; import type { FieldValuePair, SearchStrategyParams, } from '../../../../common/search_strategies/types'; -import { rangeRt } from '../../../routes/default_api_types'; import { getCorrelationsFilters } from './get_filters'; export const getTermsQuery = ({ fieldName, fieldValue }: FieldValuePair) => { @@ -36,22 +31,14 @@ export const getQueryWithParams = ({ params, termFilters }: QueryParams) => { transactionName, } = params; - // converts string based start/end to epochmillis - const decodedRange = pipe( - rangeRt.decode({ start, end }), - getOrElse((errors) => { - throw new Error(failure(errors).join('\n')); - }) - ); - const correlationFilters = getCorrelationsFilters({ environment, kuery, serviceName, transactionType, transactionName, - start: decodedRange.start, - end: decodedRange.end, + start, + end, }); return { diff --git a/x-pack/plugins/apm/server/lib/search_strategies/queries/get_request_base.test.ts b/x-pack/plugins/apm/server/lib/search_strategies/queries/get_request_base.test.ts index fd5f52207d4c5..eb771e1e1aaf4 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/queries/get_request_base.test.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/queries/get_request_base.test.ts @@ -16,6 +16,8 @@ describe('correlations', () => { includeFrozen: true, environment: ENVIRONMENT_ALL.value, kuery: '', + start: 1577836800000, + end: 1609459200000, }); expect(requestBase).toEqual({ index: 'apm-*', @@ -29,6 +31,8 @@ describe('correlations', () => { index: 'apm-*', environment: ENVIRONMENT_ALL.value, kuery: '', + start: 1577836800000, + end: 1609459200000, }); expect(requestBase).toEqual({ index: 'apm-*', diff --git a/x-pack/plugins/apm/server/lib/search_strategies/queries/query_correlation.test.ts b/x-pack/plugins/apm/server/lib/search_strategies/queries/query_correlation.test.ts index fc2dacce61a73..40fcc17444492 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/queries/query_correlation.test.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/queries/query_correlation.test.ts @@ -18,8 +18,8 @@ import { const params = { index: 'apm-*', - start: '2020', - end: '2021', + start: 1577836800000, + end: 1609459200000, includeFrozen: false, environment: ENVIRONMENT_ALL.value, kuery: '', diff --git a/x-pack/plugins/apm/server/lib/search_strategies/queries/query_field_candidates.test.ts b/x-pack/plugins/apm/server/lib/search_strategies/queries/query_field_candidates.test.ts index 6e0521ac1a008..bae42666e6db0 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/queries/query_field_candidates.test.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/queries/query_field_candidates.test.ts @@ -20,8 +20,8 @@ import { const params = { index: 'apm-*', - start: '2020', - end: '2021', + start: 1577836800000, + end: 1609459200000, includeFrozen: false, environment: ENVIRONMENT_ALL.value, kuery: '', diff --git a/x-pack/plugins/apm/server/lib/search_strategies/queries/query_field_value_pairs.test.ts b/x-pack/plugins/apm/server/lib/search_strategies/queries/query_field_value_pairs.test.ts index 9ffbf6b2ce18d..ab7a0b4e02072 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/queries/query_field_value_pairs.test.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/queries/query_field_value_pairs.test.ts @@ -20,8 +20,8 @@ import { const params = { index: 'apm-*', - start: '2020', - end: '2021', + start: 1577836800000, + end: 1609459200000, includeFrozen: false, environment: ENVIRONMENT_ALL.value, kuery: '', diff --git a/x-pack/plugins/apm/server/lib/search_strategies/queries/query_fractions.test.ts b/x-pack/plugins/apm/server/lib/search_strategies/queries/query_fractions.test.ts index daf6b368c78b1..9c704ef7b489a 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/queries/query_fractions.test.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/queries/query_fractions.test.ts @@ -17,8 +17,8 @@ import { const params = { index: 'apm-*', - start: '2020', - end: '2021', + start: 1577836800000, + end: 1609459200000, includeFrozen: false, environment: ENVIRONMENT_ALL.value, kuery: '', diff --git a/x-pack/plugins/apm/server/lib/search_strategies/queries/query_histogram.test.ts b/x-pack/plugins/apm/server/lib/search_strategies/queries/query_histogram.test.ts index 7ecb1d2d8a333..7cc6106f671a7 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/queries/query_histogram.test.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/queries/query_histogram.test.ts @@ -17,8 +17,8 @@ import { const params = { index: 'apm-*', - start: '2020', - end: '2021', + start: 1577836800000, + end: 1609459200000, includeFrozen: false, environment: ENVIRONMENT_ALL.value, kuery: '', diff --git a/x-pack/plugins/apm/server/lib/search_strategies/queries/query_histogram_range_steps.test.ts b/x-pack/plugins/apm/server/lib/search_strategies/queries/query_histogram_range_steps.test.ts index ffc86c7ef6c32..41a2fa9a5039e 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/queries/query_histogram_range_steps.test.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/queries/query_histogram_range_steps.test.ts @@ -17,8 +17,8 @@ import { const params = { index: 'apm-*', - start: '2020', - end: '2021', + start: 1577836800000, + end: 1609459200000, includeFrozen: false, environment: ENVIRONMENT_ALL.value, kuery: '', diff --git a/x-pack/plugins/apm/server/lib/search_strategies/queries/query_histogram_range_steps.ts b/x-pack/plugins/apm/server/lib/search_strategies/queries/query_histogram_range_steps.ts index 790919d193028..439bb9e4b9cd6 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/queries/query_histogram_range_steps.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/queries/query_histogram_range_steps.ts @@ -17,7 +17,11 @@ import type { SearchStrategyParams } from '../../../../common/search_strategies/ import { getQueryWithParams } from './get_query_with_params'; import { getRequestBase } from './get_request_base'; -const getHistogramRangeSteps = (min: number, max: number, steps: number) => { +export const getHistogramRangeSteps = ( + min: number, + max: number, + steps: number +) => { // A d3 based scale function as a helper to get equally distributed bins on a log scale. // We round the final values because the ES range agg we use won't accept numbers with decimals for `transaction.duration.us`. const logFn = scaleLog().domain([min, max]).range([1, steps]); diff --git a/x-pack/plugins/apm/server/lib/search_strategies/queries/query_histograms_generator.test.ts b/x-pack/plugins/apm/server/lib/search_strategies/queries/query_histograms_generator.test.ts index 375e32b1472c6..00e8c26497eb2 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/queries/query_histograms_generator.test.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/queries/query_histograms_generator.test.ts @@ -17,8 +17,8 @@ import { fetchTransactionDurationHistograms } from './query_histograms_generator const params = { index: 'apm-*', - start: '2020', - end: '2021', + start: 1577836800000, + end: 1609459200000, includeFrozen: false, environment: ENVIRONMENT_ALL.value, kuery: '', diff --git a/x-pack/plugins/apm/server/lib/search_strategies/queries/query_percentiles.test.ts b/x-pack/plugins/apm/server/lib/search_strategies/queries/query_percentiles.test.ts index ce86ffd9654e6..57e3e6cadb9bc 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/queries/query_percentiles.test.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/queries/query_percentiles.test.ts @@ -17,8 +17,8 @@ import { const params = { index: 'apm-*', - start: '2020', - end: '2021', + start: 1577836800000, + end: 1609459200000, includeFrozen: false, environment: ENVIRONMENT_ALL.value, kuery: '', diff --git a/x-pack/plugins/apm/server/lib/search_strategies/queries/query_ranges.test.ts b/x-pack/plugins/apm/server/lib/search_strategies/queries/query_ranges.test.ts index e210eb7d41e78..7d67e80ae3398 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/queries/query_ranges.test.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/queries/query_ranges.test.ts @@ -17,8 +17,8 @@ import { const params = { index: 'apm-*', - start: '2020', - end: '2021', + start: 1577836800000, + end: 1609459200000, includeFrozen: false, environment: ENVIRONMENT_ALL.value, kuery: '', diff --git a/x-pack/plugins/apm/server/lib/search_strategies/search_strategy_provider.test.ts b/x-pack/plugins/apm/server/lib/search_strategies/search_strategy_provider.test.ts index 8a9d04df32036..034bd2a60ad19 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/search_strategy_provider.test.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/search_strategy_provider.test.ts @@ -13,7 +13,7 @@ import { IKibanaSearchRequest } from '../../../../../../src/plugins/data/common' import { ENVIRONMENT_ALL } from '../../../common/environment_filter_values'; import type { LatencyCorrelationsParams } from '../../../common/search_strategies/latency_correlations/types'; -import type { SearchStrategyClientParams } from '../../../common/search_strategies/types'; +import type { RawSearchStrategyClientParams } from '../../../common/search_strategies/types'; import type { ApmIndicesConfig } from '../settings/apm_indices/get_apm_indices'; @@ -112,7 +112,7 @@ describe('APM Correlations search strategy', () => { let mockDeps: SearchStrategyDependencies; let params: Required< IKibanaSearchRequest< - LatencyCorrelationsParams & SearchStrategyClientParams + LatencyCorrelationsParams & RawSearchStrategyClientParams > >['params']; diff --git a/x-pack/plugins/apm/server/lib/search_strategies/search_strategy_provider.ts b/x-pack/plugins/apm/server/lib/search_strategies/search_strategy_provider.ts index cec10294460b0..8035e9e4d97ca 100644 --- a/x-pack/plugins/apm/server/lib/search_strategies/search_strategy_provider.ts +++ b/x-pack/plugins/apm/server/lib/search_strategies/search_strategy_provider.ts @@ -7,6 +7,10 @@ import uuid from 'uuid'; import { of } from 'rxjs'; +import { getOrElse } from 'fp-ts/lib/Either'; +import { pipe } from 'fp-ts/lib/pipeable'; +import * as t from 'io-ts'; +import { failure } from 'io-ts/lib/PathReporter'; import type { ElasticsearchClient } from 'src/core/server'; @@ -16,18 +20,21 @@ import { IKibanaSearchResponse, } from '../../../../../../src/plugins/data/common'; -import type { SearchStrategyClientParams } from '../../../common/search_strategies/types'; -import type { RawResponseBase } from '../../../common/search_strategies/types'; -import type { ApmIndicesConfig } from '../settings/apm_indices/get_apm_indices'; - import type { - LatencyCorrelationsSearchServiceProvider, - LatencyCorrelationsSearchStrategy, -} from './latency_correlations'; + RawResponseBase, + RawSearchStrategyClientParams, + SearchStrategyClientParams, +} from '../../../common/search_strategies/types'; +import type { + LatencyCorrelationsParams, + LatencyCorrelationsRawResponse, +} from '../../../common/search_strategies/latency_correlations/types'; import type { - FailedTransactionsCorrelationsSearchServiceProvider, - FailedTransactionsCorrelationsSearchStrategy, -} from './failed_transactions_correlations'; + FailedTransactionsCorrelationsParams, + FailedTransactionsCorrelationsRawResponse, +} from '../../../common/search_strategies/failed_transactions_correlations/types'; +import { rangeRt } from '../../routes/default_api_types'; +import type { ApmIndicesConfig } from '../settings/apm_indices/get_apm_indices'; interface SearchServiceState { cancel: () => void; @@ -56,35 +63,50 @@ export type SearchServiceProvider< // Failed Transactions Correlations function overload export function searchStrategyProvider( - searchServiceProvider: FailedTransactionsCorrelationsSearchServiceProvider, + searchServiceProvider: SearchServiceProvider< + FailedTransactionsCorrelationsParams & SearchStrategyClientParams, + FailedTransactionsCorrelationsRawResponse & RawResponseBase + >, getApmIndices: () => Promise, includeFrozen: boolean -): FailedTransactionsCorrelationsSearchStrategy; +): ISearchStrategy< + IKibanaSearchRequest< + FailedTransactionsCorrelationsParams & RawSearchStrategyClientParams + >, + IKibanaSearchResponse< + FailedTransactionsCorrelationsRawResponse & RawResponseBase + > +>; // Latency Correlations function overload export function searchStrategyProvider( - searchServiceProvider: LatencyCorrelationsSearchServiceProvider, + searchServiceProvider: SearchServiceProvider< + LatencyCorrelationsParams & SearchStrategyClientParams, + LatencyCorrelationsRawResponse & RawResponseBase + >, getApmIndices: () => Promise, includeFrozen: boolean -): LatencyCorrelationsSearchStrategy; +): ISearchStrategy< + IKibanaSearchRequest< + LatencyCorrelationsParams & RawSearchStrategyClientParams + >, + IKibanaSearchResponse +>; -export function searchStrategyProvider< - TSearchStrategyClientParams extends SearchStrategyClientParams, - TRawResponse extends RawResponseBase ->( +export function searchStrategyProvider( searchServiceProvider: SearchServiceProvider< - TSearchStrategyClientParams, - TRawResponse + TRequestParams & SearchStrategyClientParams, + TResponseParams & RawResponseBase >, getApmIndices: () => Promise, includeFrozen: boolean ): ISearchStrategy< - IKibanaSearchRequest, - IKibanaSearchResponse + IKibanaSearchRequest, + IKibanaSearchResponse > { const searchServiceMap = new Map< string, - GetSearchServiceState + GetSearchServiceState >(); return { @@ -93,9 +115,21 @@ export function searchStrategyProvider< throw new Error('Invalid request parameters.'); } + const { start: startString, end: endString } = request.params; + + // converts string based start/end to epochmillis + const decodedRange = pipe( + rangeRt.decode({ start: startString, end: endString }), + getOrElse((errors) => { + throw new Error(failure(errors).join('\n')); + }) + ); + // The function to fetch the current state of the search service. // This will be either an existing service for a follow up fetch or a new one for new requests. - let getSearchServiceState: GetSearchServiceState; + let getSearchServiceState: GetSearchServiceState< + TResponseParams & RawResponseBase + >; // If the request includes an ID, we require that the search service already exists // otherwise we throw an error. The client should never poll a service that's been cancelled or finished. @@ -111,10 +145,30 @@ export function searchStrategyProvider< getSearchServiceState = existingGetSearchServiceState; } else { + const { + start, + end, + environment, + kuery, + serviceName, + transactionName, + transactionType, + ...requestParams + } = request.params; + getSearchServiceState = searchServiceProvider( deps.esClient.asCurrentUser, getApmIndices, - request.params as TSearchStrategyClientParams, + { + environment, + kuery, + serviceName, + transactionName, + transactionType, + start: decodedRange.start, + end: decodedRange.end, + ...(requestParams as unknown as TRequestParams), + }, includeFrozen ); } diff --git a/x-pack/plugins/apm/server/routes/get_global_apm_server_route_repository.ts b/x-pack/plugins/apm/server/routes/get_global_apm_server_route_repository.ts index 472e46fecfa10..3fa6152d953f3 100644 --- a/x-pack/plugins/apm/server/routes/get_global_apm_server_route_repository.ts +++ b/x-pack/plugins/apm/server/routes/get_global_apm_server_route_repository.ts @@ -17,6 +17,7 @@ import { environmentsRouteRepository } from './environments'; import { errorsRouteRepository } from './errors'; import { apmFleetRouteRepository } from './fleet'; import { indexPatternRouteRepository } from './index_pattern'; +import { latencyDistributionRouteRepository } from './latency_distribution'; import { metricsRouteRepository } from './metrics'; import { observabilityOverviewRouteRepository } from './observability_overview'; import { rumRouteRepository } from './rum_client'; @@ -41,6 +42,7 @@ const getTypedGlobalApmServerRouteRepository = () => { .merge(indexPatternRouteRepository) .merge(environmentsRouteRepository) .merge(errorsRouteRepository) + .merge(latencyDistributionRouteRepository) .merge(metricsRouteRepository) .merge(observabilityOverviewRouteRepository) .merge(rumRouteRepository) diff --git a/x-pack/plugins/apm/server/routes/latency_distribution.ts b/x-pack/plugins/apm/server/routes/latency_distribution.ts new file mode 100644 index 0000000000000..ea921a7f4838d --- /dev/null +++ b/x-pack/plugins/apm/server/routes/latency_distribution.ts @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as t from 'io-ts'; +import { toNumberRt } from '@kbn/io-ts-utils'; +import { getOverallLatencyDistribution } from '../lib/latency/get_overall_latency_distribution'; +import { setupRequest } from '../lib/helpers/setup_request'; +import { createApmServerRoute } from './create_apm_server_route'; +import { createApmServerRouteRepository } from './create_apm_server_route_repository'; +import { environmentRt, kueryRt, rangeRt } from './default_api_types'; + +const latencyOverallDistributionRoute = createApmServerRoute({ + endpoint: 'GET /internal/apm/latency/overall_distribution', + params: t.type({ + query: t.intersection([ + t.partial({ + serviceName: t.string, + transactionName: t.string, + transactionType: t.string, + }), + environmentRt, + kueryRt, + rangeRt, + t.type({ + percentileThreshold: toNumberRt, + }), + ]), + }), + options: { tags: ['access:apm'] }, + handler: async (resources) => { + const setup = await setupRequest(resources); + + const { + environment, + kuery, + serviceName, + transactionType, + transactionName, + start, + end, + percentileThreshold, + } = resources.params.query; + + return getOverallLatencyDistribution({ + environment, + kuery, + serviceName, + transactionType, + transactionName, + start, + end, + percentileThreshold, + setup, + }); + }, +}); + +export const latencyDistributionRouteRepository = + createApmServerRouteRepository().add(latencyOverallDistributionRoute); diff --git a/x-pack/test/apm_api_integration/tests/correlations/failed_transactions.ts b/x-pack/test/apm_api_integration/tests/correlations/failed_transactions.ts index 6e2025a7fa2ca..3388d5b4aa379 100644 --- a/x-pack/test/apm_api_integration/tests/correlations/failed_transactions.ts +++ b/x-pack/test/apm_api_integration/tests/correlations/failed_transactions.ts @@ -10,7 +10,7 @@ import expect from '@kbn/expect'; import { IKibanaSearchRequest } from '../../../../../src/plugins/data/common'; import type { FailedTransactionsCorrelationsParams } from '../../../../plugins/apm/common/search_strategies/failed_transactions_correlations/types'; -import type { SearchStrategyClientParams } from '../../../../plugins/apm/common/search_strategies/types'; +import type { RawSearchStrategyClientParams } from '../../../../plugins/apm/common/search_strategies/types'; import { APM_SEARCH_STRATEGIES } from '../../../../plugins/apm/common/search_strategies/constants'; import { FtrProviderContext } from '../../common/ftr_provider_context'; @@ -23,7 +23,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { const getRequestBody = () => { const request: IKibanaSearchRequest< - FailedTransactionsCorrelationsParams & SearchStrategyClientParams + FailedTransactionsCorrelationsParams & RawSearchStrategyClientParams > = { params: { environment: 'ENVIRONMENT_ALL', diff --git a/x-pack/test/apm_api_integration/tests/correlations/latency.ts b/x-pack/test/apm_api_integration/tests/correlations/latency.ts index 99aee770c625d..75a4edd447c70 100644 --- a/x-pack/test/apm_api_integration/tests/correlations/latency.ts +++ b/x-pack/test/apm_api_integration/tests/correlations/latency.ts @@ -10,7 +10,7 @@ import expect from '@kbn/expect'; import { IKibanaSearchRequest } from '../../../../../src/plugins/data/common'; import type { LatencyCorrelationsParams } from '../../../../plugins/apm/common/search_strategies/latency_correlations/types'; -import type { SearchStrategyClientParams } from '../../../../plugins/apm/common/search_strategies/types'; +import type { RawSearchStrategyClientParams } from '../../../../plugins/apm/common/search_strategies/types'; import { APM_SEARCH_STRATEGIES } from '../../../../plugins/apm/common/search_strategies/constants'; import { FtrProviderContext } from '../../common/ftr_provider_context'; @@ -22,16 +22,17 @@ export default function ApiTest({ getService }: FtrProviderContext) { const supertest = getService('legacySupertestAsApmReadUser'); const getRequestBody = () => { - const request: IKibanaSearchRequest = { - params: { - environment: 'ENVIRONMENT_ALL', - start: '2020', - end: '2021', - kuery: '', - percentileThreshold: 95, - analyzeCorrelations: true, - }, - }; + const request: IKibanaSearchRequest = + { + params: { + environment: 'ENVIRONMENT_ALL', + start: '2020', + end: '2021', + kuery: '', + percentileThreshold: 95, + analyzeCorrelations: true, + }, + }; return { batch: [ diff --git a/x-pack/test/apm_api_integration/tests/index.ts b/x-pack/test/apm_api_integration/tests/index.ts index 09f4e2596ea46..f68a49658f2ee 100644 --- a/x-pack/test/apm_api_integration/tests/index.ts +++ b/x-pack/test/apm_api_integration/tests/index.ts @@ -175,6 +175,10 @@ export default function apmApiIntegrationTests(providerContext: FtrProviderConte loadTestFile(require.resolve('./transactions/error_rate')); }); + describe('transactions/latency_overall_distribution', function () { + loadTestFile(require.resolve('./transactions/latency_overall_distribution')); + }); + describe('transactions/latency', function () { loadTestFile(require.resolve('./transactions/latency')); }); diff --git a/x-pack/test/apm_api_integration/tests/transactions/latency_overall_distribution.ts b/x-pack/test/apm_api_integration/tests/transactions/latency_overall_distribution.ts new file mode 100644 index 0000000000000..c915ac8911e37 --- /dev/null +++ b/x-pack/test/apm_api_integration/tests/transactions/latency_overall_distribution.ts @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { registry } from '../../common/registry'; + +export default function ApiTest({ getService }: FtrProviderContext) { + const apmApiClient = getService('apmApiClient'); + + const endpoint = 'GET /internal/apm/latency/overall_distribution'; + + // This matches the parameters used for the other tab's search strategy approach in `../correlations/*`. + const getOptions = () => ({ + params: { + query: { + environment: 'ENVIRONMENT_ALL', + start: '2020', + end: '2021', + kuery: '', + percentileThreshold: '95', + }, + }, + }); + + registry.when( + 'latency overall distribution without data', + { config: 'trial', archives: [] }, + () => { + it('handles the empty state', async () => { + const response = await apmApiClient.readUser({ + endpoint, + ...getOptions(), + }); + + expect(response.status).to.be(200); + expect(response.body?.percentileThresholdValue).to.be(undefined); + expect(response.body?.overallHistogram?.length).to.be(undefined); + }); + } + ); + + registry.when( + 'latency overall distribution with data and default args', + // This uses the same archive used for the other tab's search strategy approach in `../correlations/*`. + { config: 'trial', archives: ['8.0.0'] }, + () => { + it('returns percentileThresholdValue and overall histogram', async () => { + const response = await apmApiClient.readUser({ + endpoint, + ...getOptions(), + }); + + expect(response.status).to.eql(200); + // This matches the values returned for the other tab's search strategy approach in `../correlations/*`. + expect(response.body?.percentileThresholdValue).to.be(1309695.875); + expect(response.body?.overallHistogram?.length).to.be(101); + }); + } + ); +} From 512d59467bcdbbf52d1b9038f1d6a1d1a2e0b964 Mon Sep 17 00:00:00 2001 From: Dominique Clarke Date: Mon, 18 Oct 2021 18:16:20 -0400 Subject: [PATCH 018/204] [Uptime] redirect Uptime tutorials to the Elastic Synthetics Integration (#115229) * redirect uptime tutorials * adjust tests and aria labels --- .../shared/add_data_buttons/synthetics_add_data.tsx | 4 ++-- .../observability/public/pages/overview/empty_section.ts | 2 +- x-pack/plugins/uptime/public/apps/use_no_data_config.ts | 4 ++-- .../components/common/header/action_menu_content.test.tsx | 6 ++++-- .../public/components/common/header/action_menu_content.tsx | 6 ++++-- x-pack/plugins/uptime/public/lib/helper/rtl_helpers.tsx | 2 +- 6 files changed, 14 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/observability/public/components/shared/add_data_buttons/synthetics_add_data.tsx b/x-pack/plugins/observability/public/components/shared/add_data_buttons/synthetics_add_data.tsx index af91624769e6b..d852d6fdb9a31 100644 --- a/x-pack/plugins/observability/public/components/shared/add_data_buttons/synthetics_add_data.tsx +++ b/x-pack/plugins/observability/public/components/shared/add_data_buttons/synthetics_add_data.tsx @@ -16,9 +16,9 @@ export function SyntheticsAddData() { return ( diff --git a/x-pack/plugins/observability/public/pages/overview/empty_section.ts b/x-pack/plugins/observability/public/pages/overview/empty_section.ts index 2747b2ecdebc9..f249a820a60a4 100644 --- a/x-pack/plugins/observability/public/pages/overview/empty_section.ts +++ b/x-pack/plugins/observability/public/pages/overview/empty_section.ts @@ -69,7 +69,7 @@ export const getEmptySections = ({ core }: { core: CoreStart }): ISection[] => { linkTitle: i18n.translate('xpack.observability.emptySection.apps.uptime.link', { defaultMessage: 'Install Heartbeat', }), - href: core.http.basePath.prepend('/app/home#/tutorial/uptimeMonitors'), + href: core.http.basePath.prepend('/app/integrations/detail/synthetics/overview'), }, { id: 'ux', diff --git a/x-pack/plugins/uptime/public/apps/use_no_data_config.ts b/x-pack/plugins/uptime/public/apps/use_no_data_config.ts index dc00a25e3a111..6e73a6d5e8268 100644 --- a/x-pack/plugins/uptime/public/apps/use_no_data_config.ts +++ b/x-pack/plugins/uptime/public/apps/use_no_data_config.ts @@ -31,13 +31,13 @@ export function useNoDataConfig(): KibanaPageTemplateProps['noDataConfig'] { actions: { beats: { title: i18n.translate('xpack.uptime.noDataConfig.beatsCard.title', { - defaultMessage: 'Add monitors with Heartbeat', + defaultMessage: 'Add monitors with the Elastic Synthetics integration', }), description: i18n.translate('xpack.uptime.noDataConfig.beatsCard.description', { defaultMessage: 'Proactively monitor the availability of your sites and services. Receive alerts and resolve issues faster to optimize your users experience.', }), - href: basePath + `/app/home#/tutorial/uptimeMonitors`, + href: basePath + `/app/integrations/detail/synthetics/overview`, }, }, docsLink: docLinks!.links.observability.guide, diff --git a/x-pack/plugins/uptime/public/components/common/header/action_menu_content.test.tsx b/x-pack/plugins/uptime/public/components/common/header/action_menu_content.test.tsx index 0265588c3fdeb..76b9378ca4ff6 100644 --- a/x-pack/plugins/uptime/public/components/common/header/action_menu_content.test.tsx +++ b/x-pack/plugins/uptime/public/components/common/header/action_menu_content.test.tsx @@ -45,11 +45,13 @@ describe('ActionMenuContent', () => { it('renders Add Data link', () => { const { getByLabelText, getByText } = render(); - const addDataAnchor = getByLabelText('Navigate to a tutorial about adding Uptime data'); + const addDataAnchor = getByLabelText( + 'Navigate to the Elastic Synthetics integration to add Uptime data' + ); // this href value is mocked, so it doesn't correspond to the real link // that Kibana core services will provide - expect(addDataAnchor.getAttribute('href')).toBe('/home#/tutorial/uptimeMonitors'); + expect(addDataAnchor.getAttribute('href')).toBe('/integrations/detail/synthetics/overview'); expect(getByText('Add data')); }); }); diff --git a/x-pack/plugins/uptime/public/components/common/header/action_menu_content.tsx b/x-pack/plugins/uptime/public/components/common/header/action_menu_content.tsx index bcfbf18c93cf5..789953258750b 100644 --- a/x-pack/plugins/uptime/public/components/common/header/action_menu_content.tsx +++ b/x-pack/plugins/uptime/public/components/common/header/action_menu_content.tsx @@ -99,9 +99,11 @@ export function ActionMenuContent(): React.ReactElement { diff --git a/x-pack/plugins/uptime/public/lib/helper/rtl_helpers.tsx b/x-pack/plugins/uptime/public/lib/helper/rtl_helpers.tsx index ac129bdb327d9..60ccec84c3bb1 100644 --- a/x-pack/plugins/uptime/public/lib/helper/rtl_helpers.tsx +++ b/x-pack/plugins/uptime/public/lib/helper/rtl_helpers.tsx @@ -83,7 +83,7 @@ const createMockStore = () => { const mockAppUrls: Record = { uptime: '/app/uptime', observability: '/app/observability', - '/home#/tutorial/uptimeMonitors': '/home#/tutorial/uptimeMonitors', + '/integrations/detail/synthetics/overview': '/integrations/detail/synthetics/overview', }; /* default mock core */ From a4f6988fc67e1bef37b3c153377ddbde40ddd704 Mon Sep 17 00:00:00 2001 From: Kevin Logan <56395104+kevinlog@users.noreply.github.com> Date: Mon, 18 Oct 2021 19:27:28 -0400 Subject: [PATCH 019/204] [Security Solution] Skip flakey test Configures a new connector.Cases connectors Configures a new connector (#115440) --- .../cypress/integration/cases/connectors.spec.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/cases/connectors.spec.ts b/x-pack/plugins/security_solution/cypress/integration/cases/connectors.spec.ts index 287d86c6fba9e..69b623de0b43c 100644 --- a/x-pack/plugins/security_solution/cypress/integration/cases/connectors.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/cases/connectors.spec.ts @@ -20,7 +20,8 @@ import { loginAndWaitForPageWithoutDateRange } from '../../tasks/login'; import { CASES_URL } from '../../urls/navigation'; -describe('Cases connectors', () => { +// Skipping flakey test: https://github.com/elastic/kibana/issues/115438 +describe.skip('Cases connectors', () => { const configureResult = { connector: { id: 'e271c3b8-f702-4fbc-98e0-db942b573bbd', From 75048dc13b00d1ab8cde9e6e7d9db9829636cd05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Marcondes?= <55978943+cauemarcondes@users.noreply.github.com> Date: Mon, 18 Oct 2021 19:53:20 -0400 Subject: [PATCH 020/204] [APM] Ensure APM deprecation documentationUrl point to correct doc branch (#115401) * using branch in the url * fixing TS --- x-pack/plugins/apm/server/deprecations/deprecations.test.ts | 5 ++++- x-pack/plugins/apm/server/deprecations/index.ts | 5 +++-- x-pack/plugins/apm/server/plugin.ts | 2 ++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/apm/server/deprecations/deprecations.test.ts b/x-pack/plugins/apm/server/deprecations/deprecations.test.ts index d706146faf212..43e8140fb9b3c 100644 --- a/x-pack/plugins/apm/server/deprecations/deprecations.test.ts +++ b/x-pack/plugins/apm/server/deprecations/deprecations.test.ts @@ -19,7 +19,7 @@ const deprecationContext = { describe('getDeprecations', () => { describe('when fleet is disabled', () => { it('returns no deprecations', async () => { - const deprecationsCallback = getDeprecations({}); + const deprecationsCallback = getDeprecations({ branch: 'master' }); const deprecations = await deprecationsCallback(deprecationContext); expect(deprecations).toEqual([]); }); @@ -28,6 +28,7 @@ describe('getDeprecations', () => { describe('when running on cloud with legacy apm-server', () => { it('returns deprecations', async () => { const deprecationsCallback = getDeprecations({ + branch: 'master', cloudSetup: { isCloudEnabled: true } as unknown as CloudSetup, fleet: { start: () => ({ @@ -43,6 +44,7 @@ describe('getDeprecations', () => { describe('when running on cloud with fleet', () => { it('returns no deprecations', async () => { const deprecationsCallback = getDeprecations({ + branch: 'master', cloudSetup: { isCloudEnabled: true } as unknown as CloudSetup, fleet: { start: () => ({ @@ -58,6 +60,7 @@ describe('getDeprecations', () => { describe('when running on prem', () => { it('returns no deprecations', async () => { const deprecationsCallback = getDeprecations({ + branch: 'master', cloudSetup: { isCloudEnabled: false } as unknown as CloudSetup, fleet: { start: () => ({ agentPolicyService: { get: () => undefined } }), diff --git a/x-pack/plugins/apm/server/deprecations/index.ts b/x-pack/plugins/apm/server/deprecations/index.ts index b592a2bf13268..76c90270abb8f 100644 --- a/x-pack/plugins/apm/server/deprecations/index.ts +++ b/x-pack/plugins/apm/server/deprecations/index.ts @@ -15,9 +15,11 @@ import { APMRouteHandlerResources } from '../'; export function getDeprecations({ cloudSetup, fleet, + branch, }: { cloudSetup?: CloudSetup; fleet?: APMRouteHandlerResources['plugins']['fleet']; + branch: string; }) { return async ({ savedObjectsClient, @@ -46,8 +48,7 @@ export function getDeprecations({ defaultMessage: 'Running the APM Server binary directly is considered a legacy option and is deprecated since 7.16. Switch to APM Server managed by an Elastic Agent instead. Read our documentation to learn more.', }), - documentationUrl: - 'https://www.elastic.co/guide/en/apm/server/current/apm-integration.html', + documentationUrl: `https://www.elastic.co/guide/en/apm/server/${branch}/apm-integration.html`, level: 'warning', correctiveActions: { manualSteps: [ diff --git a/x-pack/plugins/apm/server/plugin.ts b/x-pack/plugins/apm/server/plugin.ts index d2d8dbf602364..72a1bc483015e 100644 --- a/x-pack/plugins/apm/server/plugin.ts +++ b/x-pack/plugins/apm/server/plugin.ts @@ -215,10 +215,12 @@ export class APMPlugin ); })(); }); + core.deprecations.registerDeprecations({ getDeprecations: getDeprecations({ cloudSetup: plugins.cloud, fleet: resourcePlugins.fleet, + branch: this.initContext.env.packageInfo.branch, }), }); From e673383584c44f8fcb3bca767f9c38f0f7f79393 Mon Sep 17 00:00:00 2001 From: Oliver Gupte Date: Mon, 18 Oct 2021 19:58:35 -0400 Subject: [PATCH 021/204] [APM] APM-Fleet integration version check & upgrade message (#115297) --- x-pack/plugins/apm/common/fleet.ts | 2 + .../components/app/Settings/schema/index.tsx | 4 +- .../schema/migrated/card_footer_content.tsx | 47 +++++++++++++ .../migrated/successful_migration_card.tsx | 30 ++++++++ .../migrated/upgrade_available_card.tsx | 51 ++++++++++++++ .../app/Settings/schema/schema_overview.tsx | 68 +++++-------------- .../public/components/shared/Links/kibana.ts | 11 +++ .../get_apm_package_policy_definition.ts | 7 +- x-pack/plugins/apm/server/routes/fleet.ts | 6 +- 9 files changed, 170 insertions(+), 56 deletions(-) create mode 100644 x-pack/plugins/apm/public/components/app/Settings/schema/migrated/card_footer_content.tsx create mode 100644 x-pack/plugins/apm/public/components/app/Settings/schema/migrated/successful_migration_card.tsx create mode 100644 x-pack/plugins/apm/public/components/app/Settings/schema/migrated/upgrade_available_card.tsx diff --git a/x-pack/plugins/apm/common/fleet.ts b/x-pack/plugins/apm/common/fleet.ts index 618cd20d66159..97551cc16b4be 100644 --- a/x-pack/plugins/apm/common/fleet.ts +++ b/x-pack/plugins/apm/common/fleet.ts @@ -6,3 +6,5 @@ */ export const POLICY_ELASTIC_AGENT_ON_CLOUD = 'policy-elastic-agent-on-cloud'; + +export const SUPPORTED_APM_PACKAGE_VERSION = '7.16.0'; diff --git a/x-pack/plugins/apm/public/components/app/Settings/schema/index.tsx b/x-pack/plugins/apm/public/components/app/Settings/schema/index.tsx index ac32e22fa3ded..b13046d34be94 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/schema/index.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/schema/index.tsx @@ -54,7 +54,8 @@ export function Schema() { const isLoading = status !== FETCH_STATUS.SUCCESS; const cloudApmMigrationEnabled = !!data.cloud_apm_migration_enabled; const hasCloudAgentPolicy = !!data.has_cloud_agent_policy; - const hasCloudApmPackagePolicy = !!data.has_cloud_apm_package_policy; + const cloudApmPackagePolicy = data.cloud_apm_package_policy; + const hasCloudApmPackagePolicy = !!cloudApmPackagePolicy; const hasRequiredRole = !!data.has_required_role; function updateLocalStorage(newStatus: FETCH_STATUS) { @@ -90,6 +91,7 @@ export function Schema() { cloudApmMigrationEnabled={cloudApmMigrationEnabled} hasCloudAgentPolicy={hasCloudAgentPolicy} hasRequiredRole={hasRequiredRole} + cloudApmPackagePolicy={cloudApmPackagePolicy} /> {isSwitchActive && ( + + {i18n.translate( + 'xpack.apm.settings.schema.success.viewIntegrationInFleet.buttonText', + { defaultMessage: 'View the APM integration in Fleet' } + )} + + + +

+ + {i18n.translate( + 'xpack.apm.settings.schema.success.returnText.serviceInventoryLink', + { defaultMessage: 'Service inventory' } + )} + + ), + }} + /> +

+
+
+ ); +} diff --git a/x-pack/plugins/apm/public/components/app/Settings/schema/migrated/successful_migration_card.tsx b/x-pack/plugins/apm/public/components/app/Settings/schema/migrated/successful_migration_card.tsx new file mode 100644 index 0000000000000..839479fbbf652 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/Settings/schema/migrated/successful_migration_card.tsx @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiCard, EuiIcon } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; +import { CardFooterContent } from './card_footer_content'; + +export function SuccessfulMigrationCard() { + return ( + } + title={i18n.translate('xpack.apm.settings.schema.success.title', { + defaultMessage: 'Elastic Agent successfully setup!', + })} + description={i18n.translate( + 'xpack.apm.settings.schema.success.description', + { + defaultMessage: + 'Your APM integration is now setup and ready to receive data from your currently instrumented agents. Feel free to review the policies applied to your integtration.', + } + )} + footer={} + /> + ); +} diff --git a/x-pack/plugins/apm/public/components/app/Settings/schema/migrated/upgrade_available_card.tsx b/x-pack/plugins/apm/public/components/app/Settings/schema/migrated/upgrade_available_card.tsx new file mode 100644 index 0000000000000..8c10236335961 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/Settings/schema/migrated/upgrade_available_card.tsx @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiCard, EuiIcon, EuiLink } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import React from 'react'; +import { useUpgradeApmPackagePolicyHref } from '../../../../shared/Links/kibana'; +import { CardFooterContent } from './card_footer_content'; + +export function UpgradeAvailableCard({ + apmPackagePolicyId, +}: { + apmPackagePolicyId: string | undefined; +}) { + const upgradeApmPackagePolicyHref = + useUpgradeApmPackagePolicyHref(apmPackagePolicyId); + + return ( + } + title={i18n.translate( + 'xpack.apm.settings.schema.upgradeAvailable.title', + { + defaultMessage: 'APM integration upgrade available!', + } + )} + description={ + + {i18n.translate( + 'xpack.apm.settings.schema.upgradeAvailable.upgradePackagePolicyLink', + { defaultMessage: 'Upgrade your APM integration' } + )} + + ), + }} + /> + } + footer={} + /> + ); +} diff --git a/x-pack/plugins/apm/public/components/app/Settings/schema/schema_overview.tsx b/x-pack/plugins/apm/public/components/app/Settings/schema/schema_overview.tsx index 0031c102e8ae5..cead6cd8a6fb4 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/schema/schema_overview.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/schema/schema_overview.tsx @@ -19,11 +19,14 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import React from 'react'; -import { APMLink } from '../../../shared/Links/apm/APMLink'; +import semverLt from 'semver/functions/lt'; +import { SUPPORTED_APM_PACKAGE_VERSION } from '../../../../../common/fleet'; +import { PackagePolicy } from '../../../../../../fleet/common/types'; import { ElasticDocsLink } from '../../../shared/Links/ElasticDocsLink'; -import { useFleetCloudAgentPolicyHref } from '../../../shared/Links/kibana'; import rocketLaunchGraphic from './blog-rocket-720x420.png'; import { MigrationInProgressPanel } from './migration_in_progress_panel'; +import { UpgradeAvailableCard } from './migrated/upgrade_available_card'; +import { SuccessfulMigrationCard } from './migrated/successful_migration_card'; interface Props { onSwitch: () => void; @@ -34,6 +37,7 @@ interface Props { cloudApmMigrationEnabled: boolean; hasCloudAgentPolicy: boolean; hasRequiredRole: boolean; + cloudApmPackagePolicy: PackagePolicy | undefined; } export function SchemaOverview({ onSwitch, @@ -44,10 +48,13 @@ export function SchemaOverview({ cloudApmMigrationEnabled, hasCloudAgentPolicy, hasRequiredRole, + cloudApmPackagePolicy, }: Props) { - const fleetCloudAgentPolicyHref = useFleetCloudAgentPolicyHref(); const isDisabled = !cloudApmMigrationEnabled || !hasCloudAgentPolicy || !hasRequiredRole; + const packageVersion = cloudApmPackagePolicy?.package?.version; + const isUpgradeAvailable = + packageVersion && semverLt(packageVersion, SUPPORTED_APM_PACKAGE_VERSION); if (isLoading) { return ( @@ -76,54 +83,13 @@ export function SchemaOverview({ - - } - title={i18n.translate('xpack.apm.settings.schema.success.title', { - defaultMessage: 'Elastic Agent successfully setup!', - })} - description={i18n.translate( - 'xpack.apm.settings.schema.success.description', - { - defaultMessage: - 'Your APM integration is now setup and ready to receive data from your currently instrumented agents. Feel free to review the policies applied to your integtration.', - } - )} - footer={ -
- - {i18n.translate( - 'xpack.apm.settings.schema.success.viewIntegrationInFleet.buttonText', - { defaultMessage: 'View the APM integration in Fleet' } - )} - - - -

- - {i18n.translate( - 'xpack.apm.settings.schema.success.returnText.serviceInventoryLink', - { defaultMessage: 'Service inventory' } - )} - - ), - }} - /> -

-
-
- } - /> + {isUpgradeAvailable ? ( + + ) : ( + + )}
diff --git a/x-pack/plugins/apm/public/components/shared/Links/kibana.ts b/x-pack/plugins/apm/public/components/shared/Links/kibana.ts index bfb7cf849f567..c0bdf3a98aa31 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/kibana.ts +++ b/x-pack/plugins/apm/public/components/shared/Links/kibana.ts @@ -26,3 +26,14 @@ export function useFleetCloudAgentPolicyHref() { } = useApmPluginContext(); return basePath.prepend('/app/fleet#/policies/policy-elastic-agent-on-cloud'); } + +export function useUpgradeApmPackagePolicyHref(packagePolicyId = '') { + const { + core: { + http: { basePath }, + }, + } = useApmPluginContext(); + return basePath.prepend( + `/app/fleet/policies/policy-elastic-agent-on-cloud/upgrade-package-policy/${packagePolicyId}?from=integrations-policy-list` + ); +} diff --git a/x-pack/plugins/apm/server/lib/fleet/get_apm_package_policy_definition.ts b/x-pack/plugins/apm/server/lib/fleet/get_apm_package_policy_definition.ts index 64b071b67d2bd..98b6a6489c47b 100644 --- a/x-pack/plugins/apm/server/lib/fleet/get_apm_package_policy_definition.ts +++ b/x-pack/plugins/apm/server/lib/fleet/get_apm_package_policy_definition.ts @@ -5,7 +5,10 @@ * 2.0. */ -import { POLICY_ELASTIC_AGENT_ON_CLOUD } from '../../../common/fleet'; +import { + POLICY_ELASTIC_AGENT_ON_CLOUD, + SUPPORTED_APM_PACKAGE_VERSION, +} from '../../../common/fleet'; import { APMPluginSetupDependencies } from '../../types'; import { APM_PACKAGE_NAME } from './get_cloud_apm_package_policy'; @@ -36,7 +39,7 @@ export function getApmPackagePolicyDefinition( ], package: { name: APM_PACKAGE_NAME, - version: '0.4.0', + version: SUPPORTED_APM_PACKAGE_VERSION, title: 'Elastic APM', }, }; diff --git a/x-pack/plugins/apm/server/routes/fleet.ts b/x-pack/plugins/apm/server/routes/fleet.ts index 2884c08ceb9a1..e18aefcd6e0d8 100644 --- a/x-pack/plugins/apm/server/routes/fleet.ts +++ b/x-pack/plugins/apm/server/routes/fleet.ts @@ -92,7 +92,7 @@ const fleetAgentsRoute = createApmServerRoute({ }); const saveApmServerSchemaRoute = createApmServerRoute({ - endpoint: 'POST /internal/apm/fleet/apm_server_schema', + endpoint: 'POST /api/apm/fleet/apm_server_schema', options: { tags: ['access:apm', 'access:apm_write'] }, params: t.type({ body: t.type({ @@ -143,11 +143,13 @@ const getMigrationCheckRoute = createApmServerRoute({ fleetPluginStart, }) : undefined; + const apmPackagePolicy = getApmPackagePolicy(cloudAgentPolicy); return { has_cloud_agent_policy: !!cloudAgentPolicy, - has_cloud_apm_package_policy: !!getApmPackagePolicy(cloudAgentPolicy), + has_cloud_apm_package_policy: !!apmPackagePolicy, cloud_apm_migration_enabled: cloudApmMigrationEnabled, has_required_role: hasRequiredRole, + cloud_apm_package_policy: apmPackagePolicy, }; }, }); From 5e58fbded0592b1006c289341f376d89f1f88793 Mon Sep 17 00:00:00 2001 From: Georgii Gorbachev Date: Tue, 19 Oct 2021 02:29:14 +0200 Subject: [PATCH 022/204] [Security Solution][Detections] Fix a bug in siem-detection-engine-rule-status Saved Object migration to SO references (#115355) **Ticket:** https://github.com/elastic/kibana/issues/107068 **Follow-up after:** https://github.com/elastic/kibana/pull/114585 ## Summary The existing migration function `legacyMigrateRuleAlertIdSOReferences` that migrates `alertId` fields to SO references array did not include all the other attributes of a `siem-detection-engine-rule-status` doc being migrated to the resulting doc. This PR includes a fix and an integration test for that. ## Run the test To run the test, in one terminal execute: ``` cd ${KIBANA_HOME} && node scripts/functional_tests_server --config x-pack/test/detection_engine_api_integration/security_and_spaces/config.ts ``` In another terminal execute: ``` cd ${KIBANA_HOME} && node scripts/functional_test_runner --config x-pack/test/detection_engine_api_integration/security_and_spaces/config.ts --include=x-pack/test/detection_engine_api_integration/security_and_spaces/tests/migrations.ts ``` ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --- .../legacy_rule_status/legacy_migrations.ts | 63 ++++++++----------- .../security_and_spaces/tests/migrations.ts | 25 ++++++++ 2 files changed, 52 insertions(+), 36 deletions(-) diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/legacy_rule_status/legacy_migrations.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/legacy_rule_status/legacy_migrations.ts index 92d7487be0cdb..72ab4a2237ba1 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/legacy_rule_status/legacy_migrations.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/legacy_rule_status/legacy_migrations.ts @@ -18,34 +18,19 @@ import { IRuleSavedAttributesSavedObjectAttributes } from '../types'; import { legacyGetRuleReference } from './legacy_utils'; export const truncateMessageFields: SavedObjectMigrationFn> = (doc) => { - const { lastFailureMessage, lastSuccessMessage, ...restAttributes } = doc.attributes; + const { lastFailureMessage, lastSuccessMessage, ...otherAttributes } = doc.attributes; return { ...doc, attributes: { + ...otherAttributes, lastFailureMessage: truncateMessage(lastFailureMessage), lastSuccessMessage: truncateMessage(lastSuccessMessage), - ...restAttributes, }, references: doc.references ?? [], }; }; -/** - * This side-car rule status SO is deprecated and is to be replaced by the RuleExecutionLog on Event-Log and - * additional fields on the Alerting Framework Rule SO. - * - * @deprecated Remove this once we've fully migrated to event-log and no longer require addition status SO (8.x) - */ -export const legacyRuleStatusSavedObjectMigration = { - '7.15.2': truncateMessageFields, - '7.16.0': ( - doc: SavedObjectUnsanitizedDoc - ): SavedObjectSanitizedDoc => { - return legacyMigrateRuleAlertIdSOReferences(doc); - }, -}; - /** * This migrates alertId within legacy `siem-detection-engine-rule-status` to saved object references on an upgrade. * We only migrate alertId if we find these conditions: @@ -62,29 +47,24 @@ export const legacyRuleStatusSavedObjectMigration = { export const legacyMigrateRuleAlertIdSOReferences = ( doc: SavedObjectUnsanitizedDoc ): SavedObjectSanitizedDoc => { - const { references } = doc; - - // Isolate alertId from the doc - const { alertId, ...attributesWithoutAlertId } = doc.attributes; - const existingReferences = references ?? []; + const { alertId, ...otherAttributes } = doc.attributes; + const existingReferences = doc.references ?? []; + // early return if alertId is not a string as expected if (!isString(alertId)) { - // early return if alertId is not a string as expected return { ...doc, references: existingReferences }; - } else { - const alertReferences = legacyMigrateAlertId({ - alertId, - existingReferences, - }); - - return { - ...doc, - attributes: { - ...attributesWithoutAlertId.attributes, - }, - references: [...existingReferences, ...alertReferences], - }; } + + const alertReferences = legacyMigrateAlertId({ + alertId, + existingReferences, + }); + + return { + ...doc, + attributes: otherAttributes, + references: [...existingReferences, ...alertReferences], + }; }; /** @@ -113,3 +93,14 @@ export const legacyMigrateAlertId = ({ return [legacyGetRuleReference(alertId)]; } }; + +/** + * This side-car rule status SO is deprecated and is to be replaced by the RuleExecutionLog on Event-Log and + * additional fields on the Alerting Framework Rule SO. + * + * @deprecated Remove this once we've fully migrated to event-log and no longer require addition status SO (8.x) + */ +export const legacyRuleStatusSavedObjectMigration = { + '7.15.2': truncateMessageFields, + '7.16.0': legacyMigrateRuleAlertIdSOReferences, +}; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/migrations.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/migrations.ts index 6d1d64a04cd93..cfae7532ba496 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/migrations.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/migrations.ts @@ -6,6 +6,7 @@ */ import expect from '@kbn/expect'; +import { IRuleStatusSOAttributes } from '../../../../plugins/security_solution/server/lib/detection_engine/rules/types'; import { FtrProviderContext } from '../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export @@ -113,6 +114,30 @@ export default ({ getService }: FtrProviderContext): void => { undefined ); }); + + it('migrates legacy siem-detection-engine-rule-status and retains other attributes as the same attributes as before', async () => { + const response = await es.get<{ + 'siem-detection-engine-rule-status': IRuleStatusSOAttributes; + }>({ + index: '.kibana', + id: 'siem-detection-engine-rule-status:d62d2980-27c4-11ec-92b0-f7b47106bb35', + }); + expect(response.statusCode).to.eql(200); + + expect(response.body._source?.['siem-detection-engine-rule-status']).to.eql({ + statusDate: '2021-10-11T20:51:26.622Z', + status: 'succeeded', + lastFailureAt: '2021-10-11T18:10:08.982Z', + lastSuccessAt: '2021-10-11T20:51:26.622Z', + lastFailureMessage: + '4 days (323690920ms) were not queried between this rule execution and the last execution, so signals may have been missed. Consider increasing your look behind time or adding more Kibana instances. name: "Threshy" id: "fb1046a0-0452-11ec-9b15-d13d79d162f3" rule id: "b789c80f-f6d8-41f1-8b4f-b4a23342cde2" signals index: ".siem-signals-spong-default"', + lastSuccessMessage: 'succeeded', + gap: '4 days', + bulkCreateTimeDurations: ['34.49'], + searchAfterTimeDurations: ['62.58'], + lastLookBackDate: null, + }); + }); }); }); }; From a8b616aaa29e52863526284c1b038eda1a3a5944 Mon Sep 17 00:00:00 2001 From: Thomas Neirynck Date: Mon, 18 Oct 2021 20:31:07 -0400 Subject: [PATCH 023/204] [Fleet] Add beta flag for custom integrations (#115447) --- .../integrations/sections/epm/screens/home/index.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/index.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/index.tsx index 2d1077f586a1a..4270d360b9294 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/index.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/index.tsx @@ -66,6 +66,13 @@ export const mapToCard = ( uiInternalPathUrl = url; } + let release: 'ga' | 'beta' | 'experimental' | undefined; + if ('release' in item) { + release = item.release; + } else if (item.isBeta === true) { + release = 'beta'; + } + return { id: `${item.type === 'ui_link' ? 'ui_link' : 'epr'}-${item.id}`, description: item.description, @@ -75,7 +82,7 @@ export const mapToCard = ( integration: 'integration' in item ? item.integration || '' : '', name: 'name' in item ? item.name || '' : '', version: 'version' in item ? item.version || '' : '', - release: 'release' in item ? item.release : undefined, + release, categories: ((item.categories || []) as string[]).filter((c: string) => !!c), }; }; From 083a2b9523d10fa8891563274abbca360a4216b7 Mon Sep 17 00:00:00 2001 From: Brian Seeders Date: Mon, 18 Oct 2021 21:08:50 -0400 Subject: [PATCH 024/204] skip flaky suite (#115488) --- .../test/security_solution_endpoint_api_int/apis/metadata.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/metadata.ts b/x-pack/test/security_solution_endpoint_api_int/apis/metadata.ts index 35fe0cdd6da25..2dcf36cc42ae2 100644 --- a/x-pack/test/security_solution_endpoint_api_int/apis/metadata.ts +++ b/x-pack/test/security_solution_endpoint_api_int/apis/metadata.ts @@ -24,7 +24,8 @@ export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); - describe('test metadata api', () => { + // Failing: See https://github.com/elastic/kibana/issues/115488 + describe.skip('test metadata api', () => { // TODO add this after endpoint package changes are merged and in snapshot // describe('with .metrics-endpoint.metadata_united_default index', () => { // }); From b103a544cc71d0998074b94c6a00a5da5d6e78ae Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Mon, 18 Oct 2021 22:43:00 -0400 Subject: [PATCH 025/204] [Uptime] Fix unhandled promise rejection failure (#114883) * Fix unhandled promise rejection failure. * Mock monaco to avoid editor-related errors failing test. * Update assertion. Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../components/fleet_package/custom_fields.test.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/uptime/public/components/fleet_package/custom_fields.test.tsx b/x-pack/plugins/uptime/public/components/fleet_package/custom_fields.test.tsx index 26ee26cc8ed7f..62c6f5598adb4 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/custom_fields.test.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/custom_fields.test.tsx @@ -313,11 +313,11 @@ describe.skip('', () => { // resolve errors fireEvent.click(monitorType); - waitFor(() => { - expect(getByText('http')).toBeInTheDocument(); - expect(getByText('tcp')).toBeInTheDocument(); - expect(getByText('icmp')).toBeInTheDocument(); - expect(queryByText('browser')).not.toBeInTheDocument(); + await waitFor(() => { + expect(getByText('HTTP')).toBeInTheDocument(); + expect(getByText('TCP')).toBeInTheDocument(); + expect(getByText('ICMP')).toBeInTheDocument(); + expect(queryByText('Browser')).not.toBeInTheDocument(); }); }); }); From d0bc10f896a30c4b08c5fc302cd98c661d6847e3 Mon Sep 17 00:00:00 2001 From: Ashokaditya Date: Tue, 19 Oct 2021 05:27:12 +0200 Subject: [PATCH 026/204] [Security Solution][Endpoint]Activity Log API/UX changes (#114905) * rename legacy actions/responses fixes elastic/security-team/issues/1702 * use correct name for responses index refs elastic/kibana/pull/113621 * extract helper method to utils * append endpoint responses docs to activity log * Show completed responses on activity log fixes elastic/security-team/issues/1703 * remove width restriction on date picker * add a simple test to verify endpoint responses fixes elastic/security-team/issues/1702 * find unique action_ids from `.fleet-actions` and `.logs-endpoint.actions-default` indices fixes elastic/security-team/issues/1702 * do not filter out endpoint only actions/responses that did not make it to Fleet review comments * use a constant to manage various doc types review comments * refactor `getActivityLog` Simplify `getActivityLog` so it is easier to reason with. review comments * skip this for now will mock this better in a new PR * improve types * display endpoint actions similar to fleet actions, but with success icon color * Correctly do mocks for tests * Include only errored endpoint actions, remove successful duplicates fixes elastic/security-team/issues/1703 * Update tests to use non duplicate action_ids review comments fixes elastic/security-team/issues/1703 * show correct action title review fixes * statusCode constant review change * rename review changes * Update translations.ts refs https://github.com/elastic/kibana/pull/114905/commits/74a8340b5eb2e31faba67a4fbe656f74fe52d0a2 Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../common/endpoint/constants.ts | 4 +- .../common/endpoint/types/actions.ts | 33 ++- .../activity_log_date_range_picker/index.tsx | 1 - .../view/details/components/log_entry.tsx | 101 ++++++- .../components/log_entry_timeline_icon.tsx | 21 +- .../view/details/endpoints.stories.tsx | 14 +- .../pages/endpoint_hosts/view/index.test.tsx | 95 +++++-- .../pages/endpoint_hosts/view/translations.ts | 36 +++ .../endpoint/routes/actions/audit_log.test.ts | 213 ++++++++++++-- .../endpoint/routes/actions/isolation.ts | 29 +- .../server/endpoint/routes/actions/mocks.ts | 32 +++ .../server/endpoint/services/actions.ts | 146 ++++------ .../endpoint/utils/audit_log_helpers.ts | 266 ++++++++++++++++++ .../server/endpoint/utils/index.ts | 2 + .../endpoint/utils/yes_no_data_stream.test.ts | 100 +++++++ .../endpoint/utils/yes_no_data_stream.ts | 59 ++++ 16 files changed, 949 insertions(+), 203 deletions(-) create mode 100644 x-pack/plugins/security_solution/server/endpoint/utils/audit_log_helpers.ts create mode 100644 x-pack/plugins/security_solution/server/endpoint/utils/yes_no_data_stream.test.ts create mode 100644 x-pack/plugins/security_solution/server/endpoint/utils/yes_no_data_stream.ts diff --git a/x-pack/plugins/security_solution/common/endpoint/constants.ts b/x-pack/plugins/security_solution/common/endpoint/constants.ts index 6e9123da2dd9b..178a2b68a4aab 100644 --- a/x-pack/plugins/security_solution/common/endpoint/constants.ts +++ b/x-pack/plugins/security_solution/common/endpoint/constants.ts @@ -10,7 +10,7 @@ export const ENDPOINT_ACTIONS_DS = '.logs-endpoint.actions'; export const ENDPOINT_ACTIONS_INDEX = `${ENDPOINT_ACTIONS_DS}-default`; export const ENDPOINT_ACTION_RESPONSES_DS = '.logs-endpoint.action.responses'; -export const ENDPOINT_ACTION_RESPONSES_INDEX = `${ENDPOINT_ACTIONS_DS}-default`; +export const ENDPOINT_ACTION_RESPONSES_INDEX = `${ENDPOINT_ACTION_RESPONSES_DS}-default`; export const eventsIndexPattern = 'logs-endpoint.events.*'; export const alertsIndexPattern = 'logs-endpoint.alerts-*'; @@ -60,3 +60,5 @@ export const UNISOLATE_HOST_ROUTE = `${BASE_ENDPOINT_ROUTE}/unisolate`; /** Endpoint Actions Log Routes */ export const ENDPOINT_ACTION_LOG_ROUTE = `/api/endpoint/action_log/{agent_id}`; export const ACTION_STATUS_ROUTE = `/api/endpoint/action_status`; + +export const failedFleetActionErrorCode = '424'; diff --git a/x-pack/plugins/security_solution/common/endpoint/types/actions.ts b/x-pack/plugins/security_solution/common/endpoint/types/actions.ts index bc46ca2f5b451..fb29297eb5929 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types/actions.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types/actions.ts @@ -10,6 +10,13 @@ import { ActionStatusRequestSchema, HostIsolationRequestSchema } from '../schema export type ISOLATION_ACTIONS = 'isolate' | 'unisolate'; +export const ActivityLogItemTypes = { + ACTION: 'action' as const, + RESPONSE: 'response' as const, + FLEET_ACTION: 'fleetAction' as const, + FLEET_RESPONSE: 'fleetResponse' as const, +}; + interface EcsError { code?: string; id?: string; @@ -87,8 +94,24 @@ export interface EndpointActionResponse { action_data: EndpointActionData; } +export interface EndpointActivityLogAction { + type: typeof ActivityLogItemTypes.ACTION; + item: { + id: string; + data: LogsEndpointAction; + }; +} + +export interface EndpointActivityLogActionResponse { + type: typeof ActivityLogItemTypes.RESPONSE; + item: { + id: string; + data: LogsEndpointActionResponse; + }; +} + export interface ActivityLogAction { - type: 'action'; + type: typeof ActivityLogItemTypes.FLEET_ACTION; item: { // document _id id: string; @@ -97,7 +120,7 @@ export interface ActivityLogAction { }; } export interface ActivityLogActionResponse { - type: 'response'; + type: typeof ActivityLogItemTypes.FLEET_RESPONSE; item: { // document id id: string; @@ -105,7 +128,11 @@ export interface ActivityLogActionResponse { data: EndpointActionResponse; }; } -export type ActivityLogEntry = ActivityLogAction | ActivityLogActionResponse; +export type ActivityLogEntry = + | ActivityLogAction + | ActivityLogActionResponse + | EndpointActivityLogAction + | EndpointActivityLogActionResponse; export interface ActivityLog { page: number; pageSize: number; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/activity_log_date_range_picker/index.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/activity_log_date_range_picker/index.tsx index 05887d82cacad..a57fa8d8e4ce5 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/activity_log_date_range_picker/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/activity_log_date_range_picker/index.tsx @@ -32,7 +32,6 @@ interface Range { const DatePickerWrapper = styled.div` width: ${(props) => props.theme.eui.fractions.single.percentage}; - max-width: 350px; `; const StickyFlexItem = styled(EuiFlexItem)` background: ${(props) => `${props.theme.eui.euiHeaderBackgroundColor}`}; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/log_entry.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/log_entry.tsx index bbe0a6f3afcd1..79af2ecb354fd 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/log_entry.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/log_entry.tsx @@ -9,24 +9,34 @@ import React, { memo, useMemo } from 'react'; import styled from 'styled-components'; import { EuiComment, EuiText, EuiAvatarProps, EuiCommentProps, IconType } from '@elastic/eui'; -import { Immutable, ActivityLogEntry } from '../../../../../../../common/endpoint/types'; +import { + Immutable, + ActivityLogEntry, + ActivityLogItemTypes, +} from '../../../../../../../common/endpoint/types'; import { FormattedRelativePreferenceDate } from '../../../../../../common/components/formatted_date'; import { LogEntryTimelineIcon } from './log_entry_timeline_icon'; +import { useEuiTheme } from '../../../../../../common/lib/theme/use_eui_theme'; import * as i18 from '../../translations'; const useLogEntryUIProps = ( - logEntry: Immutable + logEntry: Immutable, + theme: ReturnType ): { actionEventTitle: string; + avatarColor: EuiAvatarProps['color']; + avatarIconColor: EuiAvatarProps['iconColor']; avatarSize: EuiAvatarProps['size']; commentText: string; commentType: EuiCommentProps['type']; displayComment: boolean; displayResponseEvent: boolean; + failedActionEventTitle: string; iconType: IconType; isResponseEvent: boolean; isSuccessful: boolean; + isCompleted: boolean; responseEventTitle: string; username: string | React.ReactNode; } => { @@ -34,15 +44,19 @@ const useLogEntryUIProps = ( let iconType: IconType = 'dot'; let commentType: EuiCommentProps['type'] = 'update'; let commentText: string = ''; + let avatarColor: EuiAvatarProps['color'] = theme.euiColorLightestShade; + let avatarIconColor: EuiAvatarProps['iconColor']; let avatarSize: EuiAvatarProps['size'] = 's'; + let failedActionEventTitle: string = ''; let isIsolateAction: boolean = false; let isResponseEvent: boolean = false; let isSuccessful: boolean = false; + let isCompleted: boolean = false; let displayComment: boolean = false; let displayResponseEvent: boolean = true; let username: EuiCommentProps['username'] = ''; - if (logEntry.type === 'action') { + if (logEntry.type === ActivityLogItemTypes.FLEET_ACTION) { avatarSize = 'm'; commentType = 'regular'; commentText = logEntry.item.data.data.comment?.trim() ?? ''; @@ -59,13 +73,51 @@ const useLogEntryUIProps = ( displayComment = true; } } - } else if (logEntry.type === 'response') { + } + if (logEntry.type === ActivityLogItemTypes.ACTION) { + avatarSize = 'm'; + commentType = 'regular'; + commentText = logEntry.item.data.EndpointActions.data.comment?.trim() ?? ''; + displayResponseEvent = false; + iconType = 'lockOpen'; + username = logEntry.item.data.user.id; + avatarIconColor = theme.euiColorVis9_behindText; + failedActionEventTitle = i18.ACTIVITY_LOG.LogEntry.action.failedEndpointReleaseAction; + if (logEntry.item.data.EndpointActions.data) { + const data = logEntry.item.data.EndpointActions.data; + if (data.command === 'isolate') { + iconType = 'lock'; + failedActionEventTitle = i18.ACTIVITY_LOG.LogEntry.action.failedEndpointIsolateAction; + } + if (commentText) { + displayComment = true; + } + } + } else if (logEntry.type === ActivityLogItemTypes.FLEET_RESPONSE) { isResponseEvent = true; if (logEntry.item.data.action_data.command === 'isolate') { isIsolateAction = true; } if (!!logEntry.item.data.completed_at && !logEntry.item.data.error) { isSuccessful = true; + } else { + avatarColor = theme.euiColorVis9_behindText; + } + } else if (logEntry.type === ActivityLogItemTypes.RESPONSE) { + iconType = 'check'; + isResponseEvent = true; + if (logEntry.item.data.EndpointActions.data.command === 'isolate') { + isIsolateAction = true; + } + if (logEntry.item.data.EndpointActions.completed_at) { + isCompleted = true; + if (!logEntry.item.data.error) { + isSuccessful = true; + avatarColor = theme.euiColorVis0_behindText; + } else { + isSuccessful = false; + avatarColor = theme.euiColorVis9_behindText; + } } } @@ -75,13 +127,23 @@ const useLogEntryUIProps = ( const getResponseEventTitle = () => { if (isIsolateAction) { - if (isSuccessful) { + if (isCompleted) { + if (isSuccessful) { + return i18.ACTIVITY_LOG.LogEntry.response.unisolationCompletedAndSuccessful; + } + return i18.ACTIVITY_LOG.LogEntry.response.unisolationCompletedAndUnsuccessful; + } else if (isSuccessful) { return i18.ACTIVITY_LOG.LogEntry.response.isolationSuccessful; } else { return i18.ACTIVITY_LOG.LogEntry.response.isolationFailed; } } else { - if (isSuccessful) { + if (isCompleted) { + if (isSuccessful) { + return i18.ACTIVITY_LOG.LogEntry.response.unisolationCompletedAndSuccessful; + } + return i18.ACTIVITY_LOG.LogEntry.response.unisolationCompletedAndUnsuccessful; + } else if (isSuccessful) { return i18.ACTIVITY_LOG.LogEntry.response.unisolationSuccessful; } else { return i18.ACTIVITY_LOG.LogEntry.response.unisolationFailed; @@ -91,18 +153,22 @@ const useLogEntryUIProps = ( return { actionEventTitle, + avatarColor, + avatarIconColor, avatarSize, commentText, commentType, displayComment, displayResponseEvent, + failedActionEventTitle, iconType, isResponseEvent, isSuccessful, + isCompleted, responseEventTitle: getResponseEventTitle(), username, }; - }, [logEntry]); + }, [logEntry, theme]); }; const StyledEuiComment = styled(EuiComment)` @@ -126,28 +192,41 @@ const StyledEuiComment = styled(EuiComment)` `; export const LogEntry = memo(({ logEntry }: { logEntry: Immutable }) => { + const theme = useEuiTheme(); const { actionEventTitle, + avatarColor, + avatarIconColor, avatarSize, commentText, commentType, displayComment, displayResponseEvent, + failedActionEventTitle, iconType, isResponseEvent, - isSuccessful, responseEventTitle, username, - } = useLogEntryUIProps(logEntry); + } = useLogEntryUIProps(logEntry, theme); return ( } - event={{displayResponseEvent ? responseEventTitle : actionEventTitle}} + event={ + + {displayResponseEvent + ? responseEventTitle + : failedActionEventTitle + ? failedActionEventTitle + : actionEventTitle} + + } timelineIcon={ - + } data-test-subj="timelineEntry" > diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/log_entry_timeline_icon.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/log_entry_timeline_icon.tsx index 3ff311cd8a139..25e7c7d2c4a49 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/log_entry_timeline_icon.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/log_entry_timeline_icon.tsx @@ -7,32 +7,27 @@ import React, { memo } from 'react'; import { EuiAvatar, EuiAvatarProps } from '@elastic/eui'; -import { useEuiTheme } from '../../../../../../common/lib/theme/use_eui_theme'; export const LogEntryTimelineIcon = memo( ({ + avatarColor, + avatarIconColor, avatarSize, - isResponseEvent, - isSuccessful, iconType, + isResponseEvent, }: { + avatarColor: EuiAvatarProps['color']; + avatarIconColor?: EuiAvatarProps['iconColor']; avatarSize: EuiAvatarProps['size']; - isResponseEvent: boolean; - isSuccessful: boolean; iconType: EuiAvatarProps['iconType']; + isResponseEvent: boolean; }) => { - const euiTheme = useEuiTheme(); - return ( ); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoints.stories.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoints.stories.tsx index 123a51e5a52bd..717368a1ff3a0 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoints.stories.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoints.stories.tsx @@ -8,7 +8,11 @@ import React, { ComponentType } from 'react'; import moment from 'moment'; -import { ActivityLog, Immutable } from '../../../../../../common/endpoint/types'; +import { + ActivityLog, + Immutable, + ActivityLogItemTypes, +} from '../../../../../../common/endpoint/types'; import { EndpointDetailsFlyoutTabs } from './components/endpoint_details_tabs'; import { EndpointActivityLog } from './endpoint_activity_log'; import { EndpointDetailsFlyout } from '.'; @@ -26,7 +30,7 @@ export const dummyEndpointActivityLog = ( endDate: moment().toString(), data: [ { - type: 'action', + type: ActivityLogItemTypes.FLEET_ACTION, item: { id: '', data: { @@ -44,7 +48,7 @@ export const dummyEndpointActivityLog = ( }, }, { - type: 'action', + type: ActivityLogItemTypes.FLEET_ACTION, item: { id: '', data: { @@ -63,7 +67,7 @@ export const dummyEndpointActivityLog = ( }, }, { - type: 'action', + type: ActivityLogItemTypes.FLEET_ACTION, item: { id: '', data: { @@ -82,7 +86,7 @@ export const dummyEndpointActivityLog = ( }, }, { - type: 'action', + type: ActivityLogItemTypes.FLEET_ACTION, item: { id: '', data: { diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx index b2c438659b771..727c2e8a35024 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx @@ -42,6 +42,7 @@ import { import { getCurrentIsolationRequestState } from '../store/selectors'; import { licenseService } from '../../../../common/hooks/use_license'; import { FleetActionGenerator } from '../../../../../common/endpoint/data_generators/fleet_action_generator'; +import { EndpointActionGenerator } from '../../../../../common/endpoint/data_generators/endpoint_action_generator'; import { APP_PATH, MANAGEMENT_PATH, @@ -807,7 +808,7 @@ describe('when on the endpoint list page', () => { let renderResult: ReturnType; const agentId = 'some_agent_id'; - let getMockData: () => ActivityLog; + let getMockData: (option?: { hasLogsEndpointActionResponses?: boolean }) => ActivityLog; beforeEach(async () => { window.IntersectionObserver = jest.fn(() => ({ root: null, @@ -828,10 +829,15 @@ describe('when on the endpoint list page', () => { }); const fleetActionGenerator = new FleetActionGenerator('seed'); - const responseData = fleetActionGenerator.generateResponse({ + const endpointActionGenerator = new EndpointActionGenerator('seed'); + const endpointResponseData = endpointActionGenerator.generateResponse({ + agent: { id: agentId }, + }); + const fleetResponseData = fleetActionGenerator.generateResponse({ agent_id: agentId, }); - const actionData = fleetActionGenerator.generate({ + + const fleetActionData = fleetActionGenerator.generate({ agents: [agentId], data: { comment: 'some comment', @@ -844,35 +850,49 @@ describe('when on the endpoint list page', () => { }, }); - getMockData = () => ({ - page: 1, - pageSize: 50, - startDate: 'now-1d', - endDate: 'now', - data: [ - { - type: 'response', - item: { - id: 'some_id_0', - data: responseData, + getMockData = (hasLogsEndpointActionResponses?: { + hasLogsEndpointActionResponses?: boolean; + }) => { + const response: ActivityLog = { + page: 1, + pageSize: 50, + startDate: 'now-1d', + endDate: 'now', + data: [ + { + type: 'fleetResponse', + item: { + id: 'some_id_1', + data: fleetResponseData, + }, }, - }, - { - type: 'action', - item: { - id: 'some_id_1', - data: actionData, + { + type: 'fleetAction', + item: { + id: 'some_id_2', + data: fleetActionData, + }, }, - }, - { - type: 'action', + { + type: 'fleetAction', + item: { + id: 'some_id_3', + data: isolatedActionData, + }, + }, + ], + }; + if (hasLogsEndpointActionResponses) { + response.data.unshift({ + type: 'response', item: { - id: 'some_id_3', - data: isolatedActionData, + id: 'some_id_0', + data: endpointResponseData, }, - }, - ], - }); + }); + } + return response; + }; renderResult = render(); await reactTestingLibrary.act(async () => { @@ -912,6 +932,25 @@ describe('when on the endpoint list page', () => { expect(`${logEntries[1]} .euiCommentTimeline__icon--regular`).not.toBe(null); }); + it('should display log accurately with endpoint responses', async () => { + const activityLogTab = await renderResult.findByTestId('activity_log'); + reactTestingLibrary.act(() => { + reactTestingLibrary.fireEvent.click(activityLogTab); + }); + await middlewareSpy.waitForAction('endpointDetailsActivityLogChanged'); + reactTestingLibrary.act(() => { + dispatchEndpointDetailsActivityLogChanged( + 'success', + getMockData({ hasLogsEndpointActionResponses: true }) + ); + }); + const logEntries = await renderResult.queryAllByTestId('timelineEntry'); + expect(logEntries.length).toEqual(4); + expect(`${logEntries[0]} .euiCommentTimeline__icon--update`).not.toBe(null); + expect(`${logEntries[1]} .euiCommentTimeline__icon--update`).not.toBe(null); + expect(`${logEntries[2]} .euiCommentTimeline__icon--regular`).not.toBe(null); + }); + it('should display empty state when API call has failed', async () => { const activityLogTab = await renderResult.findByTestId('activity_log'); reactTestingLibrary.act(() => { diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/translations.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/translations.ts index c8a29eed3fda7..9cd55a70005ec 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/translations.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/translations.ts @@ -56,8 +56,44 @@ export const ACTIVITY_LOG = { defaultMessage: 'submitted request: Release host', } ), + failedEndpointReleaseAction: i18n.translate( + 'xpack.securitySolution.endpointDetails.activityLog.logEntry.action.failedEndpointReleaseAction', + { + defaultMessage: 'failed to submit request: Release host', + } + ), + failedEndpointIsolateAction: i18n.translate( + 'xpack.securitySolution.endpointDetails.activityLog.logEntry.action.failedEndpointIsolateAction', + { + defaultMessage: 'failed to submit request: Isolate host', + } + ), }, response: { + isolationCompletedAndSuccessful: i18n.translate( + 'xpack.securitySolution.endpointDetails.activityLog.logEntry.response.isolationCompletedAndSuccessful', + { + defaultMessage: 'Host isolation request completed by Endpoint', + } + ), + isolationCompletedAndUnsuccessful: i18n.translate( + 'xpack.securitySolution.endpointDetails.activityLog.logEntry.response.isolationCompletedAndUnsuccessful', + { + defaultMessage: 'Host isolation request completed by Endpoint with errors', + } + ), + unisolationCompletedAndSuccessful: i18n.translate( + 'xpack.securitySolution.endpointDetails.activityLog.logEntry.response.unisolationCompletedAndSuccessful', + { + defaultMessage: 'Release request completed by Endpoint', + } + ), + unisolationCompletedAndUnsuccessful: i18n.translate( + 'xpack.securitySolution.endpointDetails.activityLog.logEntry.response.unisolationCompletedAndUnsuccessful', + { + defaultMessage: 'Release request completed by Endpoint with errors', + } + ), isolationSuccessful: i18n.translate( 'xpack.securitySolution.endpointDetails.activityLog.logEntry.response.isolationSuccessful', { diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/audit_log.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/audit_log.test.ts index 4bd63c83169e5..5ce7962000788 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/audit_log.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/audit_log.test.ts @@ -30,9 +30,15 @@ import { } from '../../mocks'; import { registerActionAuditLogRoutes } from './audit_log'; import uuid from 'uuid'; -import { aMockAction, aMockResponse, MockAction, mockSearchResult, MockResponse } from './mocks'; +import { mockAuditLogSearchResult, Results } from './mocks'; import { SecuritySolutionRequestHandlerContext } from '../../../types'; -import { ActivityLog } from '../../../../common/endpoint/types'; +import { + ActivityLog, + EndpointAction, + EndpointActionResponse, +} from '../../../../common/endpoint/types'; +import { FleetActionGenerator } from '../../../../common/endpoint/data_generators/fleet_action_generator'; +import { EndpointActionGenerator } from '../../../../common/endpoint/data_generators/endpoint_action_generator'; describe('Action Log API', () => { describe('schema', () => { @@ -93,17 +99,30 @@ describe('Action Log API', () => { }); describe('response', () => { - const mockID = 'XYZABC-000'; - const actionID = 'some-known-actionid'; + const mockAgentID = 'XYZABC-000'; let endpointAppContextService: EndpointAppContextService; + const fleetActionGenerator = new FleetActionGenerator('seed'); + const endpointActionGenerator = new EndpointActionGenerator('seed'); // convenience for calling the route and handler for audit log let getActivityLog: ( params: EndpointActionLogRequestParams, query?: EndpointActionLogRequestQuery ) => Promise>; - // convenience for injecting mock responses for actions index and responses - let havingActionsAndResponses: (actions: MockAction[], responses: MockResponse[]) => void; + + // convenience for injecting mock action requests and responses + // for .logs-endpoint and .fleet indices + let mockActions: ({ + numActions, + hasFleetActions, + hasFleetResponses, + hasResponses, + }: { + numActions: number; + hasFleetActions?: boolean; + hasFleetResponses?: boolean; + hasResponses?: boolean; + }) => void; let havingErrors: () => void; @@ -149,12 +168,113 @@ describe('Action Log API', () => { return mockResponse; }; - havingActionsAndResponses = (actions: MockAction[], responses: MockResponse[]) => { - esClientMock.asCurrentUser.search = jest.fn().mockImplementation((req) => { - const items: any[] = - req.index === '.fleet-actions' ? actions.splice(0, 50) : responses.splice(0, 1000); + // some arbitrary ids for needed actions + const getMockActionIds = (numAction: number): string[] => { + return [...Array(numAction).keys()].map(() => Math.random().toString(36).split('.')[1]); + }; + + // create as many actions as needed + const getEndpointActionsData = (actionIds: string[]) => { + const data = actionIds.map((actionId) => + endpointActionGenerator.generate({ + agent: { id: mockAgentID }, + EndpointActions: { + action_id: actionId, + }, + }) + ); + return data; + }; + // create as many responses as needed + const getEndpointResponseData = (actionIds: string[]) => { + const data = actionIds.map((actionId) => + endpointActionGenerator.generateResponse({ + agent: { id: mockAgentID }, + EndpointActions: { + action_id: actionId, + }, + }) + ); + return data; + }; + // create as many fleet actions as needed + const getFleetResponseData = (actionIds: string[]) => { + const data = actionIds.map((actionId) => + fleetActionGenerator.generateResponse({ + agent_id: mockAgentID, + action_id: actionId, + }) + ); + return data; + }; + // create as many fleet responses as needed + const getFleetActionData = (actionIds: string[]) => { + const data = actionIds.map((actionId) => + fleetActionGenerator.generate({ + agents: [mockAgentID], + action_id: actionId, + data: { + comment: 'some comment', + }, + }) + ); + return data; + }; + + // mock actions and responses results in a single response + mockActions = ({ + numActions, + hasFleetActions = false, + hasFleetResponses = false, + hasResponses = false, + }: { + numActions: number; + hasFleetActions?: boolean; + hasFleetResponses?: boolean; + hasResponses?: boolean; + }) => { + esClientMock.asCurrentUser.search = jest.fn().mockImplementationOnce(() => { + let actions: Results[] = []; + let fleetActions: Results[] = []; + let responses: Results[] = []; + let fleetResponses: Results[] = []; + + const actionIds = getMockActionIds(numActions); + + actions = getEndpointActionsData(actionIds).map((e) => ({ + _index: '.ds-.logs-endpoint.actions-default-2021.19.10-000001', + _source: e, + })); + + if (hasFleetActions) { + fleetActions = getFleetActionData(actionIds).map((e) => ({ + _index: '.fleet-actions-7', + _source: e, + })); + } - return Promise.resolve(mockSearchResult(items.map((x) => x.build()))); + if (hasFleetResponses) { + fleetResponses = getFleetResponseData(actionIds).map((e) => ({ + _index: '.ds-.fleet-actions-results-2021.19.10-000001', + _source: e, + })); + } + + if (hasResponses) { + responses = getEndpointResponseData(actionIds).map((e) => ({ + _index: '.ds-.logs-endpoint.action.responses-default-2021.19.10-000001', + _source: e, + })); + } + + const results = mockAuditLogSearchResult([ + ...actions, + ...fleetActions, + ...responses, + ...fleetResponses, + ]); + + return Promise.resolve(results); }); }; @@ -172,45 +292,80 @@ describe('Action Log API', () => { }); it('should return an empty array when nothing in audit log', async () => { - havingActionsAndResponses([], []); - const response = await getActivityLog({ agent_id: mockID }); + mockActions({ numActions: 0 }); + + const response = await getActivityLog({ agent_id: mockAgentID }); expect(response.ok).toBeCalled(); expect((response.ok.mock.calls[0][0]?.body as ActivityLog).data).toHaveLength(0); }); - it('should have actions and action responses', async () => { - havingActionsAndResponses( - [ - aMockAction().withAgent(mockID).withAction('isolate').withID(actionID), - aMockAction().withAgent(mockID).withAction('unisolate'), - ], - [aMockResponse(actionID, mockID).forAction(actionID).forAgent(mockID)] - ); - const response = await getActivityLog({ agent_id: mockID }); + it('should return fleet actions, fleet responses and endpoint responses', async () => { + mockActions({ + numActions: 2, + hasFleetActions: true, + hasFleetResponses: true, + hasResponses: true, + }); + + const response = await getActivityLog({ agent_id: mockAgentID }); + const responseBody = response.ok.mock.calls[0][0]?.body as ActivityLog; + expect(response.ok).toBeCalled(); + expect(responseBody.data).toHaveLength(6); + + expect( + responseBody.data.filter((e) => (e.item.data as EndpointActionResponse).completed_at) + ).toHaveLength(2); + expect( + responseBody.data.filter((e) => (e.item.data as EndpointAction).expiration) + ).toHaveLength(2); + }); + + it('should return only fleet actions and no responses', async () => { + mockActions({ numActions: 2, hasFleetActions: true }); + + const response = await getActivityLog({ agent_id: mockAgentID }); const responseBody = response.ok.mock.calls[0][0]?.body as ActivityLog; + expect(response.ok).toBeCalled(); + expect(responseBody.data).toHaveLength(2); + + expect( + responseBody.data.filter((e) => (e.item.data as EndpointAction).expiration) + ).toHaveLength(2); + }); + + it('should only have fleet data', async () => { + mockActions({ numActions: 2, hasFleetActions: true, hasFleetResponses: true }); + const response = await getActivityLog({ agent_id: mockAgentID }); + const responseBody = response.ok.mock.calls[0][0]?.body as ActivityLog; expect(response.ok).toBeCalled(); - expect(responseBody.data).toHaveLength(3); - expect(responseBody.data.filter((e) => e.type === 'response')).toHaveLength(1); - expect(responseBody.data.filter((e) => e.type === 'action')).toHaveLength(2); + expect(responseBody.data).toHaveLength(4); + + expect( + responseBody.data.filter((e) => (e.item.data as EndpointAction).expiration) + ).toHaveLength(2); + expect( + responseBody.data.filter((e) => (e.item.data as EndpointActionResponse).completed_at) + ).toHaveLength(2); }); it('should throw errors when no results for some agentID', async () => { havingErrors(); try { - await getActivityLog({ agent_id: mockID }); + await getActivityLog({ agent_id: mockAgentID }); } catch (error) { - expect(error.message).toEqual(`Error fetching actions log for agent_id ${mockID}`); + expect(error.message).toEqual(`Error fetching actions log for agent_id ${mockAgentID}`); } }); it('should return date ranges if present in the query', async () => { - havingActionsAndResponses([], []); + mockActions({ numActions: 0 }); + const startDate = new Date(new Date().setDate(new Date().getDate() - 1)).toISOString(); const endDate = new Date().toISOString(); const response = await getActivityLog( - { agent_id: mockID }, + { agent_id: mockAgentID }, { page: 1, page_size: 50, diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.ts index e12299bedbb34..02f0cb4867646 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.ts @@ -17,6 +17,7 @@ import { ENDPOINT_ACTION_RESPONSES_DS, ISOLATE_HOST_ROUTE, UNISOLATE_HOST_ROUTE, + failedFleetActionErrorCode, } from '../../../../common/endpoint/constants'; import { AGENT_ACTIONS_INDEX } from '../../../../../fleet/common'; import { @@ -33,6 +34,7 @@ import { getMetadataForEndpoints } from '../../services'; import { EndpointAppContext } from '../../types'; import { APP_ID } from '../../../../common/constants'; import { userCanIsolate } from '../../../../common/endpoint/actions'; +import { doLogsEndpointActionDsExists } from '../../utils'; /** * Registers the Host-(un-)isolation routes @@ -78,7 +80,7 @@ const createFailedActionResponseEntry = async ({ body: { ...doc, error: { - code: '424', + code: failedFleetActionErrorCode, message: 'Failed to deliver action request to fleet', }, }, @@ -88,31 +90,6 @@ const createFailedActionResponseEntry = async ({ } }; -const doLogsEndpointActionDsExists = async ({ - context, - logger, - dataStreamName, -}: { - context: SecuritySolutionRequestHandlerContext; - logger: Logger; - dataStreamName: string; -}): Promise => { - try { - const esClient = context.core.elasticsearch.client.asInternalUser; - const doesIndexTemplateExist = await esClient.indices.existsIndexTemplate({ - name: dataStreamName, - }); - return doesIndexTemplateExist.statusCode === 404 ? false : true; - } catch (error) { - const errorType = error?.type ?? ''; - if (errorType !== 'resource_not_found_exception') { - logger.error(error); - throw error; - } - return false; - } -}; - export const isolationRequestHandler = function ( endpointContext: EndpointAppContext, isolate: boolean diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/mocks.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/mocks.ts index 34f7d140a78de..b50d80a9bae71 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/mocks.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/mocks.ts @@ -13,11 +13,43 @@ import { ApiResponse } from '@elastic/elasticsearch'; import moment from 'moment'; import uuid from 'uuid'; import { + LogsEndpointAction, + LogsEndpointActionResponse, EndpointAction, EndpointActionResponse, ISOLATION_ACTIONS, } from '../../../../common/endpoint/types'; +export interface Results { + _index: string; + _source: + | LogsEndpointAction + | LogsEndpointActionResponse + | EndpointAction + | EndpointActionResponse; +} +export const mockAuditLogSearchResult = (results?: Results[]) => { + const response = { + body: { + hits: { + total: { value: results?.length ?? 0, relation: 'eq' }, + hits: + results?.map((a: Results) => ({ + _index: a._index, + _id: Math.random().toString(36).split('.')[1], + _score: 0.0, + _source: a._source, + })) ?? [], + }, + }, + statusCode: 200, + headers: {}, + warnings: [], + meta: {} as any, + }; + return response; +}; + export const mockSearchResult = (results: any = []): ApiResponse => { return { body: { diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions.ts index 711d78ba51b59..d59ecb674196c 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions.ts @@ -6,15 +6,28 @@ */ import { ElasticsearchClient, Logger } from 'kibana/server'; +import { SearchHit, SearchResponse } from '@elastic/elasticsearch/api/types'; +import { ApiResponse } from '@elastic/elasticsearch'; import { AGENT_ACTIONS_INDEX, AGENT_ACTIONS_RESULTS_INDEX } from '../../../../fleet/common'; import { SecuritySolutionRequestHandlerContext } from '../../types'; import { ActivityLog, + ActivityLogEntry, EndpointAction, + LogsEndpointAction, EndpointActionResponse, EndpointPendingActions, + LogsEndpointActionResponse, } from '../../../common/endpoint/types'; -import { catchAndWrapError } from '../utils'; +import { + catchAndWrapError, + categorizeActionResults, + categorizeResponseResults, + getActionRequestsResult, + getActionResponsesResult, + getTimeSortedData, + getUniqueLogData, +} from '../utils'; import { EndpointMetadataService } from './metadata'; const PENDING_ACTION_RESPONSE_MAX_LAPSED_TIME = 300000; // 300k ms === 5 minutes @@ -38,9 +51,9 @@ export const getAuditLogResponse = async ({ }): Promise => { const size = Math.floor(pageSize / 2); const from = page <= 1 ? 0 : page * size - size + 1; - const esClient = context.core.elasticsearch.client.asCurrentUser; + const data = await getActivityLog({ - esClient, + context, from, size, startDate, @@ -59,7 +72,7 @@ export const getAuditLogResponse = async ({ }; const getActivityLog = async ({ - esClient, + context, size, from, startDate, @@ -67,83 +80,39 @@ const getActivityLog = async ({ elasticAgentId, logger, }: { - esClient: ElasticsearchClient; + context: SecuritySolutionRequestHandlerContext; elasticAgentId: string; size: number; from: number; startDate: string; endDate: string; logger: Logger; -}) => { - const options = { - headers: { - 'X-elastic-product-origin': 'fleet', - }, - ignore: [404], - }; - - let actionsResult; - let responsesResult; - const dateFilters = [ - { range: { '@timestamp': { gte: startDate } } }, - { range: { '@timestamp': { lte: endDate } } }, - ]; +}): Promise => { + let actionsResult: ApiResponse, unknown>; + let responsesResult: ApiResponse, unknown>; try { // fetch actions with matching agent_id - const baseActionFilters = [ - { term: { agents: elasticAgentId } }, - { term: { input_type: 'endpoint' } }, - { term: { type: 'INPUT_ACTION' } }, - ]; - const actionsFilters = [...baseActionFilters, ...dateFilters]; - actionsResult = await esClient.search( - { - index: AGENT_ACTIONS_INDEX, - size, - from, - body: { - query: { - bool: { - // @ts-ignore - filter: actionsFilters, - }, - }, - sort: [ - { - '@timestamp': { - order: 'desc', - }, - }, - ], - }, - }, - options - ); - const actionIds = actionsResult?.body?.hits?.hits?.map( - (e) => (e._source as EndpointAction).action_id - ); + const { actionIds, actionRequests } = await getActionRequestsResult({ + context, + logger, + elasticAgentId, + startDate, + endDate, + size, + from, + }); + actionsResult = actionRequests; - // fetch responses with matching `action_id`s - const baseResponsesFilter = [ - { term: { agent_id: elasticAgentId } }, - { terms: { action_id: actionIds } }, - ]; - const responsesFilters = [...baseResponsesFilter, ...dateFilters]; - responsesResult = await esClient.search( - { - index: AGENT_ACTIONS_RESULTS_INDEX, - size: 1000, - body: { - query: { - bool: { - filter: responsesFilters, - }, - }, - }, - }, - options - ); + // fetch responses with matching unique set of `action_id`s + responsesResult = await getActionResponsesResult({ + actionIds: [...new Set(actionIds)], // de-dupe `action_id`s + context, + logger, + elasticAgentId, + startDate, + endDate, + }); } catch (error) { logger.error(error); throw error; @@ -153,21 +122,26 @@ const getActivityLog = async ({ throw new Error(`Error fetching actions log for agent_id ${elasticAgentId}`); } - const responses = responsesResult?.body?.hits?.hits?.length - ? responsesResult?.body?.hits?.hits?.map((e) => ({ - type: 'response', - item: { id: e._id, data: e._source }, - })) - : []; - const actions = actionsResult?.body?.hits?.hits?.length - ? actionsResult?.body?.hits?.hits?.map((e) => ({ - type: 'action', - item: { id: e._id, data: e._source }, - })) - : []; - const sortedData = ([...responses, ...actions] as ActivityLog['data']).sort((a, b) => - new Date(b.item.data['@timestamp']) > new Date(a.item.data['@timestamp']) ? 1 : -1 - ); + // label record as `action`, `fleetAction` + const responses = categorizeResponseResults({ + results: responsesResult?.body?.hits?.hits as Array< + SearchHit + >, + }); + + // label record as `response`, `fleetResponse` + const actions = categorizeActionResults({ + results: actionsResult?.body?.hits?.hits as Array< + SearchHit + >, + }); + + // filter out the duplicate endpoint actions that also have fleetActions + // include endpoint actions that have no fleet actions + const uniqueLogData = getUniqueLogData([...responses, ...actions]); + + // sort by @timestamp in desc order, newest first + const sortedData = getTimeSortedData(uniqueLogData); return sortedData; }; diff --git a/x-pack/plugins/security_solution/server/endpoint/utils/audit_log_helpers.ts b/x-pack/plugins/security_solution/server/endpoint/utils/audit_log_helpers.ts new file mode 100644 index 0000000000000..f75b265bf24d7 --- /dev/null +++ b/x-pack/plugins/security_solution/server/endpoint/utils/audit_log_helpers.ts @@ -0,0 +1,266 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Logger } from 'kibana/server'; +import { SearchRequest } from 'src/plugins/data/public'; +import { SearchHit, SearchResponse } from '@elastic/elasticsearch/api/types'; +import { ApiResponse } from '@elastic/elasticsearch'; +import { AGENT_ACTIONS_INDEX, AGENT_ACTIONS_RESULTS_INDEX } from '../../../../fleet/common'; +import { + ENDPOINT_ACTIONS_INDEX, + ENDPOINT_ACTION_RESPONSES_INDEX, + failedFleetActionErrorCode, +} from '../../../common/endpoint/constants'; +import { SecuritySolutionRequestHandlerContext } from '../../types'; +import { + ActivityLog, + ActivityLogAction, + EndpointActivityLogAction, + ActivityLogActionResponse, + EndpointActivityLogActionResponse, + ActivityLogItemTypes, + EndpointAction, + LogsEndpointAction, + EndpointActionResponse, + LogsEndpointActionResponse, + ActivityLogEntry, +} from '../../../common/endpoint/types'; +import { doesLogsEndpointActionsIndexExist } from '../utils'; + +const actionsIndices = [AGENT_ACTIONS_INDEX, ENDPOINT_ACTIONS_INDEX]; +const responseIndices = [AGENT_ACTIONS_RESULTS_INDEX, ENDPOINT_ACTION_RESPONSES_INDEX]; +export const logsEndpointActionsRegex = new RegExp(`(^\.ds-\.logs-endpoint\.actions-default-).+`); +export const logsEndpointResponsesRegex = new RegExp( + `(^\.ds-\.logs-endpoint\.action\.responses-default-).+` +); +const queryOptions = { + headers: { + 'X-elastic-product-origin': 'fleet', + }, + ignore: [404], +}; + +const getDateFilters = ({ startDate, endDate }: { startDate: string; endDate: string }) => { + return [ + { range: { '@timestamp': { gte: startDate } } }, + { range: { '@timestamp': { lte: endDate } } }, + ]; +}; + +export const getUniqueLogData = (activityLogEntries: ActivityLogEntry[]): ActivityLogEntry[] => { + // find the error responses for actions that didn't make it to fleet index + const onlyResponsesForFleetErrors = activityLogEntries + .filter( + (e) => + e.type === ActivityLogItemTypes.RESPONSE && + e.item.data.error?.code === failedFleetActionErrorCode + ) + .map( + (e: ActivityLogEntry) => (e.item.data as LogsEndpointActionResponse).EndpointActions.action_id + ); + + // all actions and responses minus endpoint actions. + const nonEndpointActionsDocs = activityLogEntries.filter( + (e) => e.type !== ActivityLogItemTypes.ACTION + ); + + // only endpoint actions that match the error responses + const onlyEndpointActionsDocWithoutFleetActions = activityLogEntries + .filter((e) => e.type === ActivityLogItemTypes.ACTION) + .filter((e: ActivityLogEntry) => + onlyResponsesForFleetErrors.includes( + (e.item.data as LogsEndpointAction).EndpointActions.action_id + ) + ); + + // join the error actions and the rest + return [...nonEndpointActionsDocs, ...onlyEndpointActionsDocWithoutFleetActions]; +}; + +export const categorizeResponseResults = ({ + results, +}: { + results: Array>; +}): Array => { + return results?.length + ? results?.map((e) => { + const isResponseDoc: boolean = matchesIndexPattern({ + regexPattern: logsEndpointResponsesRegex, + index: e._index, + }); + return isResponseDoc + ? { + type: ActivityLogItemTypes.RESPONSE, + item: { id: e._id, data: e._source as LogsEndpointActionResponse }, + } + : { + type: ActivityLogItemTypes.FLEET_RESPONSE, + item: { id: e._id, data: e._source as EndpointActionResponse }, + }; + }) + : []; +}; + +export const categorizeActionResults = ({ + results, +}: { + results: Array>; +}): Array => { + return results?.length + ? results?.map((e) => { + const isActionDoc: boolean = matchesIndexPattern({ + regexPattern: logsEndpointActionsRegex, + index: e._index, + }); + return isActionDoc + ? { + type: ActivityLogItemTypes.ACTION, + item: { id: e._id, data: e._source as LogsEndpointAction }, + } + : { + type: ActivityLogItemTypes.FLEET_ACTION, + item: { id: e._id, data: e._source as EndpointAction }, + }; + }) + : []; +}; + +export const getTimeSortedData = (data: ActivityLog['data']): ActivityLog['data'] => { + return data.sort((a, b) => + new Date(b.item.data['@timestamp']) > new Date(a.item.data['@timestamp']) ? 1 : -1 + ); +}; + +export const getActionRequestsResult = async ({ + context, + logger, + elasticAgentId, + startDate, + endDate, + size, + from, +}: { + context: SecuritySolutionRequestHandlerContext; + logger: Logger; + elasticAgentId: string; + startDate: string; + endDate: string; + size: number; + from: number; +}): Promise<{ + actionIds: string[]; + actionRequests: ApiResponse, unknown>; +}> => { + const dateFilters = getDateFilters({ startDate, endDate }); + const baseActionFilters = [ + { term: { agents: elasticAgentId } }, + { term: { input_type: 'endpoint' } }, + { term: { type: 'INPUT_ACTION' } }, + ]; + const actionsFilters = [...baseActionFilters, ...dateFilters]; + + const hasLogsEndpointActionsIndex = await doesLogsEndpointActionsIndexExist({ + context, + logger, + indexName: ENDPOINT_ACTIONS_INDEX, + }); + + const actionsSearchQuery: SearchRequest = { + index: hasLogsEndpointActionsIndex ? actionsIndices : AGENT_ACTIONS_INDEX, + size, + from, + body: { + query: { + bool: { + filter: actionsFilters, + }, + }, + sort: [ + { + '@timestamp': { + order: 'desc', + }, + }, + ], + }, + }; + + let actionRequests: ApiResponse, unknown>; + try { + const esClient = context.core.elasticsearch.client.asCurrentUser; + actionRequests = await esClient.search(actionsSearchQuery, queryOptions); + const actionIds = actionRequests?.body?.hits?.hits?.map((e) => { + return logsEndpointActionsRegex.test(e._index) + ? (e._source as LogsEndpointAction).EndpointActions.action_id + : (e._source as EndpointAction).action_id; + }); + + return { actionIds, actionRequests }; + } catch (error) { + logger.error(error); + throw error; + } +}; + +export const getActionResponsesResult = async ({ + context, + logger, + elasticAgentId, + actionIds, + startDate, + endDate, +}: { + context: SecuritySolutionRequestHandlerContext; + logger: Logger; + elasticAgentId: string; + actionIds: string[]; + startDate: string; + endDate: string; +}): Promise, unknown>> => { + const dateFilters = getDateFilters({ startDate, endDate }); + const baseResponsesFilter = [ + { term: { agent_id: elasticAgentId } }, + { terms: { action_id: actionIds } }, + ]; + const responsesFilters = [...baseResponsesFilter, ...dateFilters]; + + const hasLogsEndpointActionResponsesIndex = await doesLogsEndpointActionsIndexExist({ + context, + logger, + indexName: ENDPOINT_ACTION_RESPONSES_INDEX, + }); + + const responsesSearchQuery: SearchRequest = { + index: hasLogsEndpointActionResponsesIndex ? responseIndices : AGENT_ACTIONS_RESULTS_INDEX, + size: 1000, + body: { + query: { + bool: { + filter: responsesFilters, + }, + }, + }, + }; + + let actionResponses: ApiResponse, unknown>; + try { + const esClient = context.core.elasticsearch.client.asCurrentUser; + actionResponses = await esClient.search(responsesSearchQuery, queryOptions); + } catch (error) { + logger.error(error); + throw error; + } + return actionResponses; +}; + +const matchesIndexPattern = ({ + regexPattern, + index, +}: { + regexPattern: RegExp; + index: string; +}): boolean => regexPattern.test(index); diff --git a/x-pack/plugins/security_solution/server/endpoint/utils/index.ts b/x-pack/plugins/security_solution/server/endpoint/utils/index.ts index 34cabf79aff0e..6c40073f8c654 100644 --- a/x-pack/plugins/security_solution/server/endpoint/utils/index.ts +++ b/x-pack/plugins/security_solution/server/endpoint/utils/index.ts @@ -7,3 +7,5 @@ export * from './fleet_agent_status_to_endpoint_host_status'; export * from './wrap_errors'; +export * from './audit_log_helpers'; +export * from './yes_no_data_stream'; diff --git a/x-pack/plugins/security_solution/server/endpoint/utils/yes_no_data_stream.test.ts b/x-pack/plugins/security_solution/server/endpoint/utils/yes_no_data_stream.test.ts new file mode 100644 index 0000000000000..d2894c8c64c14 --- /dev/null +++ b/x-pack/plugins/security_solution/server/endpoint/utils/yes_no_data_stream.test.ts @@ -0,0 +1,100 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + elasticsearchServiceMock, + savedObjectsClientMock, + loggingSystemMock, +} from 'src/core/server/mocks'; +import { SecuritySolutionRequestHandlerContext } from '../../types'; +import { createRouteHandlerContext } from '../mocks'; +import { + doLogsEndpointActionDsExists, + doesLogsEndpointActionsIndexExist, +} from './yes_no_data_stream'; + +describe('Accurately answers if index template for data stream exists', () => { + let ctxt: jest.Mocked; + + beforeEach(() => { + ctxt = createRouteHandlerContext( + elasticsearchServiceMock.createScopedClusterClient(), + savedObjectsClientMock.create() + ); + }); + + const mockEsApiResponse = (response: { body: boolean; statusCode: number }) => { + return jest.fn().mockImplementationOnce(() => Promise.resolve(response)); + }; + + it('Returns FALSE for a non-existent data stream index template', async () => { + ctxt.core.elasticsearch.client.asInternalUser.indices.existsIndexTemplate = mockEsApiResponse({ + body: false, + statusCode: 404, + }); + const doesItExist = await doLogsEndpointActionDsExists({ + context: ctxt, + logger: loggingSystemMock.create().get('host-isolation'), + dataStreamName: '.test-stream.name', + }); + expect(doesItExist).toBeFalsy(); + }); + + it('Returns TRUE for an existing index', async () => { + ctxt.core.elasticsearch.client.asInternalUser.indices.existsIndexTemplate = mockEsApiResponse({ + body: true, + statusCode: 200, + }); + const doesItExist = await doLogsEndpointActionDsExists({ + context: ctxt, + logger: loggingSystemMock.create().get('host-isolation'), + dataStreamName: '.test-stream.name', + }); + expect(doesItExist).toBeTruthy(); + }); +}); + +describe('Accurately answers if index exists', () => { + let ctxt: jest.Mocked; + + beforeEach(() => { + ctxt = createRouteHandlerContext( + elasticsearchServiceMock.createScopedClusterClient(), + savedObjectsClientMock.create() + ); + }); + + const mockEsApiResponse = (response: { body: boolean; statusCode: number }) => { + return jest.fn().mockImplementationOnce(() => Promise.resolve(response)); + }; + + it('Returns FALSE for a non-existent index', async () => { + ctxt.core.elasticsearch.client.asInternalUser.indices.exists = mockEsApiResponse({ + body: false, + statusCode: 404, + }); + const doesItExist = await doesLogsEndpointActionsIndexExist({ + context: ctxt, + logger: loggingSystemMock.create().get('host-isolation'), + indexName: '.test-index.name-default', + }); + expect(doesItExist).toBeFalsy(); + }); + + it('Returns TRUE for an existing index', async () => { + ctxt.core.elasticsearch.client.asInternalUser.indices.exists = mockEsApiResponse({ + body: true, + statusCode: 200, + }); + const doesItExist = await doesLogsEndpointActionsIndexExist({ + context: ctxt, + logger: loggingSystemMock.create().get('host-isolation'), + indexName: '.test-index.name-default', + }); + expect(doesItExist).toBeTruthy(); + }); +}); diff --git a/x-pack/plugins/security_solution/server/endpoint/utils/yes_no_data_stream.ts b/x-pack/plugins/security_solution/server/endpoint/utils/yes_no_data_stream.ts new file mode 100644 index 0000000000000..dea2e46c3c258 --- /dev/null +++ b/x-pack/plugins/security_solution/server/endpoint/utils/yes_no_data_stream.ts @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Logger } from 'src/core/server'; +import { SecuritySolutionRequestHandlerContext } from '../../types'; + +export const doLogsEndpointActionDsExists = async ({ + context, + logger, + dataStreamName, +}: { + context: SecuritySolutionRequestHandlerContext; + logger: Logger; + dataStreamName: string; +}): Promise => { + try { + const esClient = context.core.elasticsearch.client.asInternalUser; + const doesIndexTemplateExist = await esClient.indices.existsIndexTemplate({ + name: dataStreamName, + }); + return doesIndexTemplateExist.statusCode === 404 ? false : true; + } catch (error) { + const errorType = error?.type ?? ''; + if (errorType !== 'resource_not_found_exception') { + logger.error(error); + throw error; + } + return false; + } +}; + +export const doesLogsEndpointActionsIndexExist = async ({ + context, + logger, + indexName, +}: { + context: SecuritySolutionRequestHandlerContext; + logger: Logger; + indexName: string; +}): Promise => { + try { + const esClient = context.core.elasticsearch.client.asInternalUser; + const doesIndexExist = await esClient.indices.exists({ + index: indexName, + }); + return doesIndexExist.statusCode === 404 ? false : true; + } catch (error) { + const errorType = error?.type ?? ''; + if (errorType !== 'index_not_found_exception') { + logger.error(error); + throw error; + } + return false; + } +}; From ec3809658fdc0850106c6e2672ae46c3f6621d96 Mon Sep 17 00:00:00 2001 From: Caroline Horn <549577+cchaos@users.noreply.github.com> Date: Mon, 18 Oct 2021 23:56:00 -0400 Subject: [PATCH 027/204] [Unified Integrations] Clean up empty states, tutorial links and routing to prefer unified integrations (#114911) Cleans up the integrations view and redirects all links to the integration manager. --- .../chrome/ui/header/collapsible_nav.tsx | 2 +- .../__snapshots__/add_data.test.tsx.snap | 16 +- .../components/add_data/add_data.test.tsx | 4 +- .../components/add_data/add_data.tsx | 152 +++++++++--------- .../components/sample_data/index.tsx | 4 +- .../components/tutorial_directory.js | 56 +------ .../public/application/components/welcome.tsx | 3 +- src/plugins/home/public/index.ts | 1 - src/plugins/home/public/services/index.ts | 1 - .../home/public/services/tutorials/index.ts | 1 - .../tutorials/tutorial_service.mock.ts | 1 - .../tutorials/tutorial_service.test.tsx | 32 ---- .../services/tutorials/tutorial_service.ts | 18 --- .../empty_index_list_prompt.tsx | 2 +- .../__snapshots__/overview.test.tsx.snap | 110 +++---------- .../public/components/overview/overview.tsx | 12 +- .../public/assets/elastic_beats_card_dark.svg | 1 - .../assets/elastic_beats_card_light.svg | 1 - .../__snapshots__/no_data_page.test.tsx.snap | 4 +- .../elastic_agent_card.test.tsx.snap | 55 ++++++- .../elastic_beats_card.test.tsx.snap | 70 -------- .../no_data_card/elastic_agent_card.test.tsx | 10 +- .../no_data_card/elastic_agent_card.tsx | 44 ++++- .../no_data_card/elastic_beats_card.test.tsx | 45 ------ .../no_data_card/elastic_beats_card.tsx | 66 -------- .../no_data_page/no_data_card/index.ts | 1 - .../no_data_page/no_data_page.tsx | 14 +- .../components/app/RumDashboard/RumHome.tsx | 8 +- .../routing/templates/no_data_config.ts | 10 +- .../epm/components/package_list_grid.tsx | 2 +- .../components/home_integration/index.tsx | 8 - .../tutorial_directory_header_link.tsx | 16 +- .../tutorial_directory_notice.tsx | 147 ----------------- x-pack/plugins/fleet/public/plugin.ts | 7 +- .../infra/public/pages/logs/page_content.tsx | 2 +- .../infra/public/pages/logs/page_template.tsx | 6 +- .../logs/stream/page_no_indices_content.tsx | 4 +- .../infra/public/pages/metrics/index.tsx | 4 +- .../metric_detail/components/invalid_node.tsx | 4 +- .../public/pages/metrics/page_template.tsx | 9 +- .../components/app/header/header_menu.tsx | 2 +- .../public/utils/no_data_config.ts | 7 +- .../security_solution/common/constants.ts | 2 +- .../components/overview_empty/index.test.tsx | 12 +- .../components/overview_empty/index.tsx | 50 ++---- .../translations/translations/ja-JP.json | 11 -- .../translations/translations/zh-CN.json | 11 -- x-pack/test/accessibility/apps/home.ts | 27 ---- 48 files changed, 292 insertions(+), 783 deletions(-) delete mode 100644 src/plugins/kibana_react/public/assets/elastic_beats_card_dark.svg delete mode 100644 src/plugins/kibana_react/public/assets/elastic_beats_card_light.svg delete mode 100644 src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/__snapshots__/elastic_beats_card.test.tsx.snap delete mode 100644 src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_beats_card.test.tsx delete mode 100644 src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_beats_card.tsx delete mode 100644 x-pack/plugins/fleet/public/components/home_integration/tutorial_directory_notice.tsx diff --git a/src/core/public/chrome/ui/header/collapsible_nav.tsx b/src/core/public/chrome/ui/header/collapsible_nav.tsx index ad590865b9e14..ccc0e17b655b1 100644 --- a/src/core/public/chrome/ui/header/collapsible_nav.tsx +++ b/src/core/public/chrome/ui/header/collapsible_nav.tsx @@ -362,7 +362,7 @@ export function CollapsibleNav({ iconType="plusInCircleFilled" > {i18n.translate('core.ui.primaryNav.addData', { - defaultMessage: 'Add data', + defaultMessage: 'Add integrations', })} diff --git a/src/plugins/home/public/application/components/add_data/__snapshots__/add_data.test.tsx.snap b/src/plugins/home/public/application/components/add_data/__snapshots__/add_data.test.tsx.snap index 26b5697f008b6..de6beab31247a 100644 --- a/src/plugins/home/public/application/components/add_data/__snapshots__/add_data.test.tsx.snap +++ b/src/plugins/home/public/application/components/add_data/__snapshots__/add_data.test.tsx.snap @@ -17,7 +17,7 @@ exports[`AddData render 1`] = ` id="homDataAdd__title" > @@ -43,17 +43,25 @@ exports[`AddData render 1`] = ` grow={false} > diff --git a/src/plugins/home/public/application/components/add_data/add_data.test.tsx b/src/plugins/home/public/application/components/add_data/add_data.test.tsx index 4018ae67c19ee..3aa51f89c7d67 100644 --- a/src/plugins/home/public/application/components/add_data/add_data.test.tsx +++ b/src/plugins/home/public/application/components/add_data/add_data.test.tsx @@ -27,7 +27,9 @@ beforeEach(() => { jest.clearAllMocks(); }); -const applicationStartMock = {} as unknown as ApplicationStart; +const applicationStartMock = { + capabilities: { navLinks: { integrations: true } }, +} as unknown as ApplicationStart; const addBasePathMock = jest.fn((path: string) => (path ? path : 'path')); diff --git a/src/plugins/home/public/application/components/add_data/add_data.tsx b/src/plugins/home/public/application/components/add_data/add_data.tsx index 97ba28a04a07e..50d6079dd8df3 100644 --- a/src/plugins/home/public/application/components/add_data/add_data.tsx +++ b/src/plugins/home/public/application/components/add_data/add_data.tsx @@ -22,8 +22,6 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { METRIC_TYPE } from '@kbn/analytics'; import { ApplicationStart } from 'kibana/public'; import { createAppNavigationHandler } from '../app_navigation_handler'; -// @ts-expect-error untyped component -import { Synopsis } from '../synopsis'; import { getServices } from '../../kibana_services'; import { RedirectAppLinks } from '../../../../../kibana_react/public'; @@ -35,87 +33,91 @@ interface Props { export const AddData: FC = ({ addBasePath, application, isDarkMode }) => { const { trackUiMetric } = getServices(); + const canAccessIntegrations = application.capabilities.navLinks.integrations; + if (canAccessIntegrations) { + return ( + <> +
+ + + +

+ +

+
- return ( - <> -
- - - -

- -

-
+ - + +

+ +

+
- -

- -

-
+ - + + + + {/* eslint-disable-next-line @elastic/eui/href-or-on-click */} + { + trackUiMetric(METRIC_TYPE.CLICK, 'home_tutorial_directory'); + createAppNavigationHandler('/app/integrations/browse')(event); + }} + > + + + + - - - - {/* eslint-disable-next-line @elastic/eui/href-or-on-click */} - { - trackUiMetric(METRIC_TYPE.CLICK, 'home_tutorial_directory'); - createAppNavigationHandler('/app/home#/tutorial_directory')(event); - }} + + - - - - - - - - - - -
+ + +
+ - - - - -
+ + + +
+
- - - ); + + + ); + } else { + return null; + } }; diff --git a/src/plugins/home/public/application/components/sample_data/index.tsx b/src/plugins/home/public/application/components/sample_data/index.tsx index d6b9328f57e9b..b65fbb5d002b0 100644 --- a/src/plugins/home/public/application/components/sample_data/index.tsx +++ b/src/plugins/home/public/application/components/sample_data/index.tsx @@ -40,7 +40,7 @@ export function SampleDataCard({ urlBasePath, onDecline, onConfirm }: Props) { image={cardGraphicURL} textAlign="left" title={ - + } description={ - + { - const notices = getServices().tutorialService.getDirectoryNotices(); - return notices.length ? ( - - {notices.map((DirectoryNotice, index) => ( - - - - ))} - - ) : null; - }; - renderHeaderLinks = () => { const headerLinks = getServices().tutorialService.getDirectoryHeaderLinks(); return headerLinks.length ? ( @@ -245,7 +203,6 @@ class TutorialDirectoryUi extends React.Component { render() { const headerLinks = this.renderHeaderLinks(); const tabs = this.getTabs(); - const notices = this.renderNotices(); return ( + ), tabs, rightSideItems: headerLinks ? [headerLinks] : [], }} > - {notices && ( - <> - {notices} - - - )} {this.renderTabContent()} ); diff --git a/src/plugins/home/public/application/components/welcome.tsx b/src/plugins/home/public/application/components/welcome.tsx index ca7e6874c75c2..03dff22c7b33f 100644 --- a/src/plugins/home/public/application/components/welcome.tsx +++ b/src/plugins/home/public/application/components/welcome.tsx @@ -48,8 +48,7 @@ export class Welcome extends React.Component { }; private redirecToAddData() { - const path = this.services.addBasePath('#/tutorial_directory'); - window.location.href = path; + this.services.application.navigateToApp('integrations', { path: '/browse' }); } private onSampleDataDecline = () => { diff --git a/src/plugins/home/public/index.ts b/src/plugins/home/public/index.ts index dd02bf65dd8b0..7abaf5d19f008 100644 --- a/src/plugins/home/public/index.ts +++ b/src/plugins/home/public/index.ts @@ -23,7 +23,6 @@ export type { FeatureCatalogueSolution, Environment, TutorialVariables, - TutorialDirectoryNoticeComponent, TutorialDirectoryHeaderLinkComponent, TutorialModuleNoticeComponent, } from './services'; diff --git a/src/plugins/home/public/services/index.ts b/src/plugins/home/public/services/index.ts index 65913df6310b1..2ee68a9eef0c2 100644 --- a/src/plugins/home/public/services/index.ts +++ b/src/plugins/home/public/services/index.ts @@ -22,7 +22,6 @@ export { TutorialService } from './tutorials'; export type { TutorialVariables, TutorialServiceSetup, - TutorialDirectoryNoticeComponent, TutorialDirectoryHeaderLinkComponent, TutorialModuleNoticeComponent, } from './tutorials'; diff --git a/src/plugins/home/public/services/tutorials/index.ts b/src/plugins/home/public/services/tutorials/index.ts index 8de12c31249d8..e007a5ea4d552 100644 --- a/src/plugins/home/public/services/tutorials/index.ts +++ b/src/plugins/home/public/services/tutorials/index.ts @@ -11,7 +11,6 @@ export { TutorialService } from './tutorial_service'; export type { TutorialVariables, TutorialServiceSetup, - TutorialDirectoryNoticeComponent, TutorialDirectoryHeaderLinkComponent, TutorialModuleNoticeComponent, } from './tutorial_service'; diff --git a/src/plugins/home/public/services/tutorials/tutorial_service.mock.ts b/src/plugins/home/public/services/tutorials/tutorial_service.mock.ts index 0c109d61912ca..ab38a32a1a5b3 100644 --- a/src/plugins/home/public/services/tutorials/tutorial_service.mock.ts +++ b/src/plugins/home/public/services/tutorials/tutorial_service.mock.ts @@ -25,7 +25,6 @@ const createMock = (): jest.Mocked> => { const service = { setup: jest.fn(), getVariables: jest.fn(() => ({})), - getDirectoryNotices: jest.fn(() => []), getDirectoryHeaderLinks: jest.fn(() => []), getModuleNotices: jest.fn(() => []), getCustomStatusCheck: jest.fn(), diff --git a/src/plugins/home/public/services/tutorials/tutorial_service.test.tsx b/src/plugins/home/public/services/tutorials/tutorial_service.test.tsx index a88cf526e3716..b90165aafb45f 100644 --- a/src/plugins/home/public/services/tutorials/tutorial_service.test.tsx +++ b/src/plugins/home/public/services/tutorials/tutorial_service.test.tsx @@ -27,22 +27,6 @@ describe('TutorialService', () => { }).toThrow(); }); - test('allows multiple register directory notice calls', () => { - const setup = new TutorialService().setup(); - expect(() => { - setup.registerDirectoryNotice('abc', () =>
); - setup.registerDirectoryNotice('def', () => ); - }).not.toThrow(); - }); - - test('throws when same directory notice is registered twice', () => { - const setup = new TutorialService().setup(); - expect(() => { - setup.registerDirectoryNotice('abc', () =>
); - setup.registerDirectoryNotice('abc', () => ); - }).toThrow(); - }); - test('allows multiple register directory header link calls', () => { const setup = new TutorialService().setup(); expect(() => { @@ -91,22 +75,6 @@ describe('TutorialService', () => { }); }); - describe('getDirectoryNotices', () => { - test('returns empty array', () => { - const service = new TutorialService(); - expect(service.getDirectoryNotices()).toEqual([]); - }); - - test('returns last state of register calls', () => { - const service = new TutorialService(); - const setup = service.setup(); - const notices = [() =>
, () => ]; - setup.registerDirectoryNotice('abc', notices[0]); - setup.registerDirectoryNotice('def', notices[1]); - expect(service.getDirectoryNotices()).toEqual(notices); - }); - }); - describe('getDirectoryHeaderLinks', () => { test('returns empty array', () => { const service = new TutorialService(); diff --git a/src/plugins/home/public/services/tutorials/tutorial_service.ts b/src/plugins/home/public/services/tutorials/tutorial_service.ts index 839b0702a499e..81b6bbe72e3e9 100644 --- a/src/plugins/home/public/services/tutorials/tutorial_service.ts +++ b/src/plugins/home/public/services/tutorials/tutorial_service.ts @@ -11,9 +11,6 @@ import React from 'react'; /** @public */ export type TutorialVariables = Partial>; -/** @public */ -export type TutorialDirectoryNoticeComponent = React.FC; - /** @public */ export type TutorialDirectoryHeaderLinkComponent = React.FC; @@ -27,7 +24,6 @@ type CustomComponent = () => Promise; export class TutorialService { private tutorialVariables: TutorialVariables = {}; - private tutorialDirectoryNotices: { [key: string]: TutorialDirectoryNoticeComponent } = {}; private tutorialDirectoryHeaderLinks: { [key: string]: TutorialDirectoryHeaderLinkComponent; } = {}; @@ -47,16 +43,6 @@ export class TutorialService { this.tutorialVariables[key] = value; }, - /** - * Registers a component that will be rendered at the top of tutorial directory page. - */ - registerDirectoryNotice: (id: string, component: TutorialDirectoryNoticeComponent) => { - if (this.tutorialDirectoryNotices[id]) { - throw new Error(`directory notice ${id} already set`); - } - this.tutorialDirectoryNotices[id] = component; - }, - /** * Registers a component that will be rendered next to tutorial directory title/header area. */ @@ -94,10 +80,6 @@ export class TutorialService { return this.tutorialVariables; } - public getDirectoryNotices() { - return Object.values(this.tutorialDirectoryNotices); - } - public getDirectoryHeaderLinks() { return Object.values(this.tutorialDirectoryHeaderLinks); } diff --git a/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_list_prompt/empty_index_list_prompt.tsx b/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_list_prompt/empty_index_list_prompt.tsx index 1331eb9b7c4ac..d00f9e2368e21 100644 --- a/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_list_prompt/empty_index_list_prompt.tsx +++ b/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_list_prompt/empty_index_list_prompt.tsx @@ -91,7 +91,7 @@ export const EmptyIndexListPrompt = ({ { - navigateToApp('home', { path: '#/tutorial_directory' }); + navigateToApp('home', { path: '/app/integrations/browse' }); closeFlyout(); }} icon={} diff --git a/src/plugins/kibana_overview/public/components/overview/__snapshots__/overview.test.tsx.snap b/src/plugins/kibana_overview/public/components/overview/__snapshots__/overview.test.tsx.snap index 6da2f95fa394d..babcab15a4974 100644 --- a/src/plugins/kibana_overview/public/components/overview/__snapshots__/overview.test.tsx.snap +++ b/src/plugins/kibana_overview/public/components/overview/__snapshots__/overview.test.tsx.snap @@ -226,10 +226,7 @@ exports[`Overview render 1`] = ` [MockFunction] { "calls": Array [ Array [ - "/app/home#/tutorial_directory", - ], - Array [ - "home#/tutorial_directory", + "/app/integrations/browse", ], Array [ "kibana_landing_page", @@ -259,11 +256,7 @@ exports[`Overview render 1`] = ` "results": Array [ Object { "type": "return", - "value": "/app/home#/tutorial_directory", - }, - Object { - "type": "return", - "value": "home#/tutorial_directory", + "value": "/app/integrations/browse", }, Object { "type": "return", @@ -533,10 +526,7 @@ exports[`Overview without features 1`] = ` [MockFunction] { "calls": Array [ Array [ - "/app/home#/tutorial_directory", - ], - Array [ - "home#/tutorial_directory", + "/app/integrations/browse", ], Array [ "kibana_landing_page", @@ -563,16 +553,10 @@ exports[`Overview without features 1`] = ` "/plugins/kibanaReact/assets/solutions_solution_4.svg", ], Array [ - "/app/home#/tutorial_directory", + "/app/integrations/browse", ], Array [ - "home#/tutorial_directory", - ], - Array [ - "/app/home#/tutorial_directory", - ], - Array [ - "home#/tutorial_directory", + "/app/integrations/browse", ], Array [ "kibana_landing_page", @@ -602,11 +586,7 @@ exports[`Overview without features 1`] = ` "results": Array [ Object { "type": "return", - "value": "/app/home#/tutorial_directory", - }, - Object { - "type": "return", - "value": "home#/tutorial_directory", + "value": "/app/integrations/browse", }, Object { "type": "return", @@ -642,19 +622,11 @@ exports[`Overview without features 1`] = ` }, Object { "type": "return", - "value": "/app/home#/tutorial_directory", - }, - Object { - "type": "return", - "value": "home#/tutorial_directory", - }, - Object { - "type": "return", - "value": "/app/home#/tutorial_directory", + "value": "/app/integrations/browse", }, Object { "type": "return", - "value": "home#/tutorial_directory", + "value": "/app/integrations/browse", }, Object { "type": "return", @@ -801,10 +773,7 @@ exports[`Overview without solutions 1`] = ` [MockFunction] { "calls": Array [ Array [ - "/app/home#/tutorial_directory", - ], - Array [ - "home#/tutorial_directory", + "/app/integrations/browse", ], Array [ "kibana_landing_page", @@ -831,20 +800,13 @@ exports[`Overview without solutions 1`] = ` "/plugins/kibanaReact/assets/solutions_solution_4.svg", ], Array [ - "/app/home#/tutorial_directory", - ], - Array [ - "home#/tutorial_directory", + "/app/integrations/browse", ], ], "results": Array [ Object { "type": "return", - "value": "/app/home#/tutorial_directory", - }, - Object { - "type": "return", - "value": "home#/tutorial_directory", + "value": "/app/integrations/browse", }, Object { "type": "return", @@ -880,11 +842,7 @@ exports[`Overview without solutions 1`] = ` }, Object { "type": "return", - "value": "/app/home#/tutorial_directory", - }, - Object { - "type": "return", - "value": "home#/tutorial_directory", + "value": "/app/integrations/browse", }, ], } @@ -898,10 +856,7 @@ exports[`Overview without solutions 1`] = ` [MockFunction] { "calls": Array [ Array [ - "/app/home#/tutorial_directory", - ], - Array [ - "home#/tutorial_directory", + "/app/integrations/browse", ], Array [ "kibana_landing_page", @@ -928,20 +883,13 @@ exports[`Overview without solutions 1`] = ` "/plugins/kibanaReact/assets/solutions_solution_4.svg", ], Array [ - "/app/home#/tutorial_directory", - ], - Array [ - "home#/tutorial_directory", + "/app/integrations/browse", ], ], "results": Array [ Object { "type": "return", - "value": "/app/home#/tutorial_directory", - }, - Object { - "type": "return", - "value": "home#/tutorial_directory", + "value": "/app/integrations/browse", }, Object { "type": "return", @@ -977,11 +925,7 @@ exports[`Overview without solutions 1`] = ` }, Object { "type": "return", - "value": "/app/home#/tutorial_directory", - }, - Object { - "type": "return", - "value": "home#/tutorial_directory", + "value": "/app/integrations/browse", }, ], } @@ -1001,10 +945,7 @@ exports[`Overview without solutions 1`] = ` [MockFunction] { "calls": Array [ Array [ - "/app/home#/tutorial_directory", - ], - Array [ - "home#/tutorial_directory", + "/app/integrations/browse", ], Array [ "kibana_landing_page", @@ -1031,20 +972,13 @@ exports[`Overview without solutions 1`] = ` "/plugins/kibanaReact/assets/solutions_solution_4.svg", ], Array [ - "/app/home#/tutorial_directory", - ], - Array [ - "home#/tutorial_directory", + "/app/integrations/browse", ], ], "results": Array [ Object { "type": "return", - "value": "/app/home#/tutorial_directory", - }, - Object { - "type": "return", - "value": "home#/tutorial_directory", + "value": "/app/integrations/browse", }, Object { "type": "return", @@ -1080,11 +1014,7 @@ exports[`Overview without solutions 1`] = ` }, Object { "type": "return", - "value": "/app/home#/tutorial_directory", - }, - Object { - "type": "return", - "value": "home#/tutorial_directory", + "value": "/app/integrations/browse", }, ], } diff --git a/src/plugins/kibana_overview/public/components/overview/overview.tsx b/src/plugins/kibana_overview/public/components/overview/overview.tsx index 07769e2f3c474..6a0279bd12465 100644 --- a/src/plugins/kibana_overview/public/components/overview/overview.tsx +++ b/src/plugins/kibana_overview/public/components/overview/overview.tsx @@ -61,7 +61,7 @@ export const Overview: FC = ({ newsFetchResult, solutions, features }) => const IS_DARK_THEME = uiSettings.get('theme:darkMode'); // Home does not have a locator implemented, so hard-code it here. - const addDataHref = addBasePath('/app/home#/tutorial_directory'); + const addDataHref = addBasePath('/app/integrations/browse'); const devToolsHref = share.url.locators.get('CONSOLE_APP_LOCATOR')?.useUrl({}); const managementHref = share.url.locators .get('MANAGEMENT_APP_LOCATOR') @@ -86,8 +86,14 @@ export const Overview: FC = ({ newsFetchResult, solutions, features }) => }), logo: 'logoKibana', actions: { - beats: { - href: addBasePath(`home#/tutorial_directory`), + elasticAgent: { + title: i18n.translate('kibanaOverview.noDataConfig.title', { + defaultMessage: 'Add integrations', + }), + description: i18n.translate('kibanaOverview.noDataConfig.description', { + defaultMessage: + 'Use Elastic Agent or Beats to collect data and build out Analytics solutions.', + }), }, }, docsLink: docLinks.links.kibana, diff --git a/src/plugins/kibana_react/public/assets/elastic_beats_card_dark.svg b/src/plugins/kibana_react/public/assets/elastic_beats_card_dark.svg deleted file mode 100644 index 8652d8d921506..0000000000000 --- a/src/plugins/kibana_react/public/assets/elastic_beats_card_dark.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/plugins/kibana_react/public/assets/elastic_beats_card_light.svg b/src/plugins/kibana_react/public/assets/elastic_beats_card_light.svg deleted file mode 100644 index f54786c1b950c..0000000000000 --- a/src/plugins/kibana_react/public/assets/elastic_beats_card_light.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/plugins/kibana_react/public/page_template/no_data_page/__snapshots__/no_data_page.test.tsx.snap b/src/plugins/kibana_react/public/page_template/no_data_page/__snapshots__/no_data_page.test.tsx.snap index d8bc5745ec8e5..8842a3c9f5842 100644 --- a/src/plugins/kibana_react/public/page_template/no_data_page/__snapshots__/no_data_page.test.tsx.snap +++ b/src/plugins/kibana_react/public/page_template/no_data_page/__snapshots__/no_data_page.test.tsx.snap @@ -73,9 +73,9 @@ exports[`NoDataPage render 1`] = ` - diff --git a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/__snapshots__/elastic_agent_card.test.tsx.snap b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/__snapshots__/elastic_agent_card.test.tsx.snap index 3f72ae5597a98..f66d05140b2e9 100644 --- a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/__snapshots__/elastic_agent_card.test.tsx.snap +++ b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/__snapshots__/elastic_agent_card.test.tsx.snap @@ -13,7 +13,36 @@ exports[`ElasticAgentCard props button 1`] = ` href="/app/integrations/browse" image="/plugins/kibanaReact/assets/elastic_agent_card.svg" paddingSize="l" - title="Add Elastic Agent" + title={ + + + Add Elastic Agent + + + } +/> +`; + +exports[`ElasticAgentCard props category 1`] = ` + + Add Elastic Agent + + } + href="/app/integrations/browse/custom" + image="/plugins/kibanaReact/assets/elastic_agent_card.svg" + paddingSize="l" + title={ + + + Add Elastic Agent + + + } /> `; @@ -30,7 +59,13 @@ exports[`ElasticAgentCard props href 1`] = ` href="#" image="/plugins/kibanaReact/assets/elastic_agent_card.svg" paddingSize="l" - title="Add Elastic Agent" + title={ + + + Add Elastic Agent + + + } /> `; @@ -48,7 +83,13 @@ exports[`ElasticAgentCard props recommended 1`] = ` href="/app/integrations/browse" image="/plugins/kibanaReact/assets/elastic_agent_card.svg" paddingSize="l" - title="Add Elastic Agent" + title={ + + + Add Elastic Agent + + + } /> `; @@ -65,6 +106,12 @@ exports[`ElasticAgentCard renders 1`] = ` href="/app/integrations/browse" image="/plugins/kibanaReact/assets/elastic_agent_card.svg" paddingSize="l" - title="Add Elastic Agent" + title={ + + + Add Elastic Agent + + + } /> `; diff --git a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/__snapshots__/elastic_beats_card.test.tsx.snap b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/__snapshots__/elastic_beats_card.test.tsx.snap deleted file mode 100644 index af26f9e93ebac..0000000000000 --- a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/__snapshots__/elastic_beats_card.test.tsx.snap +++ /dev/null @@ -1,70 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`ElasticBeatsCard props button 1`] = ` - - Button - - } - href="/app/home#/tutorial_directory" - image="/plugins/kibanaReact/assets/elastic_beats_card_light.svg" - paddingSize="l" - title="Add data" -/> -`; - -exports[`ElasticBeatsCard props href 1`] = ` - - Button - - } - href="#" - image="/plugins/kibanaReact/assets/elastic_beats_card_light.svg" - paddingSize="l" - title="Add data" -/> -`; - -exports[`ElasticBeatsCard props recommended 1`] = ` - - Add data - - } - href="/app/home#/tutorial_directory" - image="/plugins/kibanaReact/assets/elastic_beats_card_light.svg" - paddingSize="l" - title="Add data" -/> -`; - -exports[`ElasticBeatsCard renders 1`] = ` - - Add data - - } - href="/app/home#/tutorial_directory" - image="/plugins/kibanaReact/assets/elastic_beats_card_light.svg" - paddingSize="l" - title="Add data" -/> -`; diff --git a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_agent_card.test.tsx b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_agent_card.test.tsx index 45cc32cae06d6..b971abf06a437 100644 --- a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_agent_card.test.tsx +++ b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_agent_card.test.tsx @@ -14,7 +14,10 @@ jest.mock('../../../context', () => ({ ...jest.requireActual('../../../context'), useKibana: jest.fn().mockReturnValue({ services: { - http: { basePath: { prepend: jest.fn((path: string) => (path ? path : 'path')) } }, + http: { + basePath: { prepend: jest.fn((path: string) => (path ? path : 'path')) }, + }, + application: { capabilities: { navLinks: { integrations: true } } }, uiSettings: { get: jest.fn() }, }, }), @@ -41,5 +44,10 @@ describe('ElasticAgentCard', () => { const component = shallow(); expect(component).toMatchSnapshot(); }); + + test('category', () => { + const component = shallow(); + expect(component).toMatchSnapshot(); + }); }); }); diff --git a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_agent_card.tsx b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_agent_card.tsx index f071bd9fab25a..5a91e568471d1 100644 --- a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_agent_card.tsx +++ b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_agent_card.tsx @@ -9,7 +9,7 @@ import React, { FunctionComponent } from 'react'; import { i18n } from '@kbn/i18n'; import { CoreStart } from 'kibana/public'; -import { EuiButton, EuiCard } from '@elastic/eui'; +import { EuiButton, EuiCard, EuiTextColor, EuiScreenReaderOnly } from '@elastic/eui'; import { useKibana } from '../../../context'; import { NoDataPageActions, NO_DATA_RECOMMENDED } from '../no_data_page'; @@ -27,13 +27,40 @@ export const ElasticAgentCard: FunctionComponent = ({ href, button, layout, + category, ...cardRest }) => { const { - services: { http }, + services: { http, application }, } = useKibana(); const addBasePath = http.basePath.prepend; - const basePathUrl = '/plugins/kibanaReact/assets/'; + const image = addBasePath(`/plugins/kibanaReact/assets/elastic_agent_card.svg`); + const canAccessFleet = application.capabilities.navLinks.integrations; + const hasCategory = category ? `/${category}` : ''; + + if (!canAccessFleet) { + return ( + + {i18n.translate('kibana-react.noDataPage.elasticAgentCard.noPermission.title', { + defaultMessage: `Contact your administrator`, + })} + + } + description={ + + {i18n.translate('kibana-react.noDataPage.elasticAgentCard.noPermission.description', { + defaultMessage: `This integration is not yet enabled. Your administrator has the required permissions to turn it on.`, + })} + + } + isDisabled + /> + ); + } const defaultCTAtitle = i18n.translate('kibana-react.noDataPage.elasticAgentCard.title', { defaultMessage: 'Add Elastic Agent', @@ -51,12 +78,17 @@ export const ElasticAgentCard: FunctionComponent = ({ return ( + {defaultCTAtitle} + + } description={i18n.translate('kibana-react.noDataPage.elasticAgentCard.description', { defaultMessage: `Use Elastic Agent for a simple, unified way to collect data from your machines.`, })} - image={addBasePath(`${basePathUrl}elastic_agent_card.svg`)} betaBadgeLabel={recommended ? NO_DATA_RECOMMENDED : undefined} footer={footer} layout={layout as 'vertical' | undefined} diff --git a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_beats_card.test.tsx b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_beats_card.test.tsx deleted file mode 100644 index 6ea41bf6b3e1f..0000000000000 --- a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_beats_card.test.tsx +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { shallow } from 'enzyme'; -import React from 'react'; -import { ElasticBeatsCard } from './elastic_beats_card'; - -jest.mock('../../../context', () => ({ - ...jest.requireActual('../../../context'), - useKibana: jest.fn().mockReturnValue({ - services: { - http: { basePath: { prepend: jest.fn((path: string) => (path ? path : 'path')) } }, - uiSettings: { get: jest.fn() }, - }, - }), -})); - -describe('ElasticBeatsCard', () => { - test('renders', () => { - const component = shallow(); - expect(component).toMatchSnapshot(); - }); - - describe('props', () => { - test('recommended', () => { - const component = shallow(); - expect(component).toMatchSnapshot(); - }); - - test('button', () => { - const component = shallow(); - expect(component).toMatchSnapshot(); - }); - - test('href', () => { - const component = shallow(); - expect(component).toMatchSnapshot(); - }); - }); -}); diff --git a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_beats_card.tsx b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_beats_card.tsx deleted file mode 100644 index 0372d12096489..0000000000000 --- a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_beats_card.tsx +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import React, { FunctionComponent } from 'react'; -import { i18n } from '@kbn/i18n'; -import { CoreStart } from 'kibana/public'; -import { EuiButton, EuiCard } from '@elastic/eui'; -import { useKibana } from '../../../context'; -import { NoDataPageActions, NO_DATA_RECOMMENDED } from '../no_data_page'; - -export type ElasticBeatsCardProps = NoDataPageActions & { - solution: string; -}; - -export const ElasticBeatsCard: FunctionComponent = ({ - recommended, - title, - button, - href, - solution, // unused for now - layout, - ...cardRest -}) => { - const { - services: { http, uiSettings }, - } = useKibana(); - const addBasePath = http.basePath.prepend; - const basePathUrl = '/plugins/kibanaReact/assets/'; - const IS_DARK_THEME = uiSettings.get('theme:darkMode'); - - const defaultCTAtitle = i18n.translate('kibana-react.noDataPage.elasticBeatsCard.title', { - defaultMessage: 'Add data', - }); - - const footer = - typeof button !== 'string' && typeof button !== 'undefined' ? ( - button - ) : ( - // The href and/or onClick are attached to the whole Card, so the button is just for show. - // Do not add the behavior here too or else it will propogate through - {button || title || defaultCTAtitle} - ); - - return ( - - ); -}; diff --git a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/index.ts b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/index.ts index 3744239d9a472..e05d4d9675ca9 100644 --- a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/index.ts +++ b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/index.ts @@ -7,5 +7,4 @@ */ export * from './elastic_agent_card'; -export * from './elastic_beats_card'; export * from './no_data_card'; diff --git a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_page.tsx b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_page.tsx index 56eb0f34617d6..b2d9ef6ca5008 100644 --- a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_page.tsx +++ b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_page.tsx @@ -22,7 +22,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { KibanaPageTemplateProps } from '../page_template'; -import { ElasticAgentCard, ElasticBeatsCard, NoDataCard } from './no_data_card'; +import { ElasticAgentCard, NoDataCard } from './no_data_card'; import { KibanaPageTemplateSolutionNavAvatar } from '../solution_nav'; export const NO_DATA_PAGE_MAX_WIDTH = 950; @@ -55,6 +55,10 @@ export type NoDataPageActions = Partial & { * Remapping `onClick` to any element */ onClick?: MouseEventHandler; + /** + * Category to auto-select within Fleet + */ + category?: string; }; export type NoDataPageActionsProps = Record; @@ -107,18 +111,12 @@ export const NoDataPage: FunctionComponent = ({ const actionsKeys = Object.keys(sortedData); const renderActions = useMemo(() => { return Object.values(sortedData).map((action, i) => { - if (actionsKeys[i] === 'elasticAgent') { + if (actionsKeys[i] === 'elasticAgent' || actionsKeys[i] === 'beats') { return ( ); - } else if (actionsKeys[i] === 'beats') { - return ( - - - - ); } else { return ( ), discussForumLink: ( - + import('./tutorial_directory_notice')); -export const TutorialDirectoryNotice: TutorialDirectoryNoticeComponent = () => ( - }> - - -); - const TutorialDirectoryHeaderLinkLazy = React.lazy( () => import('./tutorial_directory_header_link') ); diff --git a/x-pack/plugins/fleet/public/components/home_integration/tutorial_directory_header_link.tsx b/x-pack/plugins/fleet/public/components/home_integration/tutorial_directory_header_link.tsx index 074a1c40bdb19..18fdd875c7379 100644 --- a/x-pack/plugins/fleet/public/components/home_integration/tutorial_directory_header_link.tsx +++ b/x-pack/plugins/fleet/public/components/home_integration/tutorial_directory_header_link.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { memo, useState, useEffect } from 'react'; +import React, { memo, useState } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiButtonEmpty } from '@elastic/eui'; import type { TutorialDirectoryHeaderLinkComponent } from 'src/plugins/home/public'; @@ -13,25 +13,15 @@ import type { TutorialDirectoryHeaderLinkComponent } from 'src/plugins/home/publ import { RedirectAppLinks } from '../../../../../../src/plugins/kibana_react/public'; import { useLink, useCapabilities, useStartServices } from '../../hooks'; -import { tutorialDirectoryNoticeState$ } from './tutorial_directory_notice'; - const TutorialDirectoryHeaderLink: TutorialDirectoryHeaderLinkComponent = memo(() => { const { getHref } = useLink(); const { application } = useStartServices(); const { show: hasIngestManager } = useCapabilities(); - const [noticeState, setNoticeState] = useState({ + const [noticeState] = useState({ settingsDataLoaded: false, - hasSeenNotice: false, }); - useEffect(() => { - const subscription = tutorialDirectoryNoticeState$.subscribe((value) => setNoticeState(value)); - return () => { - subscription.unsubscribe(); - }; - }, []); - - return hasIngestManager && noticeState.settingsDataLoaded && noticeState.hasSeenNotice ? ( + return hasIngestManager && noticeState.settingsDataLoaded ? ( { - const { getHref } = useLink(); - const { application } = useStartServices(); - const { show: hasIngestManager } = useCapabilities(); - const { data: settingsData, isLoading } = useGetSettings(); - const [dismissedNotice, setDismissedNotice] = useState(false); - - const dismissNotice = useCallback(async () => { - setDismissedNotice(true); - await sendPutSettings({ - has_seen_add_data_notice: true, - }); - }, []); - - useEffect(() => { - tutorialDirectoryNoticeState$.next({ - settingsDataLoaded: !isLoading, - hasSeenNotice: Boolean(dismissedNotice || settingsData?.item?.has_seen_add_data_notice), - }); - }, [isLoading, settingsData, dismissedNotice]); - - const hasSeenNotice = - isLoading || settingsData?.item?.has_seen_add_data_notice || dismissedNotice; - - return hasIngestManager && !hasSeenNotice ? ( - <> - - - - ), - }} - /> - } - > -

- - - - ), - }} - /> -

- - -
- - - - - -
-
- -
- { - dismissNotice(); - }} - > - - -
-
-
-
- - - ) : null; -}); - -// Needed for React.lazy -// eslint-disable-next-line import/no-default-export -export default TutorialDirectoryNotice; diff --git a/x-pack/plugins/fleet/public/plugin.ts b/x-pack/plugins/fleet/public/plugin.ts index e1f263b0763e8..4a2a6900cc78c 100644 --- a/x-pack/plugins/fleet/public/plugin.ts +++ b/x-pack/plugins/fleet/public/plugin.ts @@ -44,11 +44,7 @@ import { CUSTOM_LOGS_INTEGRATION_NAME, INTEGRATIONS_BASE_PATH } from './constant import { licenseService } from './hooks'; import { setHttpClient } from './hooks/use_request'; import { createPackageSearchProvider } from './search_provider'; -import { - TutorialDirectoryNotice, - TutorialDirectoryHeaderLink, - TutorialModuleNotice, -} from './components/home_integration'; +import { TutorialDirectoryHeaderLink, TutorialModuleNotice } from './components/home_integration'; import { createExtensionRegistrationCallback } from './services/ui_extensions'; import type { UIExtensionRegistrationCallback, UIExtensionsStorage } from './types'; import { LazyCustomLogsAssetsExtension } from './lazy_custom_logs_assets_extension'; @@ -197,7 +193,6 @@ export class FleetPlugin implements Plugin { diff --git a/x-pack/plugins/infra/public/pages/logs/page_template.tsx b/x-pack/plugins/infra/public/pages/logs/page_template.tsx index 7ee60ab84bf25..6de13b495f0ba 100644 --- a/x-pack/plugins/infra/public/pages/logs/page_template.tsx +++ b/x-pack/plugins/infra/public/pages/logs/page_template.tsx @@ -44,13 +44,13 @@ export const LogsPageTemplate: React.FC = ({ actions: { beats: { title: i18n.translate('xpack.infra.logs.noDataConfig.beatsCard.title', { - defaultMessage: 'Add logs with Beats', + defaultMessage: 'Add a logging integration', }), description: i18n.translate('xpack.infra.logs.noDataConfig.beatsCard.description', { defaultMessage: - 'Use Beats to send logs to Elasticsearch. We make it easy with modules for many popular systems and apps.', + 'Use the Elastic Agent or Beats to send logs to Elasticsearch. We make it easy with integrations for many popular systems and apps.', }), - href: basePath + `/app/home#/tutorial_directory/logging`, + href: basePath + `/app/integrations/browse`, }, }, docsLink: docLinks.links.observability.guide, diff --git a/x-pack/plugins/infra/public/pages/logs/stream/page_no_indices_content.tsx b/x-pack/plugins/infra/public/pages/logs/stream/page_no_indices_content.tsx index bc3bc22f3f1b2..2259a8d3528af 100644 --- a/x-pack/plugins/infra/public/pages/logs/stream/page_no_indices_content.tsx +++ b/x-pack/plugins/infra/public/pages/logs/stream/page_no_indices_content.tsx @@ -22,8 +22,8 @@ export const LogsPageNoIndicesContent = () => { const canConfigureSource = application?.capabilities?.logs?.configureSource ? true : false; const tutorialLinkProps = useLinkProps({ - app: 'home', - hash: '/tutorial_directory/logging', + app: 'integrations', + hash: '/browse', }); return ( diff --git a/x-pack/plugins/infra/public/pages/metrics/index.tsx b/x-pack/plugins/infra/public/pages/metrics/index.tsx index ae375dc504e7a..1a79cd996087d 100644 --- a/x-pack/plugins/infra/public/pages/metrics/index.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/index.tsx @@ -93,9 +93,7 @@ export const InfrastructurePage = ({ match }: RouteComponentProps) => { diff --git a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/invalid_node.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/invalid_node.tsx index 2a436eac30b2c..17e6382ce65cc 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/invalid_node.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/invalid_node.tsx @@ -18,8 +18,8 @@ interface InvalidNodeErrorProps { export const InvalidNodeError: React.FunctionComponent = ({ nodeName }) => { const tutorialLinkProps = useLinkProps({ - app: 'home', - hash: '/tutorial_directory/metrics', + app: 'integrations', + hash: '/browse', }); return ( diff --git a/x-pack/plugins/infra/public/pages/metrics/page_template.tsx b/x-pack/plugins/infra/public/pages/metrics/page_template.tsx index 41ea12c280841..4da671283644d 100644 --- a/x-pack/plugins/infra/public/pages/metrics/page_template.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/page_template.tsx @@ -10,7 +10,6 @@ import { i18n } from '@kbn/i18n'; import { useKibanaContextForPlugin } from '../../hooks/use_kibana'; import type { LazyObservabilityPageTemplateProps } from '../../../../observability/public'; import { KibanaPageTemplateProps } from '../../../../../../src/plugins/kibana_react/public'; -import { useLinkProps } from '../../hooks/use_link_props'; interface MetricsPageTemplateProps extends LazyObservabilityPageTemplateProps { hasData?: boolean; @@ -30,11 +29,6 @@ export const MetricsPageTemplate: React.FC = ({ }, } = useKibanaContextForPlugin(); - const tutorialLinkProps = useLinkProps({ - app: 'home', - hash: '/tutorial_directory/metrics', - }); - const noDataConfig: KibanaPageTemplateProps['noDataConfig'] = hasData ? undefined : { @@ -44,13 +38,12 @@ export const MetricsPageTemplate: React.FC = ({ actions: { beats: { title: i18n.translate('xpack.infra.metrics.noDataConfig.beatsCard.title', { - defaultMessage: 'Add metrics with Beats', + defaultMessage: 'Add a metrics integration', }), description: i18n.translate('xpack.infra.metrics.noDataConfig.beatsCard.description', { defaultMessage: 'Use Beats to send metrics data to Elasticsearch. We make it easy with modules for many popular systems and apps.', }), - ...tutorialLinkProps, }, }, docsLink: docLinks.links.observability.guide, diff --git a/x-pack/plugins/observability/public/components/app/header/header_menu.tsx b/x-pack/plugins/observability/public/components/app/header/header_menu.tsx index 707cb241501fd..0ed01b7d3673e 100644 --- a/x-pack/plugins/observability/public/components/app/header/header_menu.tsx +++ b/x-pack/plugins/observability/public/components/app/header/header_menu.tsx @@ -26,7 +26,7 @@ export function ObservabilityHeaderMenu(): React.ReactElement | null { {addDataLinkText} diff --git a/x-pack/plugins/observability/public/utils/no_data_config.ts b/x-pack/plugins/observability/public/utils/no_data_config.ts index 1e16fb145bdce..2c87b1434a0b4 100644 --- a/x-pack/plugins/observability/public/utils/no_data_config.ts +++ b/x-pack/plugins/observability/public/utils/no_data_config.ts @@ -24,12 +24,15 @@ export function getNoDataConfig({ defaultMessage: 'Observability', }), actions: { - beats: { + elasticAgent: { + title: i18n.translate('xpack.observability.noDataConfig.beatsCard.title', { + defaultMessage: 'Add integrations', + }), description: i18n.translate('xpack.observability.noDataConfig.beatsCard.description', { defaultMessage: 'Use Beats and APM agents to send observability data to Elasticsearch. We make it easy with support for many popular systems, apps, and languages.', }), - href: basePath.prepend(`/app/home#/tutorial_directory/logging`), + href: basePath.prepend(`/app/integrations`), }, }, docsLink, diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts index 5c41e92661e58..5a7e19e2cdd05 100644 --- a/x-pack/plugins/security_solution/common/constants.ts +++ b/x-pack/plugins/security_solution/common/constants.ts @@ -16,7 +16,7 @@ export const APP_NAME = 'Security'; export const APP_ICON = 'securityAnalyticsApp'; export const APP_ICON_SOLUTION = 'logoSecurity'; export const APP_PATH = `/app/security`; -export const ADD_DATA_PATH = `/app/home#/tutorial_directory/security`; +export const ADD_DATA_PATH = `/app/integrations/browse/security`; export const DEFAULT_BYTES_FORMAT = 'format:bytes:defaultPattern'; export const DEFAULT_DATE_FORMAT = 'dateFormat'; export const DEFAULT_DATE_FORMAT_TZ = 'dateFormat:tz'; diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.test.tsx index 5fa2725f9ee6f..61e9e66f1bb87 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.test.tsx @@ -45,9 +45,10 @@ describe('OverviewEmpty', () => { expect(wrapper.find('[data-test-subj="empty-page"]').prop('noDataConfig')).toEqual({ actions: { elasticAgent: { + category: 'security', description: - 'Use Elastic Agent to collect security events and protect your endpoints from threats. Manage your agents in Fleet and add integrations with a single click.', - href: '/app/integrations/browse/security', + 'Use Elastic Agent to collect security events and protect your endpoints from threats.', + title: 'Add a Security integration', }, }, docsLink: 'https://www.elastic.co/guide/en/security/mocked-test-branch/index.html', @@ -68,8 +69,11 @@ describe('OverviewEmpty', () => { it('render with correct actions ', () => { expect(wrapper.find('[data-test-subj="empty-page"]').prop('noDataConfig')).toEqual({ actions: { - beats: { - href: '/app/home#/tutorial_directory/security', + elasticAgent: { + category: 'security', + description: + 'Use Elastic Agent to collect security events and protect your endpoints from threats.', + title: 'Add a Security integration', }, }, docsLink: 'https://www.elastic.co/guide/en/security/mocked-test-branch/index.html', diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.tsx index bc76333943191..9b20c079002e6 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.tsx @@ -5,13 +5,10 @@ * 2.0. */ -import React, { useMemo } from 'react'; +import React from 'react'; import { i18n } from '@kbn/i18n'; import { useKibana } from '../../../common/lib/kibana'; -import { ADD_DATA_PATH } from '../../../../common/constants'; -import { pagePathGetters } from '../../../../../fleet/public'; import { SOLUTION_NAME } from '../../../../public/common/translations'; -import { useUserPrivileges } from '../../../common/components/user_privileges'; import { KibanaPageTemplate, @@ -19,42 +16,27 @@ import { } from '../../../../../../../src/plugins/kibana_react/public'; const OverviewEmptyComponent: React.FC = () => { - const { http, docLinks } = useKibana().services; - const basePath = http.basePath.get(); - const canAccessFleet = useUserPrivileges().endpointPrivileges.canAccessFleet; - const integrationsPathComponents = pagePathGetters.integrations_all({ category: 'security' }); - - const agentAction: NoDataPageActionsProps = useMemo( - () => ({ - elasticAgent: { - href: `${basePath}${integrationsPathComponents[0]}${integrationsPathComponents[1]}`, - description: i18n.translate( - 'xpack.securitySolution.pages.emptyPage.beatsCard.description', - { - defaultMessage: - 'Use Elastic Agent to collect security events and protect your endpoints from threats. Manage your agents in Fleet and add integrations with a single click.', - } - ), - }, - }), - [basePath, integrationsPathComponents] - ); - - const beatsAction: NoDataPageActionsProps = useMemo( - () => ({ - beats: { - href: `${basePath}${ADD_DATA_PATH}`, - }, - }), - [basePath] - ); + const { docLinks } = useKibana().services; + + const agentAction: NoDataPageActionsProps = { + elasticAgent: { + category: 'security', + title: i18n.translate('xpack.securitySolution.pages.emptyPage.beatsCard.title', { + defaultMessage: 'Add a Security integration', + }), + description: i18n.translate('xpack.securitySolution.pages.emptyPage.beatsCard.description', { + defaultMessage: + 'Use Elastic Agent to collect security events and protect your endpoints from threats.', + }), + }, + }; return ( diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 3df5e4ee6c48a..852b01977b78b 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -3009,11 +3009,7 @@ "home.tutorial.savedObject.unableToAddErrorMessage": "{savedObjectsLength} 件中 {errorsLength} 件の kibana オブジェクトが追加できません。エラー:{errorMessage}", "home.tutorial.selectionLegend": "デプロイタイプ", "home.tutorial.selfManagedButtonLabel": "自己管理", - "home.tutorial.tabs.allTitle": "すべて", - "home.tutorial.tabs.loggingTitle": "ログ", - "home.tutorial.tabs.metricsTitle": "メトリック", "home.tutorial.tabs.sampleDataTitle": "サンプルデータ", - "home.tutorial.tabs.securitySolutionTitle": "セキュリティ", "home.tutorial.unexpectedStatusCheckStateErrorDescription": "予期せぬステータス確認ステータス {statusCheckState}", "home.tutorial.unhandledInstructionTypeErrorDescription": "予期せぬ指示タイプ {visibleInstructions}", "home.tutorialDirectory.featureCatalogueDescription": "一般的なアプリやサービスからデータを取り込みます。", @@ -4204,8 +4200,6 @@ "kibana-react.noDataPage.cantDecide.link": "詳細については、ドキュメントをご確認ください。", "kibana-react.noDataPage.elasticAgentCard.description": "Elasticエージェントを使用すると、シンプルで統一された方法でコンピューターからデータを収集するできます。", "kibana-react.noDataPage.elasticAgentCard.title": "Elasticエージェントの追加", - "kibana-react.noDataPage.elasticBeatsCard.description": "Beatsを使用して、さまざまなシステムのデータをElasticsearchに追加します。", - "kibana-react.noDataPage.elasticBeatsCard.title": "データの追加", "kibana-react.noDataPage.intro": "データを追加して開始するか、{solution}については{link}をご覧ください。", "kibana-react.noDataPage.intro.link": "詳細", "kibana-react.noDataPage.noDataPage.recommended": "推奨", @@ -11076,12 +11070,7 @@ "xpack.fleet.fleetServerUpgradeModal.modalTitle": "エージェントをFleetサーバーに登録", "xpack.fleet.fleetServerUpgradeModal.onPremDescriptionMessage": "Fleetサーバーが使用できます。スケーラビリティとセキュリティが改善されています。{existingAgentsMessage} Fleetを使用し続けるには、Fleetサーバーと新しいバージョンのElasticエージェントを各ホストにインストールする必要があります。詳細については、{link}をご覧ください。", "xpack.fleet.genericActionsMenuText": "開く", - "xpack.fleet.homeIntegration.tutorialDirectory.dismissNoticeButtonText": "メッセージを消去", "xpack.fleet.homeIntegration.tutorialDirectory.fleetAppButtonText": "統合を試す", - "xpack.fleet.homeIntegration.tutorialDirectory.noticeText": "Elasticエージェント統合では、シンプルかつ統合された方法で、ログ、メトリック、他の種類のデータの監視をホストに追加することができます。複数のBeatsをインストールする必要はありません。このため、インフラストラクチャ全体でのポリシーのデプロイが簡単で高速になりました。詳細については、{blogPostLink}をお読みください。", - "xpack.fleet.homeIntegration.tutorialDirectory.noticeText.blogPostLink": "発表ブログ投稿", - "xpack.fleet.homeIntegration.tutorialDirectory.noticeTitle": "{newPrefix} Elasticエージェント統合", - "xpack.fleet.homeIntegration.tutorialDirectory.noticeTitle.newPrefix": "一般公開へ:", "xpack.fleet.homeIntegration.tutorialModule.noticeText": "{notePrefix}このモジュールの新しいバージョンは{availableAsIntegrationLink}です。統合と新しいElasticエージェントの詳細については、{blogPostLink}をお読みください。", "xpack.fleet.homeIntegration.tutorialModule.noticeText.blogPostLink": "発表ブログ投稿", "xpack.fleet.homeIntegration.tutorialModule.noticeText.integrationLink": "Elasticエージェント統合として提供", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index d9af3edb8101d..9d88c757f1e58 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -3038,11 +3038,7 @@ "home.tutorial.savedObject.unableToAddErrorMessage": "{savedObjectsLength} 个 kibana 对象中有 {errorsLength} 个无法添加,错误:{errorMessage}", "home.tutorial.selectionLegend": "部署类型", "home.tutorial.selfManagedButtonLabel": "自管型", - "home.tutorial.tabs.allTitle": "全部", - "home.tutorial.tabs.loggingTitle": "日志", - "home.tutorial.tabs.metricsTitle": "指标", "home.tutorial.tabs.sampleDataTitle": "样例数据", - "home.tutorial.tabs.securitySolutionTitle": "安全", "home.tutorial.unexpectedStatusCheckStateErrorDescription": "意外的状态检查状态 {statusCheckState}", "home.tutorial.unhandledInstructionTypeErrorDescription": "未处理的指令类型 {visibleInstructions}", "home.tutorialDirectory.featureCatalogueDescription": "从热门应用和服务中采集数据。", @@ -4244,8 +4240,6 @@ "kibana-react.noDataPage.cantDecide.link": "请参阅我们的文档以了解更多信息。", "kibana-react.noDataPage.elasticAgentCard.description": "使用 Elastic 代理以简单统一的方式从您的计算机中收集数据。", "kibana-react.noDataPage.elasticAgentCard.title": "添加 Elastic 代理", - "kibana-react.noDataPage.elasticBeatsCard.description": "使用 Beats 将各种系统的数据添加到 Elasticsearch。", - "kibana-react.noDataPage.elasticBeatsCard.title": "添加数据", "kibana-react.noDataPage.intro": "添加您的数据以开始,或{link}{solution}。", "kibana-react.noDataPage.intro.link": "了解详情", "kibana-react.noDataPage.noDataPage.recommended": "推荐", @@ -11191,12 +11185,7 @@ "xpack.fleet.fleetServerUpgradeModal.modalTitle": "将代理注册到 Fleet 服务器", "xpack.fleet.fleetServerUpgradeModal.onPremDescriptionMessage": "Fleet 服务器现在可用且提供改善的可扩展性和安全性。{existingAgentsMessage}要继续使用 Fleet,必须在各个主机上安装 Fleet 服务器和新版 Elastic 代理。详细了解我们的 {link}。", "xpack.fleet.genericActionsMenuText": "打开", - "xpack.fleet.homeIntegration.tutorialDirectory.dismissNoticeButtonText": "关闭消息", "xpack.fleet.homeIntegration.tutorialDirectory.fleetAppButtonText": "试用集成", - "xpack.fleet.homeIntegration.tutorialDirectory.noticeText": "通过 Elastic 代理集成,可以简单统一的方式将日志、指标和其他类型数据的监测添加到主机。不再需要安装多个 Beats,这样将策略部署到整个基础架构更容易也更快速。有关更多信息,请阅读我们的{blogPostLink}。", - "xpack.fleet.homeIntegration.tutorialDirectory.noticeText.blogPostLink": "公告博客", - "xpack.fleet.homeIntegration.tutorialDirectory.noticeTitle": "{newPrefix}Elastic 代理集成", - "xpack.fleet.homeIntegration.tutorialDirectory.noticeTitle.newPrefix": "已正式发布:", "xpack.fleet.homeIntegration.tutorialModule.noticeText": "{notePrefix}此模块的较新版本{availableAsIntegrationLink}。要详细了解集成和新 Elastic 代理,请阅读我们的{blogPostLink}。", "xpack.fleet.homeIntegration.tutorialModule.noticeText.blogPostLink": "公告博客", "xpack.fleet.homeIntegration.tutorialModule.noticeText.integrationLink": "将作为 Elastic 代理集成来提供", diff --git a/x-pack/test/accessibility/apps/home.ts b/x-pack/test/accessibility/apps/home.ts index a7158d9579b60..61297859c29f8 100644 --- a/x-pack/test/accessibility/apps/home.ts +++ b/x-pack/test/accessibility/apps/home.ts @@ -64,33 +64,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await a11y.testAppSnapshot(); }); - it('Add data page meets a11y requirements ', async () => { - await home.clickGoHome(); - await testSubjects.click('homeAddData'); - await a11y.testAppSnapshot(); - }); - - it('Sample data page meets a11y requirements ', async () => { - await testSubjects.click('homeTab-sampleData'); - await a11y.testAppSnapshot(); - }); - - it('click on Add logs panel to open all log examples page meets a11y requirements ', async () => { - await testSubjects.click('sampleDataSetCardlogs'); - await a11y.testAppSnapshot(); - }); - - it('click on ActiveMQ logs panel to open tutorial meets a11y requirements', async () => { - await testSubjects.click('homeTab-all'); - await testSubjects.click('homeSynopsisLinkactivemqlogs'); - await a11y.testAppSnapshot(); - }); - - it('click on cloud tutorial meets a11y requirements', async () => { - await testSubjects.click('onCloudTutorial'); - await a11y.testAppSnapshot(); - }); - it('passes with searchbox open', async () => { await testSubjects.click('nav-search-popover'); await a11y.testAppSnapshot(); From fd0fc770623b483a0eb7de48a4e3407446de9bd7 Mon Sep 17 00:00:00 2001 From: Frank Hassanabad Date: Mon, 18 Oct 2021 22:24:20 -0600 Subject: [PATCH 028/204] Fixes console errors seen (#115448) ## Summary During testing I encountered this error message: ``` [2021-10-18T13:19:07.053-06:00][ERROR][plugins.securitySolution] The notification throttle "from" and/or "to" range values could not be constructed as valid. Tried to construct the values of "from": now-null "to": 2021-10-18T19:19:00.835Z. This will cause a reset of the notification throttle. Expect either missing alert notifications or alert notifications happening earlier than expected. ``` This error was happening whenever I had a rule that was using an immediately invoked action and was encountering an error such as a non ECS compliant signal. The root cause is that I was not checking everywhere to ensure we had a throttle rule to ensure scheduling. This fixes that by adding an `if` statement/guard around the areas of code. I also improve the error message by adding which ruleId the error is coming from. ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --- ...dule_throttle_notification_actions.test.ts | 2 +- .../schedule_throttle_notification_actions.ts | 1 + .../create_security_rule_type_wrapper.ts | 60 +++++++++-------- .../legacy_notifications/one_action.json | 2 +- .../signals/signal_rule_alert_type.test.ts | 64 ++++++++++++++++++- .../signals/signal_rule_alert_type.ts | 61 +++++++++--------- 6 files changed, 129 insertions(+), 61 deletions(-) diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/schedule_throttle_notification_actions.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/schedule_throttle_notification_actions.test.ts index 81f229c636bd8..964df3c91eb08 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/schedule_throttle_notification_actions.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/schedule_throttle_notification_actions.test.ts @@ -278,7 +278,7 @@ describe('schedule_throttle_notification_actions', () => { }); expect(logger.error).toHaveBeenCalledWith( - 'The notification throttle "from" and/or "to" range values could not be constructed as valid. Tried to construct the values of "from": now-invalid "to": 2021-08-24T19:19:22.094Z. This will cause a reset of the notification throttle. Expect either missing alert notifications or alert notifications happening earlier than expected.' + 'The notification throttle "from" and/or "to" range values could not be constructed as valid. Tried to construct the values of "from": now-invalid "to": 2021-08-24T19:19:22.094Z. This will cause a reset of the notification throttle. Expect either missing alert notifications or alert notifications happening earlier than expected. Check your rule with ruleId: "rule-123" for data integrity issues' ); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/schedule_throttle_notification_actions.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/schedule_throttle_notification_actions.ts index 5bf18496e6375..7b4b314cc8911 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/schedule_throttle_notification_actions.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/schedule_throttle_notification_actions.ts @@ -145,6 +145,7 @@ export const scheduleThrottledNotificationActions = async ({ ` "from": now-${throttle}`, ` "to": ${startedAt.toISOString()}.`, ' This will cause a reset of the notification throttle. Expect either missing alert notifications or alert notifications happening earlier than expected.', + ` Check your rule with ruleId: "${ruleId}" for data integrity issues`, ].join('') ); } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts index 0ad416e86e31a..0fe7cbdc9bd9f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts @@ -375,20 +375,22 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = ); } else { // NOTE: Since this is throttled we have to call it even on an error condition, otherwise it will "reset" the throttle and fire early - await scheduleThrottledNotificationActions({ - alertInstance: services.alertInstanceFactory(alertId), - throttle: ruleSO.attributes.throttle, - startedAt, - id: ruleSO.id, - kibanaSiemAppUrl: (meta as { kibana_siem_app_url?: string } | undefined) - ?.kibana_siem_app_url, - outputIndex: ruleDataClient.indexName, - ruleId, - esClient: services.scopedClusterClient.asCurrentUser, - notificationRuleParams, - signals: result.createdSignals, - logger, - }); + if (ruleSO.attributes.throttle != null) { + await scheduleThrottledNotificationActions({ + alertInstance: services.alertInstanceFactory(alertId), + throttle: ruleSO.attributes.throttle, + startedAt, + id: ruleSO.id, + kibanaSiemAppUrl: (meta as { kibana_siem_app_url?: string } | undefined) + ?.kibana_siem_app_url, + outputIndex: ruleDataClient.indexName, + ruleId, + esClient: services.scopedClusterClient.asCurrentUser, + notificationRuleParams, + signals: result.createdSignals, + logger, + }); + } const errorMessage = buildRuleMessage( 'Bulk Indexing of signals failed:', truncateMessageList(result.errors).join() @@ -407,20 +409,22 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = } } catch (error) { // NOTE: Since this is throttled we have to call it even on an error condition, otherwise it will "reset" the throttle and fire early - await scheduleThrottledNotificationActions({ - alertInstance: services.alertInstanceFactory(alertId), - throttle: ruleSO.attributes.throttle, - startedAt, - id: ruleSO.id, - kibanaSiemAppUrl: (meta as { kibana_siem_app_url?: string } | undefined) - ?.kibana_siem_app_url, - outputIndex: ruleDataClient.indexName, - ruleId, - esClient: services.scopedClusterClient.asCurrentUser, - notificationRuleParams, - signals: result.createdSignals, - logger, - }); + if (ruleSO.attributes.throttle != null) { + await scheduleThrottledNotificationActions({ + alertInstance: services.alertInstanceFactory(alertId), + throttle: ruleSO.attributes.throttle, + startedAt, + id: ruleSO.id, + kibanaSiemAppUrl: (meta as { kibana_siem_app_url?: string } | undefined) + ?.kibana_siem_app_url, + outputIndex: ruleDataClient.indexName, + ruleId, + esClient: services.scopedClusterClient.asCurrentUser, + notificationRuleParams, + signals: result.createdSignals, + logger, + }); + } const errorMessage = error.message ?? '(no error message given)'; const message = buildRuleMessage( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/legacy_notifications/one_action.json b/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/legacy_notifications/one_action.json index 1966dcf5ff53c..bf980e370e3a3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/legacy_notifications/one_action.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/legacy_notifications/one_action.json @@ -3,7 +3,7 @@ "interval": "1m", "actions": [ { - "id": "42534430-2092-11ec-99a6-05d79563c01a", + "id": "1fa31c30-3046-11ec-8971-1f3f7bae65af", "group": "default", "params": { "message": "Hourly\nRule {{context.rule.name}} generated {{state.signals_count}} alerts" diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts index 88b276358a705..6a84776ccee5d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts @@ -536,14 +536,74 @@ describe('signal_rule_alert_type', () => { errors: ['Error that bubbled up.'], }; (queryExecutor as jest.Mock).mockResolvedValue(result); - await alert.executor(payload); + const ruleAlert = getAlertMock(false, getQueryRuleParams()); + ruleAlert.throttle = '1h'; + const payLoadWithThrottle = getPayload( + ruleAlert, + alertServices + ) as jest.Mocked; + payLoadWithThrottle.rule.throttle = '1h'; + alertServices.savedObjectsClient.get.mockResolvedValue({ + id: 'id', + type: 'type', + references: [], + attributes: ruleAlert, + }); + await alert.executor(payLoadWithThrottle); expect(scheduleThrottledNotificationActions).toHaveBeenCalledTimes(1); }); + it('should NOT call scheduleThrottledNotificationActions if result is false and the throttle is not set', async () => { + const result: SearchAfterAndBulkCreateReturnType = { + success: false, + warning: false, + searchAfterTimes: [], + bulkCreateTimes: [], + lastLookBackDate: null, + createdSignalsCount: 0, + createdSignals: [], + warningMessages: [], + errors: ['Error that bubbled up.'], + }; + (queryExecutor as jest.Mock).mockResolvedValue(result); + await alert.executor(payload); + expect(scheduleThrottledNotificationActions).toHaveBeenCalledTimes(0); + }); + it('should call scheduleThrottledNotificationActions if an error was thrown to prevent the throttle from being reset', async () => { (queryExecutor as jest.Mock).mockRejectedValue({}); - await alert.executor(payload); + const ruleAlert = getAlertMock(false, getQueryRuleParams()); + ruleAlert.throttle = '1h'; + const payLoadWithThrottle = getPayload( + ruleAlert, + alertServices + ) as jest.Mocked; + payLoadWithThrottle.rule.throttle = '1h'; + alertServices.savedObjectsClient.get.mockResolvedValue({ + id: 'id', + type: 'type', + references: [], + attributes: ruleAlert, + }); + await alert.executor(payLoadWithThrottle); expect(scheduleThrottledNotificationActions).toHaveBeenCalledTimes(1); }); + + it('should NOT call scheduleThrottledNotificationActions if an error was thrown to prevent the throttle from being reset if throttle is not defined', async () => { + const result: SearchAfterAndBulkCreateReturnType = { + success: false, + warning: false, + searchAfterTimes: [], + bulkCreateTimes: [], + lastLookBackDate: null, + createdSignalsCount: 0, + createdSignals: [], + warningMessages: [], + errors: ['Error that bubbled up.'], + }; + (queryExecutor as jest.Mock).mockRejectedValue(result); + await alert.executor(payload); + expect(scheduleThrottledNotificationActions).toHaveBeenCalledTimes(0); + }); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts index 4e98bee83aeb5..90220814fb928 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts @@ -474,21 +474,22 @@ export const signalRulesAlertType = ({ ); } else { // NOTE: Since this is throttled we have to call it even on an error condition, otherwise it will "reset" the throttle and fire early - await scheduleThrottledNotificationActions({ - alertInstance: services.alertInstanceFactory(alertId), - throttle: savedObject.attributes.throttle, - startedAt, - id: savedObject.id, - kibanaSiemAppUrl: (meta as { kibana_siem_app_url?: string } | undefined) - ?.kibana_siem_app_url, - outputIndex, - ruleId, - signals: result.createdSignals, - esClient: services.scopedClusterClient.asCurrentUser, - notificationRuleParams, - logger, - }); - + if (savedObject.attributes.throttle != null) { + await scheduleThrottledNotificationActions({ + alertInstance: services.alertInstanceFactory(alertId), + throttle: savedObject.attributes.throttle, + startedAt, + id: savedObject.id, + kibanaSiemAppUrl: (meta as { kibana_siem_app_url?: string } | undefined) + ?.kibana_siem_app_url, + outputIndex, + ruleId, + signals: result.createdSignals, + esClient: services.scopedClusterClient.asCurrentUser, + notificationRuleParams, + logger, + }); + } const errorMessage = buildRuleMessage( 'Bulk Indexing of signals failed:', truncateMessageList(result.errors).join() @@ -507,20 +508,22 @@ export const signalRulesAlertType = ({ } } catch (error) { // NOTE: Since this is throttled we have to call it even on an error condition, otherwise it will "reset" the throttle and fire early - await scheduleThrottledNotificationActions({ - alertInstance: services.alertInstanceFactory(alertId), - throttle: savedObject.attributes.throttle, - startedAt, - id: savedObject.id, - kibanaSiemAppUrl: (meta as { kibana_siem_app_url?: string } | undefined) - ?.kibana_siem_app_url, - outputIndex, - ruleId, - signals: result.createdSignals, - esClient: services.scopedClusterClient.asCurrentUser, - notificationRuleParams, - logger, - }); + if (savedObject.attributes.throttle != null) { + await scheduleThrottledNotificationActions({ + alertInstance: services.alertInstanceFactory(alertId), + throttle: savedObject.attributes.throttle, + startedAt, + id: savedObject.id, + kibanaSiemAppUrl: (meta as { kibana_siem_app_url?: string } | undefined) + ?.kibana_siem_app_url, + outputIndex, + ruleId, + signals: result.createdSignals, + esClient: services.scopedClusterClient.asCurrentUser, + notificationRuleParams, + logger, + }); + } const errorMessage = error.message ?? '(no error message given)'; const message = buildRuleMessage( 'An error occurred during rule execution:', From e53f4d2f28d127935bd7fe5dfa235a81e30b9460 Mon Sep 17 00:00:00 2001 From: Frank Hassanabad Date: Mon, 18 Oct 2021 22:37:00 -0600 Subject: [PATCH 029/204] [Security Solutions] Makes legacy actions/notification system, legacy action status, and exception lists multiple space shareable (#115427) ## Summary See https://github.com/elastic/kibana/issues/114548 Makes the following saved objects multiple-isolated: * siem-detection-engine-rule-status * exception-list * siem-detection-engine-rule-actions ### Checklist Delete any items that are not applicable to this PR. - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --- x-pack/plugins/lists/server/saved_objects/exception_list.ts | 3 ++- .../rule_actions/legacy_saved_object_mappings.ts | 3 ++- .../legacy_rule_status_saved_object_mappings.ts | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/lists/server/saved_objects/exception_list.ts b/x-pack/plugins/lists/server/saved_objects/exception_list.ts index 8354e64d64a6e..3d31bdd561140 100644 --- a/x-pack/plugins/lists/server/saved_objects/exception_list.ts +++ b/x-pack/plugins/lists/server/saved_objects/exception_list.ts @@ -177,11 +177,12 @@ const combinedMappings: SavedObjectsType['mappings'] = { }; export const exceptionListType: SavedObjectsType = { + convertToMultiNamespaceTypeVersion: '8.0.0', hidden: false, mappings: combinedMappings, migrations, name: exceptionListSavedObjectType, - namespaceType: 'single', + namespaceType: 'multiple-isolated', }; export const exceptionListAgnosticType: SavedObjectsType = { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions/legacy_saved_object_mappings.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions/legacy_saved_object_mappings.ts index 3d6a405225fe6..835ccd92f9cc4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions/legacy_saved_object_mappings.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_actions/legacy_saved_object_mappings.ts @@ -59,9 +59,10 @@ const legacyRuleActionsSavedObjectMappings: SavedObjectsType['mappings'] = { * @deprecated Remove this once we no longer need legacy migrations for rule actions (8.0.0) */ export const legacyType: SavedObjectsType = { + convertToMultiNamespaceTypeVersion: '8.0.0', name: legacyRuleActionsSavedObjectType, hidden: false, - namespaceType: 'single', + namespaceType: 'multiple-isolated', mappings: legacyRuleActionsSavedObjectMappings, migrations: legacyRuleActionsSavedObjectMigration, }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/legacy_rule_status/legacy_rule_status_saved_object_mappings.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/legacy_rule_status/legacy_rule_status_saved_object_mappings.ts index 3fe3fc06cc7d6..298c75b8b7d51 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/legacy_rule_status/legacy_rule_status_saved_object_mappings.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/legacy_rule_status/legacy_rule_status_saved_object_mappings.ts @@ -65,9 +65,10 @@ export const ruleStatusSavedObjectMappings: SavedObjectsType['mappings'] = { * @deprecated Remove this once we've fully migrated to event-log and no longer require addition status SO (8.x) */ export const legacyRuleStatusType: SavedObjectsType = { + convertToMultiNamespaceTypeVersion: '8.0.0', name: legacyRuleStatusSavedObjectType, hidden: false, - namespaceType: 'single', + namespaceType: 'multiple-isolated', mappings: ruleStatusSavedObjectMappings, migrations: legacyRuleStatusSavedObjectMigration, }; From 57ff4a7172bb68067df9b809d592748deeb1114c Mon Sep 17 00:00:00 2001 From: Paul Tavares <56442535+paul-tavares@users.noreply.github.com> Date: Tue, 19 Oct 2021 00:43:12 -0400 Subject: [PATCH 030/204] [Security Solution][Endpoint] Adds additional endpoint privileges to the `useUserPrivileges()` hook (#115051) * Adds new `canIsolateHost` and `canCreateArtifactsByPolicy` privileges for endpoint * Refactor `useEndpointPrivileges` mocks to also provide a test function to return the full set of default privileges * refactor useEndpointPrivileges tests to be more resilient to future changes --- .../__mocks__/use_endpoint_privileges.ts | 18 ---- .../__mocks__/use_endpoint_privileges.ts | 12 +++ .../user_privileges/endpoint/index.ts | 9 ++ .../user_privileges/endpoint/mocks.ts | 29 ++++++ .../use_endpoint_privileges.test.ts | 89 ++++++++++--------- .../{ => endpoint}/use_endpoint_privileges.ts | 25 ++++-- .../user_privileges/endpoint/utils.ts | 19 ++++ .../components/user_privileges/index.tsx | 10 +-- .../components/user_info/index.test.tsx | 2 +- .../alerts/use_alerts_privileges.test.tsx | 6 +- .../alerts/use_signal_index.test.tsx | 2 +- .../search_exceptions.test.tsx | 17 ++-- .../search_exceptions/search_exceptions.tsx | 2 +- .../view/event_filters_list_page.test.tsx | 2 +- .../host_isolation_exceptions_list.test.tsx | 3 +- .../policy_trusted_apps_empty_unassigned.tsx | 2 +- .../policy_trusted_apps_flyout.test.tsx | 2 +- .../policy_trusted_apps_layout.test.tsx | 22 ++--- .../layout/policy_trusted_apps_layout.tsx | 2 +- .../list/policy_trusted_apps_list.test.tsx | 10 +-- .../list/policy_trusted_apps_list.tsx | 2 +- .../view/trusted_apps_page.test.tsx | 2 +- .../public/overview/pages/overview.test.tsx | 2 +- 23 files changed, 170 insertions(+), 119 deletions(-) delete mode 100644 x-pack/plugins/security_solution/public/common/components/user_privileges/__mocks__/use_endpoint_privileges.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/__mocks__/use_endpoint_privileges.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/index.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/mocks.ts rename x-pack/plugins/security_solution/public/common/components/user_privileges/{ => endpoint}/use_endpoint_privileges.test.ts (63%) rename x-pack/plugins/security_solution/public/common/components/user_privileges/{ => endpoint}/use_endpoint_privileges.ts (71%) create mode 100644 x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/utils.ts diff --git a/x-pack/plugins/security_solution/public/common/components/user_privileges/__mocks__/use_endpoint_privileges.ts b/x-pack/plugins/security_solution/public/common/components/user_privileges/__mocks__/use_endpoint_privileges.ts deleted file mode 100644 index 80ca534534187..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/user_privileges/__mocks__/use_endpoint_privileges.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EndpointPrivileges } from '../use_endpoint_privileges'; - -export const useEndpointPrivileges = jest.fn(() => { - const endpointPrivilegesMock: EndpointPrivileges = { - loading: false, - canAccessFleet: true, - canAccessEndpointManagement: true, - isPlatinumPlus: true, - }; - return endpointPrivilegesMock; -}); diff --git a/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/__mocks__/use_endpoint_privileges.ts b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/__mocks__/use_endpoint_privileges.ts new file mode 100644 index 0000000000000..ae9aacaf3d55b --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/__mocks__/use_endpoint_privileges.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getEndpointPrivilegesInitialStateMock } from '../mocks'; + +export { getEndpointPrivilegesInitialState } from '../utils'; + +export const useEndpointPrivileges = jest.fn(getEndpointPrivilegesInitialStateMock); diff --git a/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/index.ts b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/index.ts new file mode 100644 index 0000000000000..adea89ce1a051 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './use_endpoint_privileges'; +export { getEndpointPrivilegesInitialState } from './utils'; diff --git a/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/mocks.ts b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/mocks.ts new file mode 100644 index 0000000000000..2851c92816cea --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/mocks.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { EndpointPrivileges } from './use_endpoint_privileges'; +import { getEndpointPrivilegesInitialState } from './utils'; + +export const getEndpointPrivilegesInitialStateMock = ( + overrides: Partial = {} +): EndpointPrivileges => { + // Get the initial state and set all permissions to `true` (enabled) for testing + const endpointPrivilegesMock: EndpointPrivileges = { + ...( + Object.entries(getEndpointPrivilegesInitialState()) as Array< + [keyof EndpointPrivileges, boolean] + > + ).reduce((mockPrivileges, [key, value]) => { + mockPrivileges[key] = !value; + + return mockPrivileges; + }, {} as EndpointPrivileges), + ...overrides, + }; + + return endpointPrivilegesMock; +}; diff --git a/x-pack/plugins/security_solution/public/common/components/user_privileges/use_endpoint_privileges.test.ts b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/use_endpoint_privileges.test.ts similarity index 63% rename from x-pack/plugins/security_solution/public/common/components/user_privileges/use_endpoint_privileges.test.ts rename to x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/use_endpoint_privileges.test.ts index 82443e913499b..d4ba29a4ef950 100644 --- a/x-pack/plugins/security_solution/public/common/components/user_privileges/use_endpoint_privileges.test.ts +++ b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/use_endpoint_privileges.test.ts @@ -6,16 +6,17 @@ */ import { act, renderHook, RenderHookResult, RenderResult } from '@testing-library/react-hooks'; -import { useHttp, useCurrentUser } from '../../lib/kibana'; +import { useHttp, useCurrentUser } from '../../../lib/kibana'; import { EndpointPrivileges, useEndpointPrivileges } from './use_endpoint_privileges'; -import { securityMock } from '../../../../../security/public/mocks'; -import { appRoutesService } from '../../../../../fleet/common'; -import { AuthenticatedUser } from '../../../../../security/common'; -import { licenseService } from '../../hooks/use_license'; -import { fleetGetCheckPermissionsHttpMock } from '../../../management/pages/mocks'; - -jest.mock('../../lib/kibana'); -jest.mock('../../hooks/use_license', () => { +import { securityMock } from '../../../../../../security/public/mocks'; +import { appRoutesService } from '../../../../../../fleet/common'; +import { AuthenticatedUser } from '../../../../../../security/common'; +import { licenseService } from '../../../hooks/use_license'; +import { fleetGetCheckPermissionsHttpMock } from '../../../../management/pages/mocks'; +import { getEndpointPrivilegesInitialStateMock } from './mocks'; + +jest.mock('../../../lib/kibana'); +jest.mock('../../../hooks/use_license', () => { const licenseServiceInstance = { isPlatinumPlus: jest.fn(), }; @@ -27,6 +28,8 @@ jest.mock('../../hooks/use_license', () => { }; }); +const licenseServiceMock = licenseService as jest.Mocked; + describe('When using useEndpointPrivileges hook', () => { let authenticatedUser: AuthenticatedUser; let fleetApiMock: ReturnType; @@ -45,7 +48,7 @@ describe('When using useEndpointPrivileges hook', () => { fleetApiMock = fleetGetCheckPermissionsHttpMock( useHttp() as Parameters[0] ); - (licenseService.isPlatinumPlus as jest.Mock).mockReturnValue(true); + licenseServiceMock.isPlatinumPlus.mockReturnValue(true); render = () => { const hookRenderResponse = renderHook(() => useEndpointPrivileges()); @@ -69,34 +72,31 @@ describe('When using useEndpointPrivileges hook', () => { (useCurrentUser as jest.Mock).mockReturnValue(null); const { rerender } = render(); - expect(result.current).toEqual({ - canAccessEndpointManagement: false, - canAccessFleet: false, - loading: true, - isPlatinumPlus: true, - }); + expect(result.current).toEqual( + getEndpointPrivilegesInitialStateMock({ + canAccessEndpointManagement: false, + canAccessFleet: false, + loading: true, + }) + ); // Make user service available (useCurrentUser as jest.Mock).mockReturnValue(authenticatedUser); rerender(); - expect(result.current).toEqual({ - canAccessEndpointManagement: false, - canAccessFleet: false, - loading: true, - isPlatinumPlus: true, - }); + expect(result.current).toEqual( + getEndpointPrivilegesInitialStateMock({ + canAccessEndpointManagement: false, + canAccessFleet: false, + loading: true, + }) + ); // Release the API response await act(async () => { fleetApiMock.waitForApi(); releaseApiResponse!(); }); - expect(result.current).toEqual({ - canAccessEndpointManagement: true, - canAccessFleet: true, - loading: false, - isPlatinumPlus: true, - }); + expect(result.current).toEqual(getEndpointPrivilegesInitialStateMock()); }); it('should call Fleet permissions api to determine user privilege to fleet', async () => { @@ -113,12 +113,11 @@ describe('When using useEndpointPrivileges hook', () => { render(); await waitForNextUpdate(); await fleetApiMock.waitForApi(); - expect(result.current).toEqual({ - canAccessEndpointManagement: false, - canAccessFleet: true, // this is only true here because I did not adjust the API mock - loading: false, - isPlatinumPlus: true, - }); + expect(result.current).toEqual( + getEndpointPrivilegesInitialStateMock({ + canAccessEndpointManagement: false, + }) + ); }); it('should set privileges to false if fleet api check returns failure', async () => { @@ -130,11 +129,21 @@ describe('When using useEndpointPrivileges hook', () => { render(); await waitForNextUpdate(); await fleetApiMock.waitForApi(); - expect(result.current).toEqual({ - canAccessEndpointManagement: false, - canAccessFleet: false, - loading: false, - isPlatinumPlus: true, - }); + expect(result.current).toEqual( + getEndpointPrivilegesInitialStateMock({ + canAccessEndpointManagement: false, + canAccessFleet: false, + }) + ); }); + + it.each([['canIsolateHost'], ['canCreateArtifactsByPolicy']])( + 'should set %s to false if license is not PlatinumPlus', + async (privilege) => { + licenseServiceMock.isPlatinumPlus.mockReturnValue(false); + render(); + await waitForNextUpdate(); + expect(result.current).toEqual(expect.objectContaining({ [privilege]: false })); + } + ); }); diff --git a/x-pack/plugins/security_solution/public/common/components/user_privileges/use_endpoint_privileges.ts b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/use_endpoint_privileges.ts similarity index 71% rename from x-pack/plugins/security_solution/public/common/components/user_privileges/use_endpoint_privileges.ts rename to x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/use_endpoint_privileges.ts index 315935104d107..36f02d22487fc 100644 --- a/x-pack/plugins/security_solution/public/common/components/user_privileges/use_endpoint_privileges.ts +++ b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/use_endpoint_privileges.ts @@ -6,9 +6,9 @@ */ import { useEffect, useMemo, useRef, useState } from 'react'; -import { useCurrentUser, useHttp } from '../../lib/kibana'; -import { appRoutesService, CheckPermissionsResponse } from '../../../../../fleet/common'; -import { useLicense } from '../../hooks/use_license'; +import { useCurrentUser, useHttp } from '../../../lib/kibana'; +import { appRoutesService, CheckPermissionsResponse } from '../../../../../../fleet/common'; +import { useLicense } from '../../../hooks/use_license'; export interface EndpointPrivileges { loading: boolean; @@ -16,6 +16,11 @@ export interface EndpointPrivileges { canAccessFleet: boolean; /** If user has permissions to access Endpoint management (includes check to ensure they also have access to fleet) */ canAccessEndpointManagement: boolean; + /** if user has permissions to create Artifacts by Policy */ + canCreateArtifactsByPolicy: boolean; + /** If user has permissions to use the Host isolation feature */ + canIsolateHost: boolean; + /** @deprecated do not use. instead, use one of the other privileges defined */ isPlatinumPlus: boolean; } @@ -29,7 +34,7 @@ export const useEndpointPrivileges = (): EndpointPrivileges => { const http = useHttp(); const user = useCurrentUser(); const isMounted = useRef(true); - const license = useLicense(); + const isPlatinumPlusLicense = useLicense().isPlatinumPlus(); const [canAccessFleet, setCanAccessFleet] = useState(false); const [fleetCheckDone, setFleetCheckDone] = useState(false); @@ -61,13 +66,19 @@ export const useEndpointPrivileges = (): EndpointPrivileges => { }, [user?.roles]); const privileges = useMemo(() => { - return { + const privilegeList: EndpointPrivileges = { loading: !fleetCheckDone || !user, canAccessFleet, canAccessEndpointManagement: canAccessFleet && isSuperUser, - isPlatinumPlus: license.isPlatinumPlus(), + canCreateArtifactsByPolicy: isPlatinumPlusLicense, + canIsolateHost: isPlatinumPlusLicense, + // FIXME: Remove usages of the property below + /** @deprecated */ + isPlatinumPlus: isPlatinumPlusLicense, }; - }, [canAccessFleet, fleetCheckDone, isSuperUser, user, license]); + + return privilegeList; + }, [canAccessFleet, fleetCheckDone, isSuperUser, user, isPlatinumPlusLicense]); // Capture if component is unmounted useEffect( diff --git a/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/utils.ts b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/utils.ts new file mode 100644 index 0000000000000..df91314479f18 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/user_privileges/endpoint/utils.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EndpointPrivileges } from './use_endpoint_privileges'; + +export const getEndpointPrivilegesInitialState = (): EndpointPrivileges => { + return { + loading: true, + canAccessFleet: false, + canAccessEndpointManagement: false, + canIsolateHost: false, + canCreateArtifactsByPolicy: false, + isPlatinumPlus: false, + }; +}; diff --git a/x-pack/plugins/security_solution/public/common/components/user_privileges/index.tsx b/x-pack/plugins/security_solution/public/common/components/user_privileges/index.tsx index 437d27278102b..bc0640296b33d 100644 --- a/x-pack/plugins/security_solution/public/common/components/user_privileges/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/user_privileges/index.tsx @@ -11,9 +11,10 @@ import { DeepReadonly } from 'utility-types'; import { Capabilities } from '../../../../../../../src/core/public'; import { useFetchDetectionEnginePrivileges } from '../../../detections/components/user_privileges/use_fetch_detection_engine_privileges'; import { useFetchListPrivileges } from '../../../detections/components/user_privileges/use_fetch_list_privileges'; -import { EndpointPrivileges, useEndpointPrivileges } from './use_endpoint_privileges'; +import { EndpointPrivileges, useEndpointPrivileges } from './endpoint'; import { SERVER_APP_ID } from '../../../../common/constants'; +import { getEndpointPrivilegesInitialState } from './endpoint/utils'; export interface UserPrivilegesState { listPrivileges: ReturnType; detectionEnginePrivileges: ReturnType; @@ -24,12 +25,7 @@ export interface UserPrivilegesState { export const initialUserPrivilegesState = (): UserPrivilegesState => ({ listPrivileges: { loading: false, error: undefined, result: undefined }, detectionEnginePrivileges: { loading: false, error: undefined, result: undefined }, - endpointPrivileges: { - loading: true, - canAccessEndpointManagement: false, - canAccessFleet: false, - isPlatinumPlus: false, - }, + endpointPrivileges: getEndpointPrivilegesInitialState(), kibanaSecuritySolutionsPrivileges: { crud: false, read: false }, }); diff --git a/x-pack/plugins/security_solution/public/detections/components/user_info/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/user_info/index.test.tsx index 3d95dca81165e..1a8588017e4d6 100644 --- a/x-pack/plugins/security_solution/public/detections/components/user_info/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/user_info/index.test.tsx @@ -17,7 +17,7 @@ import { UserPrivilegesProvider } from '../../../common/components/user_privileg jest.mock('../../../common/lib/kibana'); jest.mock('../../containers/detection_engine/alerts/api'); -jest.mock('../../../common/components/user_privileges/use_endpoint_privileges'); +jest.mock('../../../common/components/user_privileges/endpoint/use_endpoint_privileges'); describe('useUserInfo', () => { beforeAll(() => { diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_alerts_privileges.test.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_alerts_privileges.test.tsx index 40894c1d01929..1dc1423606097 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_alerts_privileges.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_alerts_privileges.test.tsx @@ -12,6 +12,7 @@ import { useAppToastsMock } from '../../../../common/hooks/use_app_toasts.mock'; import { useUserPrivileges } from '../../../../common/components/user_privileges'; import { Privilege } from './types'; import { UseAlertsPrivelegesReturn, useAlertsPrivileges } from './use_alerts_privileges'; +import { getEndpointPrivilegesInitialStateMock } from '../../../../common/components/user_privileges/endpoint/mocks'; jest.mock('./api'); jest.mock('../../../../common/hooks/use_app_toasts'); @@ -86,12 +87,11 @@ const userPrivilegesInitial: ReturnType = { result: undefined, error: undefined, }, - endpointPrivileges: { + endpointPrivileges: getEndpointPrivilegesInitialStateMock({ loading: true, canAccessEndpointManagement: false, canAccessFleet: false, - isPlatinumPlus: true, - }, + }), kibanaSecuritySolutionsPrivileges: { crud: true, read: true }, }; diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_signal_index.test.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_signal_index.test.tsx index ade83fed4fd6b..ad4ad5062c9d5 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_signal_index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_signal_index.test.tsx @@ -13,7 +13,7 @@ import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; jest.mock('./api'); jest.mock('../../../../common/hooks/use_app_toasts'); -jest.mock('../../../../common/components/user_privileges/use_endpoint_privileges'); +jest.mock('../../../../common/components/user_privileges/endpoint/use_endpoint_privileges'); describe('useSignalIndex', () => { let appToastsMock: jest.Mocked>; diff --git a/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.test.tsx b/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.test.tsx index 084978d35d03a..3b987a7211411 100644 --- a/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.test.tsx @@ -11,11 +11,12 @@ import { AppContextTestRender, createAppRootMockRenderer } from '../../../common import { EndpointPrivileges, useEndpointPrivileges, -} from '../../../common/components/user_privileges/use_endpoint_privileges'; +} from '../../../common/components/user_privileges/endpoint/use_endpoint_privileges'; import { EndpointDocGenerator } from '../../../../common/endpoint/generate_data'; import { SearchExceptions, SearchExceptionsProps } from '.'; -jest.mock('../../../common/components/user_privileges/use_endpoint_privileges'); +import { getEndpointPrivilegesInitialStateMock } from '../../../common/components/user_privileges/endpoint/mocks'; +jest.mock('../../../common/components/user_privileges/endpoint/use_endpoint_privileges'); let onSearchMock: jest.Mock; const mockUseEndpointPrivileges = useEndpointPrivileges as jest.Mock; @@ -29,13 +30,11 @@ describe('Search exceptions', () => { const loadedUserEndpointPrivilegesState = ( endpointOverrides: Partial = {} - ): EndpointPrivileges => ({ - loading: false, - canAccessFleet: true, - canAccessEndpointManagement: true, - isPlatinumPlus: false, - ...endpointOverrides, - }); + ): EndpointPrivileges => + getEndpointPrivilegesInitialStateMock({ + isPlatinumPlus: false, + ...endpointOverrides, + }); beforeEach(() => { onSearchMock = jest.fn(); diff --git a/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.tsx b/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.tsx index 1f3eab5db2947..569916ac20315 100644 --- a/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.tsx +++ b/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.tsx @@ -10,7 +10,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiFieldSearch, EuiButton } from '@elastic/e import { i18n } from '@kbn/i18n'; import { PolicySelectionItem, PoliciesSelector } from '../policies_selector'; import { ImmutableArray, PolicyData } from '../../../../common/endpoint/types'; -import { useEndpointPrivileges } from '../../../common/components/user_privileges/use_endpoint_privileges'; +import { useEndpointPrivileges } from '../../../common/components/user_privileges/endpoint/use_endpoint_privileges'; export interface SearchExceptionsProps { defaultValue?: string; diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.test.tsx b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.test.tsx index 0729f95bb44a9..02efce1ab59e8 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.test.tsx @@ -14,7 +14,7 @@ import { isFailedResourceState, isLoadedResourceState } from '../../../state'; // Needed to mock the data services used by the ExceptionItem component jest.mock('../../../../common/lib/kibana'); -jest.mock('../../../../common/components/user_privileges/use_endpoint_privileges'); +jest.mock('../../../../common/components/user_privileges/endpoint/use_endpoint_privileges'); describe('When on the Event Filters List Page', () => { let render: () => ReturnType; diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/host_isolation_exceptions_list.test.tsx b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/host_isolation_exceptions_list.test.tsx index 5113457e5bccc..625da11a3644e 100644 --- a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/host_isolation_exceptions_list.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/host_isolation_exceptions_list.test.tsx @@ -16,8 +16,7 @@ import { getHostIsolationExceptionItems } from '../service'; import { HostIsolationExceptionsList } from './host_isolation_exceptions_list'; import { useLicense } from '../../../../common/hooks/use_license'; -jest.mock('../../../../common/components/user_privileges/use_endpoint_privileges'); - +jest.mock('../../../../common/components/user_privileges/endpoint/use_endpoint_privileges'); jest.mock('../service'); jest.mock('../../../../common/hooks/use_license'); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/empty/policy_trusted_apps_empty_unassigned.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/empty/policy_trusted_apps_empty_unassigned.tsx index ee52e1210a481..c12bec03ada04 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/empty/policy_trusted_apps_empty_unassigned.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/empty/policy_trusted_apps_empty_unassigned.tsx @@ -10,7 +10,7 @@ import { EuiEmptyPrompt, EuiButton, EuiPageTemplate, EuiLink } from '@elastic/eu import { FormattedMessage } from '@kbn/i18n/react'; import { usePolicyDetailsNavigateCallback } from '../../policy_hooks'; import { useGetLinkTo } from './use_policy_trusted_apps_empty_hooks'; -import { useEndpointPrivileges } from '../../../../../../common/components/user_privileges/use_endpoint_privileges'; +import { useEndpointPrivileges } from '../../../../../../common/components/user_privileges/endpoint/use_endpoint_privileges'; interface CommonProps { policyId: string; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/flyout/policy_trusted_apps_flyout.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/flyout/policy_trusted_apps_flyout.test.tsx index c1d00f7a3f99b..8e412d2020b72 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/flyout/policy_trusted_apps_flyout.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/flyout/policy_trusted_apps_flyout.test.tsx @@ -21,7 +21,7 @@ import { createLoadedResourceState, isLoadedResourceState } from '../../../../.. import { getPolicyDetailsArtifactsListPath } from '../../../../../common/routing'; jest.mock('../../../../trusted_apps/service'); -jest.mock('../../../../../../common/components/user_privileges/use_endpoint_privileges'); +jest.mock('../../../../../../common/components/user_privileges/endpoint/use_endpoint_privileges'); let mockedContext: AppContextTestRender; let waitForAction: MiddlewareActionSpyHelper['waitForAction']; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/layout/policy_trusted_apps_layout.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/layout/policy_trusted_apps_layout.test.tsx index 43e19c00bcc8e..dbb18a1b0f2ef 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/layout/policy_trusted_apps_layout.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/layout/policy_trusted_apps_layout.test.tsx @@ -19,13 +19,11 @@ import { createLoadedResourceState, isLoadedResourceState } from '../../../../.. import { getPolicyDetailsArtifactsListPath } from '../../../../../common/routing'; import { EndpointDocGenerator } from '../../../../../../../common/endpoint/generate_data'; import { policyListApiPathHandlers } from '../../../store/test_mock_utils'; -import { - EndpointPrivileges, - useEndpointPrivileges, -} from '../../../../../../common/components/user_privileges/use_endpoint_privileges'; +import { useEndpointPrivileges } from '../../../../../../common/components/user_privileges/endpoint/use_endpoint_privileges'; +import { getEndpointPrivilegesInitialStateMock } from '../../../../../../common/components/user_privileges/endpoint/mocks'; jest.mock('../../../../trusted_apps/service'); -jest.mock('../../../../../../common/components/user_privileges/use_endpoint_privileges'); +jest.mock('../../../../../../common/components/user_privileges/endpoint/use_endpoint_privileges'); const mockUseEndpointPrivileges = useEndpointPrivileges as jest.Mock; let mockedContext: AppContextTestRender; @@ -37,16 +35,6 @@ let http: typeof coreStart.http; const generator = new EndpointDocGenerator(); describe('Policy trusted apps layout', () => { - const loadedUserEndpointPrivilegesState = ( - endpointOverrides: Partial = {} - ): EndpointPrivileges => ({ - loading: false, - canAccessFleet: true, - canAccessEndpointManagement: true, - isPlatinumPlus: true, - ...endpointOverrides, - }); - beforeEach(() => { mockedContext = createAppRootMockRenderer(); http = mockedContext.coreStart.http; @@ -137,7 +125,7 @@ describe('Policy trusted apps layout', () => { it('should hide assign button on empty state with unassigned policies when downgraded to a gold or below license', async () => { mockUseEndpointPrivileges.mockReturnValue( - loadedUserEndpointPrivilegesState({ + getEndpointPrivilegesInitialStateMock({ isPlatinumPlus: false, }) ); @@ -155,7 +143,7 @@ describe('Policy trusted apps layout', () => { it('should hide the `Assign trusted applications` button when there is data and the license is downgraded to gold or below', async () => { mockUseEndpointPrivileges.mockReturnValue( - loadedUserEndpointPrivilegesState({ + getEndpointPrivilegesInitialStateMock({ isPlatinumPlus: false, }) ); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/layout/policy_trusted_apps_layout.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/layout/policy_trusted_apps_layout.tsx index a3f1ed215286a..49f76ad2e02c6 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/layout/policy_trusted_apps_layout.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/layout/policy_trusted_apps_layout.tsx @@ -30,7 +30,7 @@ import { import { usePolicyDetailsNavigateCallback, usePolicyDetailsSelector } from '../../policy_hooks'; import { PolicyTrustedAppsFlyout } from '../flyout'; import { PolicyTrustedAppsList } from '../list/policy_trusted_apps_list'; -import { useEndpointPrivileges } from '../../../../../../common/components/user_privileges/use_endpoint_privileges'; +import { useEndpointPrivileges } from '../../../../../../common/components/user_privileges/endpoint/use_endpoint_privileges'; import { useAppUrl } from '../../../../../../common/lib/kibana'; import { APP_ID } from '../../../../../../../common/constants'; import { getTrustedAppsListPath } from '../../../../../common/routing'; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.test.tsx index a8d3cc1505463..e18d3c01791c0 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.test.tsx @@ -24,9 +24,10 @@ import { APP_ID } from '../../../../../../../common/constants'; import { EndpointPrivileges, useEndpointPrivileges, -} from '../../../../../../common/components/user_privileges/use_endpoint_privileges'; +} from '../../../../../../common/components/user_privileges/endpoint/use_endpoint_privileges'; +import { getEndpointPrivilegesInitialStateMock } from '../../../../../../common/components/user_privileges/endpoint/mocks'; -jest.mock('../../../../../../common/components/user_privileges/use_endpoint_privileges'); +jest.mock('../../../../../../common/components/user_privileges/endpoint/use_endpoint_privileges'); const mockUseEndpointPrivileges = useEndpointPrivileges as jest.Mock; describe('when rendering the PolicyTrustedAppsList', () => { @@ -43,10 +44,7 @@ describe('when rendering the PolicyTrustedAppsList', () => { const loadedUserEndpointPrivilegesState = ( endpointOverrides: Partial = {} ): EndpointPrivileges => ({ - loading: false, - canAccessFleet: true, - canAccessEndpointManagement: true, - isPlatinumPlus: true, + ...getEndpointPrivilegesInitialStateMock(), ...endpointOverrides, }); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.tsx index f6afd9d502486..def0f490b7fee 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.tsx @@ -38,7 +38,7 @@ import { ContextMenuItemNavByRouterProps } from '../../../../../components/conte import { ArtifactEntryCollapsibleCardProps } from '../../../../../components/artifact_entry_card'; import { useTestIdGenerator } from '../../../../../components/hooks/use_test_id_generator'; import { RemoveTrustedAppFromPolicyModal } from './remove_trusted_app_from_policy_modal'; -import { useEndpointPrivileges } from '../../../../../../common/components/user_privileges/use_endpoint_privileges'; +import { useEndpointPrivileges } from '../../../../../../common/components/user_privileges/endpoint/use_endpoint_privileges'; const DATA_TEST_SUBJ = 'policyTrustedAppsGrid'; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx index f39fd47c78771..b4366a8922927 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx @@ -52,7 +52,7 @@ jest.mock('../../../../common/hooks/use_license', () => { }; }); -jest.mock('../../../../common/components/user_privileges/use_endpoint_privileges'); +jest.mock('../../../../common/components/user_privileges/endpoint/use_endpoint_privileges'); describe('When on the Trusted Apps Page', () => { const expectedAboutInfo = diff --git a/x-pack/plugins/security_solution/public/overview/pages/overview.test.tsx b/x-pack/plugins/security_solution/public/overview/pages/overview.test.tsx index cab02450f8886..ab5ae4f613e38 100644 --- a/x-pack/plugins/security_solution/public/overview/pages/overview.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/pages/overview.test.tsx @@ -30,7 +30,7 @@ import { mockCtiLinksResponse, } from '../components/overview_cti_links/mock'; import { useCtiDashboardLinks } from '../containers/overview_cti_links'; -import { EndpointPrivileges } from '../../common/components/user_privileges/use_endpoint_privileges'; +import { EndpointPrivileges } from '../../common/components/user_privileges/endpoint/use_endpoint_privileges'; import { useIsExperimentalFeatureEnabled } from '../../common/hooks/use_experimental_features'; import { useHostsRiskScore } from '../containers/overview_risky_host_links/use_hosts_risk_score'; From 730df8852f63bc6b652cca4f31dd56ea4e2addba Mon Sep 17 00:00:00 2001 From: "Joey F. Poon" Date: Tue, 19 Oct 2021 00:09:21 -0500 Subject: [PATCH 031/204] [Security Solution] fix endpoint list agent status logic (#115286) --- .../server/endpoint/routes/metadata/handlers.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/handlers.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/handlers.ts index 027107bcf1a59..e98cdc4f11404 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/handlers.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/handlers.ts @@ -48,6 +48,7 @@ import { } from './support/query_strategies'; import { NotFoundError } from '../../errors'; import { EndpointHostUnEnrolledError } from '../../services/metadata'; +import { getAgentStatus } from '../../../../../fleet/common/services/agent_status'; export interface MetadataRequestContext { esClient?: IScopedClusterClient; @@ -522,10 +523,11 @@ async function queryUnitedIndex( const agentPolicy = agentPoliciesMap[agent.policy_id!]; // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const endpointPolicy = endpointPoliciesMap[agent.policy_id!]; + const fleetAgentStatus = getAgentStatus(agent as Agent); + return { metadata, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - host_status: fleetAgentStatusToEndpointHostStatus(agent.last_checkin_status!), + host_status: fleetAgentStatusToEndpointHostStatus(fleetAgentStatus), policy_info: { agent: { applied: { From cb17ec78969b11922993d70959cfa32574b3ca81 Mon Sep 17 00:00:00 2001 From: Dario Gieselaar Date: Tue, 19 Oct 2021 09:22:29 +0200 Subject: [PATCH 032/204] [APM] Add readme for @elastic/apm-generator (#115368) --- packages/elastic-apm-generator/README.md | 93 ++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/packages/elastic-apm-generator/README.md b/packages/elastic-apm-generator/README.md index e69de29bb2d1d..e43187a8155d3 100644 --- a/packages/elastic-apm-generator/README.md +++ b/packages/elastic-apm-generator/README.md @@ -0,0 +1,93 @@ +# @elastic/apm-generator + +`@elastic/apm-generator` is an experimental tool to generate synthetic APM data. It is intended to be used for development and testing of the Elastic APM app in Kibana. + +At a high-level, the module works by modeling APM events/metricsets with [a fluent API](https://en.wikipedia.org/wiki/Fluent_interface). The models can then be serialized and converted to Elasticsearch documents. In the future we might support APM Server as an output as well. + +## Usage + +This section assumes that you've installed Kibana's dependencies by running `yarn kbn bootstrap` in the repository's root folder. + +This library can currently be used in two ways: + +- Imported as a Node.js module, for instance to be used in Kibana's functional test suite. +- With a command line interface, to index data based on some example scenarios. + +### Using the Node.js module + +#### Concepts + +- `Service`: a logical grouping for a monitored service. A `Service` object contains fields like `service.name`, `service.environment` and `agent.name`. +- `Instance`: a single instance of a monitored service. E.g., the workload for a monitored service might be spread across multiple containers. An `Instance` object contains fields like `service.node.name` and `container.id`. +- `Timerange`: an object that will return an array of timestamps based on an interval and a rate. These timestamps can be used to generate events/metricsets. +- `Transaction`, `Span`, `APMError` and `Metricset`: events/metricsets that occur on an instance. For more background, see the [explanation of the APM data model](https://www.elastic.co/guide/en/apm/get-started/7.15/apm-data-model.html) + + +#### Example + +```ts +import { service, timerange, toElasticsearchOutput } from '@elastic/apm-generator'; + +const instance = service('synth-go', 'production', 'go') + .instance('instance-a'); + +const from = new Date('2021-01-01T12:00:00.000Z').getTime(); +const to = new Date('2021-01-01T12:00:00.000Z').getTime() - 1; + +const traceEvents = timerange(from, to) + .interval('1m') + .rate(10) + .flatMap(timestamp => instance.transaction('GET /api/product/list') + .timestamp(timestamp) + .duration(1000) + .success() + .children( + instance.span('GET apm-*/_search', 'db', 'elasticsearch') + .timestamp(timestamp + 50) + .duration(900) + .destination('elasticsearch') + .success() + ).serialize() + ); + +const metricsets = timerange(from, to) + .interval('30s') + .rate(1) + .flatMap(timestamp => instance.appMetrics({ + 'system.memory.actual.free': 800, + 'system.memory.total': 1000, + 'system.cpu.total.norm.pct': 0.6, + 'system.process.cpu.total.norm.pct': 0.7, + }).timestamp(timestamp) + .serialize() + ); + +const esEvents = toElasticsearchOutput(traceEvents.concat(metricsets)); +``` + +#### Generating metricsets + +`@elastic/apm-generator` can also automatically generate transaction metrics, span destination metrics and transaction breakdown metrics based on the generated trace events. If we expand on the previous example: + +```ts +import { getTransactionMetrics, getSpanDestinationMetrics, getBreakdownMetrics } from '@elastic/apm-generator'; + +const esEvents = toElasticsearchOutput([ + ...traceEvents, + ...getTransactionMetrics(traceEvents), + ...getSpanDestinationMetrics(traceEvents), + ...getBreakdownMetrics(traceEvents) +]); +``` + +### CLI + +Via the CLI, you can upload examples. The supported examples are listed in `src/lib/es.ts`. A `--target` option that specifies the Elasticsearch URL should be defined when running the `example` command. Here's an example: + +`$ node packages/elastic-apm-generator/src/scripts/es.js example simple-trace --target=http://admin:changeme@localhost:9200` + +The following options are supported: +- `to`: the end of the time range, in ISO format. By default, the current time will be used. +- `from`: the start of the time range, in ISO format. By default, `to` minus 15 minutes will be used. +- `apm-server-version`: the version used in the index names bootstrapped by APM Server, e.g. `7.16.0`. __If these indices do not exist, the script will exit with an error. It will not bootstrap the indices itself.__ + From fd4b85b1c687b0a4d837b2715bedbdfce9cba77f Mon Sep 17 00:00:00 2001 From: Pete Hampton Date: Tue, 19 Oct 2021 08:34:34 +0100 Subject: [PATCH 033/204] Update heading on telemetry management section. (#115425) --- .../telemetry_management_section.test.tsx.snap | 4 ++-- .../public/components/telemetry_management_section.tsx | 8 ++++---- x-pack/plugins/translations/translations/ja-JP.json | 2 -- x-pack/plugins/translations/translations/zh-CN.json | 2 -- 4 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/plugins/telemetry_management_section/public/components/__snapshots__/telemetry_management_section.test.tsx.snap b/src/plugins/telemetry_management_section/public/components/__snapshots__/telemetry_management_section.test.tsx.snap index 758ecf54f4bf0..72947b1514911 100644 --- a/src/plugins/telemetry_management_section/public/components/__snapshots__/telemetry_management_section.test.tsx.snap +++ b/src/plugins/telemetry_management_section/public/components/__snapshots__/telemetry_management_section.test.tsx.snap @@ -53,7 +53,7 @@ exports[`TelemetryManagementSectionComponent renders as expected 1`] = ` loading={false} setting={ Object { - "ariaName": "Provide usage statistics", + "ariaName": "Provide usage data", "category": Array [], "defVal": true, "description": @@ -109,7 +109,7 @@ exports[`TelemetryManagementSectionComponent renders as expected 1`] = ` />

, - "displayName": "Provide usage statistics", + "displayName": "Provide usage data", "isCustom": true, "isOverridden": false, "name": "telemetry:enabled", diff --git a/src/plugins/telemetry_management_section/public/components/telemetry_management_section.tsx b/src/plugins/telemetry_management_section/public/components/telemetry_management_section.tsx index 3686cb10706bf..037603cb165d9 100644 --- a/src/plugins/telemetry_management_section/public/components/telemetry_management_section.tsx +++ b/src/plugins/telemetry_management_section/public/components/telemetry_management_section.tsx @@ -116,14 +116,14 @@ export class TelemetryManagementSection extends Component { setting={{ type: 'boolean', name: 'telemetry:enabled', - displayName: i18n.translate('telemetry.provideUsageStatisticsTitle', { - defaultMessage: 'Provide usage statistics', + displayName: i18n.translate('telemetry.provideUsageDataTitle', { + defaultMessage: 'Provide usage data', }), value: enabled, description: this.renderDescription(), defVal: true, - ariaName: i18n.translate('telemetry.provideUsageStatisticsAriaName', { - defaultMessage: 'Provide usage statistics', + ariaName: i18n.translate('telemetry.provideUsageDataAriaName', { + defaultMessage: 'Provide usage data', }), requiresPageReload: false, category: [], diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 852b01977b78b..acf9daebaf552 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -4508,8 +4508,6 @@ "telemetry.optInNoticeSeenErrorToastText": "通知の消去中にエラーが発生しました", "telemetry.optInSuccessOff": "使用状況データ収集がオフです。", "telemetry.optInSuccessOn": "使用状況データ収集がオンです。", - "telemetry.provideUsageStatisticsAriaName": "使用統計を提供", - "telemetry.provideUsageStatisticsTitle": "使用統計を提供", "telemetry.readOurUsageDataPrivacyStatementLinkText": "プライバシーポリシー", "telemetry.securityData": "Endpoint Security データ", "telemetry.telemetryBannerDescription": "Elastic Stackの改善にご協力ください使用状況データの収集は現在無効です。使用状況データの収集を有効にすると、製品とサービスを管理して改善することができます。詳細については、{privacyStatementLink}をご覧ください。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 9d88c757f1e58..5890f8553e7c5 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -4553,8 +4553,6 @@ "telemetry.optInNoticeSeenErrorToastText": "关闭声明时发生错误", "telemetry.optInSuccessOff": "使用情况数据收集已关闭。", "telemetry.optInSuccessOn": "使用情况数据收集已打开。", - "telemetry.provideUsageStatisticsAriaName": "提供使用情况统计", - "telemetry.provideUsageStatisticsTitle": "提供使用情况统计", "telemetry.readOurUsageDataPrivacyStatementLinkText": "隐私声明", "telemetry.securityData": "终端安全数据", "telemetry.telemetryBannerDescription": "想帮助我们改进 Elastic Stack?数据使用情况收集当前已禁用。启用使用情况数据收集可帮助我们管理并改善产品和服务。有关更多详情,请参阅我们的{privacyStatementLink}。", From 533e5d8d393c20551f811d06632f19a44bcd25ab Mon Sep 17 00:00:00 2001 From: Paul Tavares <56442535+paul-tavares@users.noreply.github.com> Date: Tue, 19 Oct 2021 03:36:46 -0400 Subject: [PATCH 034/204] [Security Solution][Endpoint] Change Trusted Apps to use `item_id` as its identifier and Enable Trusted Apps filtering by id in the UI (#115276) * Add `item_id` to list of searchable fields * trusted apps api changes to use `item_id` instead of SO `id` * Change Policy Details Trusted App "View all details" action URL to show TA list filtered by the TA id --- .../list/policy_trusted_apps_list.test.tsx | 2 +- .../list/policy_trusted_apps_list.tsx | 2 +- .../pages/trusted_apps/constants.ts | 1 + .../routes/trusted_apps/handlers.test.ts | 5 +- .../endpoint/routes/trusted_apps/mapping.ts | 2 +- .../routes/trusted_apps/service.test.ts | 24 +++++- .../endpoint/routes/trusted_apps/service.ts | 77 +++++++++++++------ 7 files changed, 84 insertions(+), 29 deletions(-) diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.test.tsx index e18d3c01791c0..9165aec3bef8d 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.test.tsx @@ -205,7 +205,7 @@ describe('when rendering the PolicyTrustedAppsList', () => { expect(appTestContext.coreStart.application.navigateToApp).toHaveBeenCalledWith( APP_ID, expect.objectContaining({ - path: '/administration/trusted_apps?show=edit&id=89f72d8a-05b5-4350-8cad-0dc3661d6e67', + path: '/administration/trusted_apps?filter=89f72d8a-05b5-4350-8cad-0dc3661d6e67', }) ); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.tsx index def0f490b7fee..89ff6bd099be4 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/policy_trusted_apps_list.tsx @@ -113,7 +113,7 @@ export const PolicyTrustedAppsList = memo( for (const trustedApp of trustedAppItems) { const isGlobal = trustedApp.effectScope.type === 'global'; - const viewUrlPath = getTrustedAppsListPath({ id: trustedApp.id, show: 'edit' }); + const viewUrlPath = getTrustedAppsListPath({ filter: trustedApp.id }); const assignedPoliciesMenuItems: ArtifactEntryCollapsibleCardProps['policies'] = trustedApp.effectScope.type === 'global' ? undefined diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/constants.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/constants.ts index 0602ae18c1408..beefb8587d787 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/constants.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/constants.ts @@ -8,6 +8,7 @@ export const SEARCHABLE_FIELDS: Readonly = [ `name`, `description`, + 'item_id', `entries.value`, `entries.entries.value`, ]; diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/handlers.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/handlers.test.ts index 547c1f6a2e5ff..614ad4fb548ea 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/handlers.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/handlers.test.ts @@ -110,7 +110,7 @@ const Gold = licenseMock.createLicense({ license: { type: 'gold', mode: 'gold' } const packagePolicyClient = createPackagePolicyServiceMock() as jest.Mocked; -describe('handlers', () => { +describe('TrustedApps API Handlers', () => { beforeEach(() => { packagePolicyClient.getByIDs.mockReset(); }); @@ -195,6 +195,7 @@ describe('handlers', () => { const mockResponse = httpServerMock.createResponseFactory(); exceptionsListClient.deleteExceptionListItem.mockResolvedValue(null); + exceptionsListClient.getExceptionListItem.mockResolvedValue(null); await deleteTrustedAppHandler( createHandlerContextMock(), @@ -582,7 +583,7 @@ describe('handlers', () => { }); it('should return 404 if trusted app does not exist', async () => { - exceptionsListClient.getExceptionListItem.mockResolvedValueOnce(null); + exceptionsListClient.getExceptionListItem.mockResolvedValue(null); await updateHandler( createHandlerContextMock(), diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/mapping.ts b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/mapping.ts index 2c085c14db009..08c1a3a809d4a 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/mapping.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/mapping.ts @@ -122,7 +122,7 @@ export const exceptionListItemToTrustedApp = ( const grouped = entriesToConditionEntriesMap(exceptionListItem.entries); return { - id: exceptionListItem.id, + id: exceptionListItem.item_id, version: exceptionListItem._version || '', name: exceptionListItem.name, description: exceptionListItem.description, diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/service.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/service.test.ts index dce84df735929..c57416ff1c974 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/service.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/service.test.ts @@ -85,9 +85,10 @@ const TRUSTED_APP: TrustedApp = { ], }; -describe('service', () => { +describe('TrustedApps service', () => { beforeEach(() => { exceptionsListClient.deleteExceptionListItem.mockReset(); + exceptionsListClient.getExceptionListItem.mockReset(); exceptionsListClient.createExceptionListItem.mockReset(); exceptionsListClient.findExceptionListItem.mockReset(); exceptionsListClient.createTrustedAppsList.mockReset(); @@ -96,6 +97,7 @@ describe('service', () => { describe('deleteTrustedApp', () => { it('should delete existing trusted app', async () => { + exceptionsListClient.getExceptionListItem.mockResolvedValue(EXCEPTION_LIST_ITEM); exceptionsListClient.deleteExceptionListItem.mockResolvedValue(EXCEPTION_LIST_ITEM); expect(await deleteTrustedApp(exceptionsListClient, { id: '123' })).toBeUndefined(); @@ -107,6 +109,7 @@ describe('service', () => { }); it('should throw for non existing trusted app', async () => { + exceptionsListClient.getExceptionListItem.mockResolvedValue(null); exceptionsListClient.deleteExceptionListItem.mockResolvedValue(null); await expect(deleteTrustedApp(exceptionsListClient, { id: '123' })).rejects.toBeInstanceOf( @@ -393,7 +396,7 @@ describe('service', () => { }); it('should throw a Not Found error if trusted app is not found prior to making update', async () => { - exceptionsListClient.getExceptionListItem.mockResolvedValueOnce(null); + exceptionsListClient.getExceptionListItem.mockResolvedValue(null); await expect( updateTrustedApp( exceptionsListClient, @@ -489,5 +492,22 @@ describe('service', () => { TrustedAppNotFoundError ); }); + + it('should try to find trusted app by `itemId` and then by `id`', async () => { + exceptionsListClient.getExceptionListItem.mockResolvedValue(null); + await getTrustedApp(exceptionsListClient, '123').catch(() => Promise.resolve()); + + expect(exceptionsListClient.getExceptionListItem).toHaveBeenCalledTimes(2); + expect(exceptionsListClient.getExceptionListItem).toHaveBeenNthCalledWith(1, { + itemId: '123', + id: undefined, + namespaceType: 'agnostic', + }); + expect(exceptionsListClient.getExceptionListItem).toHaveBeenNthCalledWith(2, { + itemId: undefined, + id: '123', + namespaceType: 'agnostic', + }); + }); }); }); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/service.ts b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/service.ts index 856a615c1ffa2..7a4b2372ece8f 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/service.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/service.ts @@ -15,13 +15,13 @@ import { DeleteTrustedAppsRequestParams, GetOneTrustedAppResponse, GetTrustedAppsListRequest, - GetTrustedAppsSummaryResponse, GetTrustedAppsListResponse, + GetTrustedAppsSummaryRequest, + GetTrustedAppsSummaryResponse, PostTrustedAppCreateRequest, PostTrustedAppCreateResponse, PutTrustedAppUpdateRequest, PutTrustedAppUpdateResponse, - GetTrustedAppsSummaryRequest, TrustedApp, } from '../../../../common/endpoint/types'; @@ -33,8 +33,8 @@ import { } from './mapping'; import { TrustedAppNotFoundError, - TrustedAppVersionConflictError, TrustedAppPolicyNotExistsError, + TrustedAppVersionConflictError, } from './errors'; import { PackagePolicyServiceInterface } from '../../../../../fleet/server'; import { PackagePolicy } from '../../../../../fleet/common'; @@ -87,30 +87,61 @@ const isUserTryingToModifyEffectScopeWithoutPermissions = ( } }; -export const deleteTrustedApp = async ( +/** + * Attempts to first fine the ExceptionItem using `item_id` and if not found, then a second attempt wil be done + * against the Saved Object `id`. + * @param exceptionsListClient + * @param id + */ +export const findTrustedAppExceptionItemByIdOrItemId = async ( exceptionsListClient: ExceptionListClient, - { id }: DeleteTrustedAppsRequestParams -) => { - const exceptionListItem = await exceptionsListClient.deleteExceptionListItem({ - id, + id: string +): Promise => { + const trustedAppExceptionItem = await exceptionsListClient.getExceptionListItem({ + itemId: id, + id: undefined, + namespaceType: 'agnostic', + }); + + if (trustedAppExceptionItem) { + return trustedAppExceptionItem; + } + + return exceptionsListClient.getExceptionListItem({ itemId: undefined, + id, namespaceType: 'agnostic', }); +}; - if (!exceptionListItem) { +export const deleteTrustedApp = async ( + exceptionsListClient: ExceptionListClient, + { id }: DeleteTrustedAppsRequestParams +): Promise => { + const trustedAppExceptionItem = await findTrustedAppExceptionItemByIdOrItemId( + exceptionsListClient, + id + ); + + if (!trustedAppExceptionItem) { throw new TrustedAppNotFoundError(id); } + + await exceptionsListClient.deleteExceptionListItem({ + id: trustedAppExceptionItem.id, + itemId: undefined, + namespaceType: 'agnostic', + }); }; export const getTrustedApp = async ( exceptionsListClient: ExceptionListClient, id: string ): Promise => { - const trustedAppExceptionItem = await exceptionsListClient.getExceptionListItem({ - itemId: '', - id, - namespaceType: 'agnostic', - }); + const trustedAppExceptionItem = await findTrustedAppExceptionItemByIdOrItemId( + exceptionsListClient, + id + ); if (!trustedAppExceptionItem) { throw new TrustedAppNotFoundError(id); @@ -189,19 +220,18 @@ export const updateTrustedApp = async ( updatedTrustedApp: PutTrustedAppUpdateRequest, isAtLeastPlatinum: boolean ): Promise => { - const currentTrustedApp = await exceptionsListClient.getExceptionListItem({ - itemId: '', - id, - namespaceType: 'agnostic', - }); + const currentTrustedAppExceptionItem = await findTrustedAppExceptionItemByIdOrItemId( + exceptionsListClient, + id + ); - if (!currentTrustedApp) { + if (!currentTrustedAppExceptionItem) { throw new TrustedAppNotFoundError(id); } if ( isUserTryingToModifyEffectScopeWithoutPermissions( - exceptionListItemToTrustedApp(currentTrustedApp), + exceptionListItemToTrustedApp(currentTrustedAppExceptionItem), updatedTrustedApp, isAtLeastPlatinum ) @@ -226,7 +256,10 @@ export const updateTrustedApp = async ( try { updatedTrustedAppExceptionItem = await exceptionsListClient.updateExceptionListItem( - updatedTrustedAppToUpdateExceptionListItemOptions(currentTrustedApp, updatedTrustedApp) + updatedTrustedAppToUpdateExceptionListItemOptions( + currentTrustedAppExceptionItem, + updatedTrustedApp + ) ); } catch (e) { if (e?.output?.statusCode === 409) { From bfe648d49606135aa52814932d7611893496c447 Mon Sep 17 00:00:00 2001 From: Pablo Machado Date: Tue, 19 Oct 2021 09:37:01 +0200 Subject: [PATCH 035/204] Fix alerts Count table title overflow wraps prematurely (#115364) --- .../components/alerts_kpis/alerts_count_panel/index.tsx | 2 +- .../components/alerts_kpis/alerts_histogram_panel/index.tsx | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/index.tsx index 29324d186784e..c8d45ca67068a 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/index.tsx @@ -94,7 +94,7 @@ export const AlertsCountPanel = memo( {i18n.COUNT_TABLE_TITLE}
} titleSize="s" hideSubtitle > diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.tsx index 0613c619d89b9..07fa81f27684c 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.tsx @@ -257,7 +257,11 @@ export const AlertsHistogramPanel = memo( }, [showLinkToAlerts, goToDetectionEngine, formatUrl]); const titleText = useMemo( - () => (onlyField == null ? title : i18n.TOP(onlyField)), + () => ( + + {onlyField == null ? title : i18n.TOP(onlyField)} + + ), [onlyField, title] ); From db834698974ea40b452316d0ca3d37a1541bed64 Mon Sep 17 00:00:00 2001 From: Esteban Beltran Date: Tue, 19 Oct 2021 10:00:49 +0200 Subject: [PATCH 036/204] [Security Solution] Generate host isolation exceptions artifact (#115160) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../server/endpoint/lib/artifacts/common.ts | 3 + .../server/endpoint/lib/artifacts/lists.ts | 20 +++ .../manifest_manager/manifest_manager.test.ts | 137 ++++++++++-------- .../manifest_manager/manifest_manager.ts | 42 ++++++ 4 files changed, 140 insertions(+), 62 deletions(-) diff --git a/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/common.ts b/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/common.ts index af5e386464305..60f91330d4558 100644 --- a/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/common.ts +++ b/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/common.ts @@ -25,6 +25,9 @@ export const ArtifactConstants = { SUPPORTED_EVENT_FILTERS_OPERATING_SYSTEMS: ['macos', 'windows', 'linux'], GLOBAL_EVENT_FILTERS_NAME: 'endpoint-eventfilterlist', + + SUPPORTED_HOST_ISOLATION_EXCEPTIONS_OPERATING_SYSTEMS: ['macos', 'windows', 'linux'], + GLOBAL_HOST_ISOLATION_EXCEPTIONS_NAME: 'endpoint-hostisolationexceptionlist', }; export const ManifestConstants = { diff --git a/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/lists.ts b/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/lists.ts index e27a09efd9710..e26a2c7f4b4bc 100644 --- a/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/lists.ts +++ b/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/lists.ts @@ -15,6 +15,7 @@ import { validate } from '@kbn/securitysolution-io-ts-utils'; import { ENDPOINT_EVENT_FILTERS_LIST_ID, + ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID, ENDPOINT_LIST_ID, ENDPOINT_TRUSTED_APPS_LIST_ID, } from '@kbn/securitysolution-list-constants'; @@ -65,6 +66,7 @@ export async function getFilteredEndpointExceptionList( | typeof ENDPOINT_LIST_ID | typeof ENDPOINT_TRUSTED_APPS_LIST_ID | typeof ENDPOINT_EVENT_FILTERS_LIST_ID + | typeof ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID ): Promise { const exceptions: WrappedTranslatedExceptionList = { entries: [] }; let page = 1; @@ -148,6 +150,24 @@ export async function getEndpointEventFiltersList( ); } +export async function getHostIsolationExceptionsList( + eClient: ExceptionListClient, + schemaVersion: string, + os: string, + policyId?: string +): Promise { + const osFilter = `exception-list-agnostic.attributes.os_types:\"${os}\"`; + const policyFilter = `(exception-list-agnostic.attributes.tags:\"policy:all\"${ + policyId ? ` or exception-list-agnostic.attributes.tags:\"policy:${policyId}\"` : '' + })`; + + return getFilteredEndpointExceptionList( + eClient, + schemaVersion, + `${osFilter} and ${policyFilter}`, + ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID + ); +} /** * Translates Exception list items to Exceptions the endpoint can understand * @param exceptions diff --git a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.test.ts b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.test.ts index d75e347b86bd5..0ef2abd5f50aa 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.test.ts @@ -7,6 +7,7 @@ import { savedObjectsClientMock } from 'src/core/server/mocks'; import { + ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID, ENDPOINT_LIST_ID, ENDPOINT_TRUSTED_APPS_LIST_ID, } from '@kbn/securitysolution-list-constants'; @@ -66,6 +67,12 @@ describe('ManifestManager', () => { const ARTIFACT_NAME_EVENT_FILTERS_MACOS = 'endpoint-eventfilterlist-macos-v1'; const ARTIFACT_NAME_EVENT_FILTERS_WINDOWS = 'endpoint-eventfilterlist-windows-v1'; const ARTIFACT_NAME_EVENT_FILTERS_LINUX = 'endpoint-eventfilterlist-linux-v1'; + const ARTIFACT_NAME_HOST_ISOLATION_EXCEPTIONS_MACOS = + 'endpoint-hostisolationexceptionlist-macos-v1'; + const ARTIFACT_NAME_HOST_ISOLATION_EXCEPTIONS_WINDOWS = + 'endpoint-hostisolationexceptionlist-windows-v1'; + const ARTIFACT_NAME_HOST_ISOLATION_EXCEPTIONS_LINUX = + 'endpoint-hostisolationexceptionlist-linux-v1'; let ARTIFACTS: InternalArtifactCompleteSchema[] = []; let ARTIFACTS_BY_ID: { [K: string]: InternalArtifactCompleteSchema } = {}; @@ -157,31 +164,29 @@ describe('ManifestManager', () => { const manifestManagerContext = buildManifestManagerContextMock({ savedObjectsClient }); const manifestManager = new ManifestManager(manifestManagerContext); - savedObjectsClient.get = jest - .fn() - .mockImplementation(async (objectType: string, id: string) => { - if (objectType === ManifestConstants.SAVED_OBJECT_TYPE) { - return { - attributes: { - created: '20-01-2020 10:00:00.000Z', - schemaVersion: 'v2', - semanticVersion: '1.0.0', - artifacts: [ - { artifactId: ARTIFACT_ID_EXCEPTIONS_MACOS, policyId: undefined }, - { artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: undefined }, - { artifactId: ARTIFACT_ID_EXCEPTIONS_LINUX, policyId: undefined }, - { artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: TEST_POLICY_ID_1 }, - { artifactId: ARTIFACT_ID_TRUSTED_APPS_MACOS, policyId: TEST_POLICY_ID_1 }, - { artifactId: ARTIFACT_ID_TRUSTED_APPS_WINDOWS, policyId: TEST_POLICY_ID_1 }, - { artifactId: ARTIFACT_ID_TRUSTED_APPS_WINDOWS, policyId: TEST_POLICY_ID_2 }, - ], - }, - version: '2.0.0', - }; - } else { - return null; - } - }); + savedObjectsClient.get = jest.fn().mockImplementation(async (objectType: string) => { + if (objectType === ManifestConstants.SAVED_OBJECT_TYPE) { + return { + attributes: { + created: '20-01-2020 10:00:00.000Z', + schemaVersion: 'v2', + semanticVersion: '1.0.0', + artifacts: [ + { artifactId: ARTIFACT_ID_EXCEPTIONS_MACOS, policyId: undefined }, + { artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: undefined }, + { artifactId: ARTIFACT_ID_EXCEPTIONS_LINUX, policyId: undefined }, + { artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: TEST_POLICY_ID_1 }, + { artifactId: ARTIFACT_ID_TRUSTED_APPS_MACOS, policyId: TEST_POLICY_ID_1 }, + { artifactId: ARTIFACT_ID_TRUSTED_APPS_WINDOWS, policyId: TEST_POLICY_ID_1 }, + { artifactId: ARTIFACT_ID_TRUSTED_APPS_WINDOWS, policyId: TEST_POLICY_ID_2 }, + ], + }, + version: '2.0.0', + }; + } else { + return null; + } + }); ( manifestManagerContext.artifactClient as jest.Mocked @@ -218,31 +223,29 @@ describe('ManifestManager', () => { const manifestManagerContext = buildManifestManagerContextMock({ savedObjectsClient }); const manifestManager = new ManifestManager(manifestManagerContext); - savedObjectsClient.get = jest - .fn() - .mockImplementation(async (objectType: string, id: string) => { - if (objectType === ManifestConstants.SAVED_OBJECT_TYPE) { - return { - attributes: { - created: '20-01-2020 10:00:00.000Z', - schemaVersion: 'v2', - semanticVersion: '1.0.0', - artifacts: [ - { artifactId: ARTIFACT_ID_EXCEPTIONS_MACOS, policyId: undefined }, - { artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: undefined }, - { artifactId: ARTIFACT_ID_EXCEPTIONS_LINUX, policyId: undefined }, - { artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: TEST_POLICY_ID_1 }, - { artifactId: ARTIFACT_ID_TRUSTED_APPS_MACOS, policyId: TEST_POLICY_ID_1 }, - { artifactId: ARTIFACT_ID_TRUSTED_APPS_WINDOWS, policyId: TEST_POLICY_ID_1 }, - { artifactId: ARTIFACT_ID_TRUSTED_APPS_WINDOWS, policyId: TEST_POLICY_ID_2 }, - ], - }, - version: '2.0.0', - }; - } else { - return null; - } - }); + savedObjectsClient.get = jest.fn().mockImplementation(async (objectType: string) => { + if (objectType === ManifestConstants.SAVED_OBJECT_TYPE) { + return { + attributes: { + created: '20-01-2020 10:00:00.000Z', + schemaVersion: 'v2', + semanticVersion: '1.0.0', + artifacts: [ + { artifactId: ARTIFACT_ID_EXCEPTIONS_MACOS, policyId: undefined }, + { artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: undefined }, + { artifactId: ARTIFACT_ID_EXCEPTIONS_LINUX, policyId: undefined }, + { artifactId: ARTIFACT_ID_EXCEPTIONS_WINDOWS, policyId: TEST_POLICY_ID_1 }, + { artifactId: ARTIFACT_ID_TRUSTED_APPS_MACOS, policyId: TEST_POLICY_ID_1 }, + { artifactId: ARTIFACT_ID_TRUSTED_APPS_WINDOWS, policyId: TEST_POLICY_ID_1 }, + { artifactId: ARTIFACT_ID_TRUSTED_APPS_WINDOWS, policyId: TEST_POLICY_ID_2 }, + ], + }, + version: '2.0.0', + }; + } else { + return null; + } + }); ( manifestManagerContext.artifactClient as jest.Mocked @@ -278,6 +281,9 @@ describe('ManifestManager', () => { ARTIFACT_NAME_EVENT_FILTERS_MACOS, ARTIFACT_NAME_EVENT_FILTERS_WINDOWS, ARTIFACT_NAME_EVENT_FILTERS_LINUX, + ARTIFACT_NAME_HOST_ISOLATION_EXCEPTIONS_MACOS, + ARTIFACT_NAME_HOST_ISOLATION_EXCEPTIONS_WINDOWS, + ARTIFACT_NAME_HOST_ISOLATION_EXCEPTIONS_LINUX, ]; const getArtifactIds = (artifacts: InternalArtifactSchema[]) => [ @@ -310,7 +316,7 @@ describe('ManifestManager', () => { context.savedObjectsClient.create = jest .fn() - .mockImplementation((type: string, object: InternalManifestSchema) => ({ + .mockImplementation((_type: string, object: InternalManifestSchema) => ({ attributes: object, })); const manifest = await manifestManager.buildNewManifest(); @@ -321,7 +327,7 @@ describe('ManifestManager', () => { const artifacts = manifest.getAllArtifacts(); - expect(artifacts.length).toBe(9); + expect(artifacts.length).toBe(12); expect(getArtifactIds(artifacts)).toStrictEqual(SUPPORTED_ARTIFACT_NAMES); for (const artifact of artifacts) { @@ -336,16 +342,18 @@ describe('ManifestManager', () => { test('Builds fully new manifest if no baseline parameter passed and present exception list items', async () => { const exceptionListItem = getExceptionListItemSchemaMock({ os_types: ['macos'] }); const trustedAppListItem = getExceptionListItemSchemaMock({ os_types: ['linux'] }); + const hostIsolationExceptionsItem = getExceptionListItemSchemaMock({ os_types: ['linux'] }); const context = buildManifestManagerContextMock({}); const manifestManager = new ManifestManager(context); context.exceptionListClient.findExceptionListItem = mockFindExceptionListItemResponses({ [ENDPOINT_LIST_ID]: { macos: [exceptionListItem] }, [ENDPOINT_TRUSTED_APPS_LIST_ID]: { linux: [trustedAppListItem] }, + [ENDPOINT_HOST_ISOLATION_EXCEPTIONS_LIST_ID]: { linux: [hostIsolationExceptionsItem] }, }); context.savedObjectsClient.create = jest .fn() - .mockImplementation((type: string, object: InternalManifestSchema) => ({ + .mockImplementation((_type: string, object: InternalManifestSchema) => ({ attributes: object, })); context.packagePolicyService.listIds = mockPolicyListIdsResponse([TEST_POLICY_ID_1]); @@ -358,7 +366,7 @@ describe('ManifestManager', () => { const artifacts = manifest.getAllArtifacts(); - expect(artifacts.length).toBe(9); + expect(artifacts.length).toBe(12); expect(getArtifactIds(artifacts)).toStrictEqual(SUPPORTED_ARTIFACT_NAMES); expect(getArtifactObject(artifacts[0])).toStrictEqual({ @@ -374,6 +382,11 @@ describe('ManifestManager', () => { expect(getArtifactObject(artifacts[6])).toStrictEqual({ entries: [] }); expect(getArtifactObject(artifacts[7])).toStrictEqual({ entries: [] }); expect(getArtifactObject(artifacts[8])).toStrictEqual({ entries: [] }); + expect(getArtifactObject(artifacts[9])).toStrictEqual({ entries: [] }); + expect(getArtifactObject(artifacts[10])).toStrictEqual({ entries: [] }); + expect(getArtifactObject(artifacts[11])).toStrictEqual({ + entries: translateToEndpointExceptions([hostIsolationExceptionsItem], 'v1'), + }); for (const artifact of artifacts) { expect(manifest.isDefaultArtifact(artifact)).toBe(true); @@ -395,7 +408,7 @@ describe('ManifestManager', () => { context.packagePolicyService.listIds = mockPolicyListIdsResponse([TEST_POLICY_ID_1]); context.savedObjectsClient.create = jest .fn() - .mockImplementation((type: string, object: InternalManifestSchema) => ({ + .mockImplementation((_type: string, object: InternalManifestSchema) => ({ attributes: object, })); const oldManifest = await manifestManager.buildNewManifest(); @@ -413,7 +426,7 @@ describe('ManifestManager', () => { const artifacts = manifest.getAllArtifacts(); - expect(artifacts.length).toBe(9); + expect(artifacts.length).toBe(12); expect(getArtifactIds(artifacts)).toStrictEqual(SUPPORTED_ARTIFACT_NAMES); expect(artifacts[0]).toStrictEqual(oldManifest.getAllArtifacts()[0]); @@ -462,7 +475,7 @@ describe('ManifestManager', () => { context.savedObjectsClient.create = jest .fn() - .mockImplementation((type: string, object: InternalManifestSchema) => ({ + .mockImplementation((_type: string, object: InternalManifestSchema) => ({ attributes: object, })); @@ -474,7 +487,7 @@ describe('ManifestManager', () => { const artifacts = manifest.getAllArtifacts(); - expect(artifacts.length).toBe(10); + expect(artifacts.length).toBe(13); expect(getArtifactIds(artifacts)).toStrictEqual(SUPPORTED_ARTIFACT_NAMES); expect(getArtifactObject(artifacts[0])).toStrictEqual({ @@ -653,7 +666,7 @@ describe('ManifestManager', () => { context.savedObjectsClient.create = jest .fn() - .mockImplementation((type: string, object: InternalManifestSchema) => object); + .mockImplementation((_type: string, object: InternalManifestSchema) => object); await expect(manifestManager.commit(manifest)).resolves.toBeUndefined(); @@ -690,7 +703,7 @@ describe('ManifestManager', () => { context.savedObjectsClient.update = jest .fn() - .mockImplementation((type: string, id: string, object: InternalManifestSchema) => object); + .mockImplementation((_type: string, _id: string, object: InternalManifestSchema) => object); await expect(manifestManager.commit(manifest)).resolves.toBeUndefined(); @@ -1023,7 +1036,7 @@ describe('ManifestManager', () => { context.savedObjectsClient.create = jest .fn() - .mockImplementation((type: string, object: InternalManifestSchema) => ({ + .mockImplementation((_type: string, object: InternalManifestSchema) => ({ attributes: object, })); const manifest = await manifestManager.buildNewManifest(); @@ -1046,7 +1059,7 @@ describe('ManifestManager', () => { context.savedObjectsClient.create = jest .fn() - .mockImplementation((type: string, object: InternalManifestSchema) => ({ + .mockImplementation((_type: string, object: InternalManifestSchema) => ({ attributes: object, })); diff --git a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts index 5c1d327b1b892..736bf1c58cb90 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/artifacts/manifest_manager/manifest_manager.ts @@ -26,6 +26,7 @@ import { getEndpointEventFiltersList, getEndpointExceptionList, getEndpointTrustedAppsList, + getHostIsolationExceptionsList, Manifest, } from '../../../lib/artifacts'; import { @@ -237,6 +238,46 @@ export class ManifestManager { ); } + protected async buildHostIsolationExceptionsArtifacts(): Promise { + const defaultArtifacts: InternalArtifactCompleteSchema[] = []; + const policySpecificArtifacts: Record = {}; + + for (const os of ArtifactConstants.SUPPORTED_HOST_ISOLATION_EXCEPTIONS_OPERATING_SYSTEMS) { + defaultArtifacts.push(await this.buildHostIsolationExceptionForOs(os)); + } + + await iterateAllListItems( + (page) => this.listEndpointPolicyIds(page), + async (policyId) => { + for (const os of ArtifactConstants.SUPPORTED_HOST_ISOLATION_EXCEPTIONS_OPERATING_SYSTEMS) { + policySpecificArtifacts[policyId] = policySpecificArtifacts[policyId] || []; + policySpecificArtifacts[policyId].push( + await this.buildHostIsolationExceptionForOs(os, policyId) + ); + } + } + ); + + return { defaultArtifacts, policySpecificArtifacts }; + } + + protected async buildHostIsolationExceptionForOs( + os: string, + policyId?: string + ): Promise { + return buildArtifact( + await getHostIsolationExceptionsList( + this.exceptionListClient, + this.schemaVersion, + os, + policyId + ), + this.schemaVersion, + os, + ArtifactConstants.GLOBAL_HOST_ISOLATION_EXCEPTIONS_NAME + ); + } + /** * Writes new artifact SO. * @@ -381,6 +422,7 @@ export class ManifestManager { this.buildExceptionListArtifacts(), this.buildTrustedAppsArtifacts(), this.buildEventFiltersArtifacts(), + this.buildHostIsolationExceptionsArtifacts(), ]); const manifest = new Manifest({ From 4d2f76974b9fd88c6fadd446024dc192528da29f Mon Sep 17 00:00:00 2001 From: Dmitry Tomashevich <39378793+Dmitriynj@users.noreply.github.com> Date: Tue, 19 Oct 2021 11:16:06 +0300 Subject: [PATCH 037/204] [Discover] Enable description for saved search modal (#114257) * [Discover] enable description for saved search * [Discover] remove i18n translations for removed description * [Discover] apply Tim's suggestion * [Discover] update snapshot * [Discover] reorder top nav buttons in tests * [Description] fix description save action Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../components/top_nav/discover_topnav.test.tsx | 2 +- .../components/top_nav/get_top_nav_links.test.ts | 16 +++++++++------- .../main/components/top_nav/get_top_nav_links.ts | 4 +++- .../main/components/top_nav/on_save_search.tsx | 10 +++++----- .../embeddable/saved_search_embeddable.tsx | 4 ++++ .../plugins/translations/translations/ja-JP.json | 1 - .../plugins/translations/translations/zh-CN.json | 1 - 7 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/plugins/discover/public/application/apps/main/components/top_nav/discover_topnav.test.tsx b/src/plugins/discover/public/application/apps/main/components/top_nav/discover_topnav.test.tsx index 4b572f6e348b8..808346b53304c 100644 --- a/src/plugins/discover/public/application/apps/main/components/top_nav/discover_topnav.test.tsx +++ b/src/plugins/discover/public/application/apps/main/components/top_nav/discover_topnav.test.tsx @@ -42,7 +42,7 @@ describe('Discover topnav component', () => { const props = getProps(true); const component = shallowWithIntl(); const topMenuConfig = component.props().config.map((obj: TopNavMenuData) => obj.id); - expect(topMenuConfig).toEqual(['options', 'new', 'save', 'open', 'share', 'inspect']); + expect(topMenuConfig).toEqual(['options', 'new', 'open', 'share', 'inspect', 'save']); }); test('generated config of TopNavMenu config is correct when no discover save permissions are assigned', () => { diff --git a/src/plugins/discover/public/application/apps/main/components/top_nav/get_top_nav_links.test.ts b/src/plugins/discover/public/application/apps/main/components/top_nav/get_top_nav_links.test.ts index d31ac6e0f2fea..20c5b9bae332d 100644 --- a/src/plugins/discover/public/application/apps/main/components/top_nav/get_top_nav_links.test.ts +++ b/src/plugins/discover/public/application/apps/main/components/top_nav/get_top_nav_links.test.ts @@ -53,13 +53,6 @@ test('getTopNavLinks result', () => { "run": [Function], "testId": "discoverNewButton", }, - Object { - "description": "Save Search", - "id": "save", - "label": "Save", - "run": [Function], - "testId": "discoverSaveButton", - }, Object { "description": "Open Saved Search", "id": "open", @@ -81,6 +74,15 @@ test('getTopNavLinks result', () => { "run": [Function], "testId": "openInspectorButton", }, + Object { + "description": "Save Search", + "emphasize": true, + "iconType": "save", + "id": "save", + "label": "Save", + "run": [Function], + "testId": "discoverSaveButton", + }, ] `); }); diff --git a/src/plugins/discover/public/application/apps/main/components/top_nav/get_top_nav_links.ts b/src/plugins/discover/public/application/apps/main/components/top_nav/get_top_nav_links.ts index 81be662470306..44d2999947f41 100644 --- a/src/plugins/discover/public/application/apps/main/components/top_nav/get_top_nav_links.ts +++ b/src/plugins/discover/public/application/apps/main/components/top_nav/get_top_nav_links.ts @@ -76,6 +76,8 @@ export const getTopNavLinks = ({ defaultMessage: 'Save Search', }), testId: 'discoverSaveButton', + iconType: 'save', + emphasize: true, run: () => onSaveSearch({ savedSearch, services, indexPattern, navigateTo, state }), }; @@ -153,9 +155,9 @@ export const getTopNavLinks = ({ return [ ...(services.capabilities.advancedSettings.save ? [options] : []), newSearch, - ...(services.capabilities.discover.save ? [saveSearch] : []), openSearch, shareSearch, inspectSearch, + ...(services.capabilities.discover.save ? [saveSearch] : []), ]; }; diff --git a/src/plugins/discover/public/application/apps/main/components/top_nav/on_save_search.tsx b/src/plugins/discover/public/application/apps/main/components/top_nav/on_save_search.tsx index 18766b5df7f33..25b04e12c650a 100644 --- a/src/plugins/discover/public/application/apps/main/components/top_nav/on_save_search.tsx +++ b/src/plugins/discover/public/application/apps/main/components/top_nav/on_save_search.tsx @@ -98,16 +98,19 @@ export async function onSaveSearch({ const onSave = async ({ newTitle, newCopyOnSave, + newDescription, isTitleDuplicateConfirmed, onTitleDuplicate, }: { newTitle: string; newCopyOnSave: boolean; + newDescription: string; isTitleDuplicateConfirmed: boolean; onTitleDuplicate: () => void; }) => { const currentTitle = savedSearch.title; savedSearch.title = newTitle; + savedSearch.description = newDescription; const saveOptions: SaveSavedSearchOptions = { onTitleDuplicate, copyOnSave: newCopyOnSave, @@ -136,14 +139,11 @@ export async function onSaveSearch({ onClose={() => {}} title={savedSearch.title ?? ''} showCopyOnSave={!!savedSearch.id} + description={savedSearch.description} objectType={i18n.translate('discover.localMenu.saveSaveSearchObjectType', { defaultMessage: 'search', })} - description={i18n.translate('discover.localMenu.saveSaveSearchDescription', { - defaultMessage: - 'Save your Discover search so you can use it in visualizations and dashboards', - })} - showDescription={false} + showDescription={true} /> ); showSaveModal(saveModal, services.core.i18n.Context); diff --git a/src/plugins/discover/public/application/embeddable/saved_search_embeddable.tsx b/src/plugins/discover/public/application/embeddable/saved_search_embeddable.tsx index 8849806cf5959..89c47559d7b4c 100644 --- a/src/plugins/discover/public/application/embeddable/saved_search_embeddable.tsx +++ b/src/plugins/discover/public/application/embeddable/saved_search_embeddable.tsx @@ -402,6 +402,10 @@ export class SavedSearchEmbeddable return this.inspectorAdapters; } + public getDescription() { + return this.savedSearch.description; + } + public destroy() { super.destroy(); if (this.searchProps) { diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index acf9daebaf552..59a8cdaab6413 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -2528,7 +2528,6 @@ "discover.localMenu.openSavedSearchDescription": "保存された検索を開きます", "discover.localMenu.openTitle": "開く", "discover.localMenu.optionsDescription": "オプション", - "discover.localMenu.saveSaveSearchDescription": "ビジュアライゼーションとダッシュボードで使用できるように Discover の検索を保存します", "discover.localMenu.saveSaveSearchObjectType": "検索", "discover.localMenu.saveSearchDescription": "検索を保存します", "discover.localMenu.saveTitle": "保存", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 5890f8553e7c5..4e901f51d54da 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -2554,7 +2554,6 @@ "discover.localMenu.openSavedSearchDescription": "打开已保存搜索", "discover.localMenu.openTitle": "打开", "discover.localMenu.optionsDescription": "选项", - "discover.localMenu.saveSaveSearchDescription": "保存您的 Discover 搜索,以便可以在可视化和仪表板中使用该搜索", "discover.localMenu.saveSaveSearchObjectType": "搜索", "discover.localMenu.saveSearchDescription": "保存搜索", "discover.localMenu.saveTitle": "保存", From 32e1fc2396588b168650bd76ebd91127bc158576 Mon Sep 17 00:00:00 2001 From: Ryland Herrick Date: Tue, 19 Oct 2021 03:23:27 -0500 Subject: [PATCH 038/204] [Security Solution][Rules] Halt Indicator Match execution after interval has passed (#115288) * Throw an error to stop execution if IM rule has exceeded its interval * Extract and unit test our timeout validation * Add integration test around timeout behavior Configures a very slow rule to trigger a timeout and assert the corresponding failure. Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../threat_mapping/create_threat_signals.ts | 6 ++- .../signals/threat_mapping/utils.test.ts | 23 ++++++++++ .../signals/threat_mapping/utils.ts | 21 +++++++++ .../tests/create_threat_matching.ts | 46 +++++++++++++++++++ 4 files changed, 95 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signals.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signals.ts index 169a820392a6e..677a2028acdf7 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signals.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signals.ts @@ -11,7 +11,7 @@ import { getThreatList, getThreatListCount } from './get_threat_list'; import { CreateThreatSignalsOptions } from './types'; import { createThreatSignal } from './create_threat_signal'; import { SearchAfterAndBulkCreateReturnType } from '../types'; -import { combineConcurrentResults } from './utils'; +import { buildExecutionIntervalValidator, combineConcurrentResults } from './utils'; import { buildThreatEnrichment } from './build_threat_enrichment'; export const createThreatSignals = async ({ @@ -46,6 +46,9 @@ export const createThreatSignals = async ({ const params = ruleSO.attributes.params; logger.debug(buildRuleMessage('Indicator matching rule starting')); const perPage = concurrentSearches * itemsPerSearch; + const verifyExecutionCanProceed = buildExecutionIntervalValidator( + ruleSO.attributes.schedule.interval + ); let results: SearchAfterAndBulkCreateReturnType = { success: true, @@ -99,6 +102,7 @@ export const createThreatSignals = async ({ }); while (threatList.hits.hits.length !== 0) { + verifyExecutionCanProceed(); const chunks = chunk(itemsPerSearch, threatList.hits.hits); logger.debug(buildRuleMessage(`${chunks.length} concurrent indicator searches are starting.`)); const concurrentSearchesPerformed = chunks.map>( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/utils.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/utils.test.ts index ec826b44023f6..f029b02127b08 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/utils.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/utils.test.ts @@ -10,6 +10,7 @@ import { sampleSignalHit } from '../__mocks__/es_results'; import { ThreatMatchNamedQuery } from './types'; import { + buildExecutionIntervalValidator, calculateAdditiveMax, calculateMax, calculateMaxLookBack, @@ -712,4 +713,26 @@ describe('utils', () => { }); }); }); + + describe('buildExecutionIntervalValidator', () => { + it('succeeds if the validator is called within the specified interval', () => { + const validator = buildExecutionIntervalValidator('1m'); + expect(() => validator()).not.toThrowError(); + }); + + it('throws an error if the validator is called after the specified interval', async () => { + const validator = buildExecutionIntervalValidator('1s'); + + await new Promise((r) => setTimeout(r, 1001)); + expect(() => validator()).toThrowError( + 'Current rule execution has exceeded its allotted interval (1s) and has been stopped.' + ); + }); + + it('throws an error if the interval cannot be parsed', () => { + expect(() => buildExecutionIntervalValidator('badString')).toThrowError( + 'Unable to parse rule interval (badString); stopping rule execution since allotted duration is undefined' + ); + }); + }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/utils.ts index 4d9fda43f032e..99f6609faec91 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/utils.ts @@ -5,7 +5,10 @@ * 2.0. */ +import moment from 'moment'; + import { SearchAfterAndBulkCreateReturnType, SignalSourceHit } from '../types'; +import { parseInterval } from '../utils'; import { ThreatMatchNamedQuery } from './types'; /** @@ -146,3 +149,21 @@ export const decodeThreatMatchNamedQuery = (encoded: string): ThreatMatchNamedQu export const extractNamedQueries = (hit: SignalSourceHit): ThreatMatchNamedQuery[] => hit.matched_queries?.map((match) => decodeThreatMatchNamedQuery(match)) ?? []; + +export const buildExecutionIntervalValidator: (interval: string) => () => void = (interval) => { + const intervalDuration = parseInterval(interval); + + if (intervalDuration == null) { + throw new Error( + `Unable to parse rule interval (${interval}); stopping rule execution since allotted duration is undefined.` + ); + } + + const executionEnd = moment().add(intervalDuration); + return () => { + if (moment().isAfter(executionEnd)) { + const message = `Current rule execution has exceeded its allotted interval (${interval}) and has been stopped.`; + throw new Error(message); + } + }; +}; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_threat_matching.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_threat_matching.ts index 0aad3c699805a..223529fce54f6 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_threat_matching.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_threat_matching.ts @@ -411,6 +411,52 @@ export default ({ getService }: FtrProviderContext) => { expect(signalsOpen.hits.hits.length).equal(0); }); + describe('timeout behavior', () => { + it('will return an error if a rule execution exceeds the rule interval', async () => { + const rule: CreateRulesSchema = { + description: 'Detecting root and admin users', + name: 'Query with a short interval', + severity: 'high', + index: ['auditbeat-*'], + type: 'threat_match', + risk_score: 55, + language: 'kuery', + rule_id: 'rule-1', + from: '1900-01-01T00:00:00.000Z', + query: '*:*', + threat_query: '*:*', // broad query to take more time + threat_index: ['auditbeat-*'], // We use auditbeat as both the matching index and the threat list for simplicity + threat_mapping: [ + { + entries: [ + { + field: 'host.name', + value: 'host.name', + type: 'mapping', + }, + ], + }, + ], + threat_filters: [], + concurrent_searches: 1, + interval: '1s', // short interval + items_per_search: 1, // iterate only 1 threat item per loop to ensure we're slow + }; + + const { id } = await createRule(supertest, rule); + await waitForRuleSuccessOrStatus(supertest, id, 'failed'); + + const { body } = await supertest + .post(`${DETECTION_ENGINE_RULES_URL}/_find_statuses`) + .set('kbn-xsrf', 'true') + .send({ ids: [id] }) + .expect(200); + expect(body[id].current_status.last_failure_message).to.contain( + 'execution has exceeded its allotted interval' + ); + }); + }); + describe('indicator enrichment', () => { before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/filebeat/threat_intel'); From 1fb28dcc69fe69993675cc198191dd2e91e092ea Mon Sep 17 00:00:00 2001 From: Khristinin Nikita Date: Tue, 19 Oct 2021 10:49:58 +0200 Subject: [PATCH 039/204] IM rule default interval timeout and lookback - 1h (#115185) * Make 1h default value for IM rule interval and lookback time * Fix test name * Move value to cosntants * Update lookback * Change lookback to 5 minutes Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../indicator_match_rule.spec.ts | 19 +++++++++++++++++++ .../cypress/screens/create_new_rule.ts | 6 ++++++ .../rules/step_schedule_rule/index.tsx | 19 +++++++++++++++---- .../detection_engine/rules/create/index.tsx | 1 + 4 files changed, 41 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_rules/indicator_match_rule.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_rules/indicator_match_rule.spec.ts index 871e50821b58c..8735b8d49974c 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_rules/indicator_match_rule.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_rules/indicator_match_rule.spec.ts @@ -102,6 +102,12 @@ import { waitForAlertsToPopulate, waitForTheRuleToBeExecuted, } from '../../tasks/create_new_rule'; +import { + SCHEDULE_INTERVAL_AMOUNT_INPUT, + SCHEDULE_INTERVAL_UNITS_INPUT, + SCHEDULE_LOOKBACK_AMOUNT_INPUT, + SCHEDULE_LOOKBACK_UNITS_INPUT, +} from '../../screens/create_new_rule'; import { goBackToRuleDetails, waitForKibana } from '../../tasks/edit_rule'; import { esArchiverLoad, esArchiverUnload } from '../../tasks/es_archiver'; import { loginAndWaitForPageWithoutDateRange } from '../../tasks/login'; @@ -383,6 +389,19 @@ describe('indicator match', () => { getIndicatorMappingComboField(2).should('not.exist'); }); }); + + describe('Schedule', () => { + it('IM rule has 1h time interval and lookback by default', () => { + selectIndicatorMatchType(); + fillDefineIndicatorMatchRuleAndContinue(getNewThreatIndicatorRule()); + fillAboutRuleAndContinue(getNewThreatIndicatorRule()); + + cy.get(SCHEDULE_INTERVAL_AMOUNT_INPUT).invoke('val').should('eql', '1'); + cy.get(SCHEDULE_INTERVAL_UNITS_INPUT).invoke('val').should('eql', 'h'); + cy.get(SCHEDULE_LOOKBACK_AMOUNT_INPUT).invoke('val').should('eql', '5'); + cy.get(SCHEDULE_LOOKBACK_UNITS_INPUT).invoke('val').should('eql', 'm'); + }); + }); }); describe('Generating signals', () => { diff --git a/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts b/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts index 3510df6186870..aadaa5dfa0d88 100644 --- a/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts +++ b/x-pack/plugins/security_solution/cypress/screens/create_new_rule.ts @@ -201,6 +201,12 @@ export const SCHEDULE_INTERVAL_AMOUNT_INPUT = export const SCHEDULE_INTERVAL_UNITS_INPUT = '[data-test-subj="detectionEngineStepScheduleRuleInterval"] [data-test-subj="timeType"]'; +export const SCHEDULE_LOOKBACK_AMOUNT_INPUT = + '[data-test-subj="detectionEngineStepScheduleRuleFrom"] [data-test-subj="interval"]'; + +export const SCHEDULE_LOOKBACK_UNITS_INPUT = + '[data-test-subj="detectionEngineStepScheduleRuleFrom"] [data-test-subj="timeType"]'; + export const SEVERITY_DROPDOWN = '[data-test-subj="detectionEngineStepAboutRuleSeverity"] [data-test-subj="select"]'; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_schedule_rule/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_schedule_rule/index.tsx index 528494c9331ac..9d7c2b76b385f 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_schedule_rule/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_schedule_rule/index.tsx @@ -6,6 +6,7 @@ */ import React, { FC, memo, useCallback, useEffect } from 'react'; +import { Type } from '@kbn/securitysolution-io-ts-alerting-types'; import { RuleStep, @@ -16,16 +17,25 @@ import { StepRuleDescription } from '../description_step'; import { ScheduleItem } from '../schedule_item_form'; import { Form, UseField, useForm } from '../../../../shared_imports'; import { StepContentWrapper } from '../step_content_wrapper'; +import { isThreatMatchRule } from '../../../../../common/detection_engine/utils'; import { NextStep } from '../next_step'; import { schema } from './schema'; interface StepScheduleRuleProps extends RuleStepProps { defaultValues?: ScheduleStepRule | null; + ruleType?: Type; } -const stepScheduleDefaultValue: ScheduleStepRule = { - interval: '5m', - from: '1m', +const DEFAULT_INTERVAL = '5m'; +const DEFAULT_FROM = '1m'; +const THREAT_MATCH_INTERVAL = '1h'; +const THREAT_MATCH_FROM = '5m'; + +const getStepScheduleDefaultValue = (ruleType: Type | undefined): ScheduleStepRule => { + return { + interval: isThreatMatchRule(ruleType) ? THREAT_MATCH_INTERVAL : DEFAULT_INTERVAL, + from: isThreatMatchRule(ruleType) ? THREAT_MATCH_FROM : DEFAULT_FROM, + }; }; const StepScheduleRuleComponent: FC = ({ @@ -37,8 +47,9 @@ const StepScheduleRuleComponent: FC = ({ isUpdateView = false, onSubmit, setForm, + ruleType, }) => { - const initialState = defaultValues ?? stepScheduleDefaultValue; + const initialState = defaultValues ?? getStepScheduleDefaultValue(ruleType); const { form } = useForm({ defaultValue: initialState, diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/index.tsx index a2f4385aeeb86..d37acaeb0ffee 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/index.tsx @@ -401,6 +401,7 @@ const CreateRulePageComponent: React.FC = () => { > Date: Tue, 19 Oct 2021 11:03:46 +0200 Subject: [PATCH 040/204] [Security Solution][Detections] Hide building block rules in "Security/Overview" (#105611) * Hide building block rules in "Security/Overview" * Add Cypress tests for alerts generated by building block rules Co-authored-by: Dmitry Shevchenko --- .../building_block_alerts.spec.ts | 40 +++++++++++++++++++ .../security_solution/cypress/objects/rule.ts | 20 ++++++++++ .../cypress/screens/overview.ts | 2 + .../cypress/tasks/api_calls/rules.ts | 1 + .../components/signals_by_category/index.tsx | 15 +++++-- .../use_filters_for_signals_by_category.ts | 37 +++++++++++++++++ 6 files changed, 111 insertions(+), 4 deletions(-) create mode 100644 x-pack/plugins/security_solution/cypress/integration/detection_alerts/building_block_alerts.spec.ts create mode 100644 x-pack/plugins/security_solution/public/overview/components/signals_by_category/use_filters_for_signals_by_category.ts diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/building_block_alerts.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/building_block_alerts.spec.ts new file mode 100644 index 0000000000000..262ffe8163e57 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/building_block_alerts.spec.ts @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getBuildingBlockRule } from '../../objects/rule'; +import { OVERVIEW_ALERTS_HISTOGRAM } from '../../screens/overview'; +import { OVERVIEW } from '../../screens/security_header'; +import { goToRuleDetails } from '../../tasks/alerts_detection_rules'; +import { createCustomRuleActivated } from '../../tasks/api_calls/rules'; +import { cleanKibana } from '../../tasks/common'; +import { waitForAlertsToPopulate, waitForTheRuleToBeExecuted } from '../../tasks/create_new_rule'; +import { loginAndWaitForPage } from '../../tasks/login'; +import { navigateFromHeaderTo } from '../../tasks/security_header'; +import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../urls/navigation'; + +const EXPECTED_NUMBER_OF_ALERTS = 16; + +describe('Alerts generated by building block rules', () => { + beforeEach(() => { + cleanKibana(); + }); + + it('Alerts should be visible on the Rule Detail page and not visible on the Overview page', () => { + createCustomRuleActivated(getBuildingBlockRule()); + loginAndWaitForPage(DETECTIONS_RULE_MANAGEMENT_URL); + goToRuleDetails(); + waitForTheRuleToBeExecuted(); + + // Check that generated events are visible on the Details page + waitForAlertsToPopulate(EXPECTED_NUMBER_OF_ALERTS); + + navigateFromHeaderTo(OVERVIEW); + + // Check that generated events are hidden on the Overview page + cy.get(OVERVIEW_ALERTS_HISTOGRAM).should('contain.text', 'No data to display'); + }); +}); diff --git a/x-pack/plugins/security_solution/cypress/objects/rule.ts b/x-pack/plugins/security_solution/cypress/objects/rule.ts index 4b061865d632b..27973854097db 100644 --- a/x-pack/plugins/security_solution/cypress/objects/rule.ts +++ b/x-pack/plugins/security_solution/cypress/objects/rule.ts @@ -58,6 +58,7 @@ export interface CustomRule { lookBack: Interval; timeline: CompleteTimeline; maxSignals: number; + buildingBlockType?: string; } export interface ThresholdRule extends CustomRule { @@ -188,6 +189,25 @@ export const getNewRule = (): CustomRule => ({ maxSignals: 100, }); +export const getBuildingBlockRule = (): CustomRule => ({ + customQuery: 'host.name: *', + index: getIndexPatterns(), + name: 'Building Block Rule Test', + description: 'The new rule description.', + severity: 'High', + riskScore: '17', + tags: ['test', 'newRule'], + referenceUrls: ['http://example.com/', 'https://example.com/'], + falsePositivesExamples: ['False1', 'False2'], + mitre: [getMitre1(), getMitre2()], + note: '# test markdown', + runsEvery: getRunsEvery(), + lookBack: getLookBack(), + timeline: getTimeline(), + maxSignals: 100, + buildingBlockType: 'default', +}); + export const getUnmappedRule = (): CustomRule => ({ customQuery: '*:*', index: ['unmapped*'], diff --git a/x-pack/plugins/security_solution/cypress/screens/overview.ts b/x-pack/plugins/security_solution/cypress/screens/overview.ts index 1376a39e5ee79..1945b7e3ce3e7 100644 --- a/x-pack/plugins/security_solution/cypress/screens/overview.ts +++ b/x-pack/plugins/security_solution/cypress/screens/overview.ts @@ -166,3 +166,5 @@ export const OVERVIEW_RISKY_HOSTS_VIEW_DASHBOARD_BUTTON = export const OVERVIEW_RISKY_HOSTS_TOTAL_EVENT_COUNT = `${OVERVIEW_RISKY_HOSTS_LINKS} [data-test-subj="header-panel-subtitle"]`; export const OVERVIEW_RISKY_HOSTS_ENABLE_MODULE_BUTTON = '[data-test-subj="risky-hosts-enable-module-button"]'; + +export const OVERVIEW_ALERTS_HISTOGRAM = '[data-test-subj="alerts-histogram-panel"]'; diff --git a/x-pack/plugins/security_solution/cypress/tasks/api_calls/rules.ts b/x-pack/plugins/security_solution/cypress/tasks/api_calls/rules.ts index 04ff0fcabc081..fd2838e5b3caa 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/api_calls/rules.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/api_calls/rules.ts @@ -114,6 +114,7 @@ export const createCustomRuleActivated = ( enabled: true, tags: ['rule1'], max_signals: maxSignals, + building_block_type: rule.buildingBlockType, }, headers: { 'kbn-xsrf': 'cypress-creds' }, failOnStatusCode: false, diff --git a/x-pack/plugins/security_solution/public/overview/components/signals_by_category/index.tsx b/x-pack/plugins/security_solution/public/overview/components/signals_by_category/index.tsx index 321e6d00b5301..cbeb1464e1b41 100644 --- a/x-pack/plugins/security_solution/public/overview/components/signals_by_category/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/signals_by_category/index.tsx @@ -7,19 +7,24 @@ import React, { useCallback } from 'react'; import { useDispatch } from 'react-redux'; +import { Filter, Query } from '@kbn/es-query'; import { AlertsHistogramPanel } from '../../../detections/components/alerts_kpis/alerts_histogram_panel'; import { useSignalIndex } from '../../../detections/containers/detection_engine/alerts/use_signal_index'; import { setAbsoluteRangeDatePicker } from '../../../common/store/inputs/actions'; -import { Filter, Query } from '../../../../../../../src/plugins/data/public'; + import { InputsModelId } from '../../../common/store/inputs/constants'; -import * as i18n from '../../pages/translations'; import { UpdateDateRange } from '../../../common/components/charts/common'; + import { AlertsStackByField } from '../../../detections/components/alerts_kpis/common/types'; +import * as i18n from '../../pages/translations'; + +import { useFiltersForSignalsByCategory } from './use_filters_for_signals_by_category'; + interface Props { combinedQueries?: string; - filters?: Filter[]; + filters: Filter[]; headerChildren?: React.ReactNode; /** Override all defaults, and only display this field */ onlyField?: AlertsStackByField; @@ -43,6 +48,8 @@ const SignalsByCategoryComponent: React.FC = ({ }) => { const dispatch = useDispatch(); const { signalIndexName } = useSignalIndex(); + const filtersForSignalsByCategory = useFiltersForSignalsByCategory(filters); + const updateDateRangeCallback = useCallback( ({ x }) => { if (!x) { @@ -63,7 +70,7 @@ const SignalsByCategoryComponent: React.FC = ({ return ( { + // TODO: Once we are past experimental phase this code should be removed + const ruleRegistryEnabled = useIsExperimentalFeatureEnabled('ruleRegistryEnabled'); + + const resultingFilters = useMemo( + () => [ + ...baseFilters, + ...(ruleRegistryEnabled + ? buildShowBuildingBlockFilterRuleRegistry(SHOW_BUILDING_BLOCK_ALERTS) // TODO: Once we are past experimental phase this code should be removed + : buildShowBuildingBlockFilter(SHOW_BUILDING_BLOCK_ALERTS)), + ], + [baseFilters, ruleRegistryEnabled] + ); + + return resultingFilters; +}; From b9024c6ad5561ddd5dc415c1dec786e1b534c75c Mon Sep 17 00:00:00 2001 From: Kevin Lacabane Date: Tue, 19 Oct 2021 11:08:20 +0200 Subject: [PATCH 041/204] timepicker-url sync functional test (#115173) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../test/functional/apps/monitoring/index.js | 2 +- .../functional/apps/monitoring/time_filter.js | 26 +++++++++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/x-pack/test/functional/apps/monitoring/index.js b/x-pack/test/functional/apps/monitoring/index.js index 6a5b6ea813171..a67964d325164 100644 --- a/x-pack/test/functional/apps/monitoring/index.js +++ b/x-pack/test/functional/apps/monitoring/index.js @@ -42,7 +42,7 @@ export default function ({ loadTestFile }) { loadTestFile(require.resolve('./beats/listing')); loadTestFile(require.resolve('./beats/beat_detail')); - // loadTestFile(require.resolve('./time_filter')); + loadTestFile(require.resolve('./time_filter')); loadTestFile(require.resolve('./enable_monitoring')); loadTestFile(require.resolve('./setup/metricbeat_migration')); diff --git a/x-pack/test/functional/apps/monitoring/time_filter.js b/x-pack/test/functional/apps/monitoring/time_filter.js index 910b91d07039d..76e7bc5cd043d 100644 --- a/x-pack/test/functional/apps/monitoring/time_filter.js +++ b/x-pack/test/functional/apps/monitoring/time_filter.js @@ -12,22 +12,44 @@ export default function ({ getService, getPageObjects }) { const PageObjects = getPageObjects(['header', 'timePicker']); const testSubjects = getService('testSubjects'); const clusterList = getService('monitoringClusterList'); + const browser = getService('browser'); + + const assertTimePickerRange = async (start, end) => { + const timeConfig = await PageObjects.timePicker.getTimeConfig(); + expect(timeConfig.start).to.eql(start); + expect(timeConfig.end).to.eql(end); + }; describe('Timefilter', () => { const { setup, tearDown } = getLifecycleMethods(getService, getPageObjects); + const from = 'Aug 15, 2017 @ 21:00:00.000'; + const to = 'Aug 16, 2017 @ 00:00:00.000'; + before(async () => { await setup('x-pack/test/functional/es_archives/monitoring/multicluster', { - from: 'Aug 15, 2017 @ 21:00:00.000', - to: 'Aug 16, 2017 @ 00:00:00.000', + from, + to, }); await clusterList.assertDefaults(); + await clusterList.closeAlertsModal(); }); after(async () => { await tearDown(); }); + it('syncs timepicker with url hash updates', async () => { + await assertTimePickerRange(from, to); + + await browser.execute(() => { + const hash = window.location.hash; + window.location.hash = hash.replace(/time:\(([^)]+)\)/, 'time:(from:now-15m,to:now)'); + }); + + await assertTimePickerRange('~ 15 minutes ago', 'now'); + }); + // FLAKY: https://github.com/elastic/kibana/issues/48910 it.skip('should send another request when clicking Refresh', async () => { await testSubjects.click('querySubmitButton'); From 5fe9a319c001444819d0f79e5ea2e3a375eb2771 Mon Sep 17 00:00:00 2001 From: mgiota Date: Tue, 19 Oct 2021 11:09:48 +0200 Subject: [PATCH 042/204] [RAC] [Metrics UI] Include group name in the reason message (#115171) * [RAC] [Metrics UI] Include group name in the reason message * remove console log * fix i18n errors * fix more i18n errors * fix i18n & check errors and move group to the end of the reason text * add empty lines at the end of translation files * fix more i18n tests * try to remove manually added translations * Revert "try to remove manually added translations" This reverts commit 6949af2f70aff46b088bab5c942497ad46081d90. * apply i18n_check fix and reorder values in the formatted reason * log threshold reformat reason message and move group info at the end --- .../server/lib/alerting/common/messages.ts | 18 ++++++---- .../inventory_metric_threshold_executor.ts | 35 +++++++++++-------- .../log_threshold/reason_formatters.ts | 4 +-- .../metric_threshold_executor.ts | 9 ++--- .../translations/translations/ja-JP.json | 3 -- .../translations/translations/zh-CN.json | 3 -- 6 files changed, 40 insertions(+), 32 deletions(-) diff --git a/x-pack/plugins/infra/server/lib/alerting/common/messages.ts b/x-pack/plugins/infra/server/lib/alerting/common/messages.ts index 084043f357bb1..23c89abf4a7aa 100644 --- a/x-pack/plugins/infra/server/lib/alerting/common/messages.ts +++ b/x-pack/plugins/infra/server/lib/alerting/common/messages.ts @@ -109,15 +109,17 @@ const thresholdToI18n = ([a, b]: Array) => { }; export const buildFiredAlertReason: (alertResult: { + group: string; metric: string; comparator: Comparator; threshold: Array; currentValue: number | string; -}) => string = ({ metric, comparator, threshold, currentValue }) => +}) => string = ({ group, metric, comparator, threshold, currentValue }) => i18n.translate('xpack.infra.metrics.alerting.threshold.firedAlertReason', { defaultMessage: - '{metric} is {comparator} a threshold of {threshold} (current value is {currentValue})', + '{metric} is {comparator} a threshold of {threshold} (current value is {currentValue}) for {group}', values: { + group, metric, comparator: comparatorToI18n(comparator, threshold.map(toNumber), toNumber(currentValue)), threshold: thresholdToI18n(threshold), @@ -126,14 +128,15 @@ export const buildFiredAlertReason: (alertResult: { }); export const buildRecoveredAlertReason: (alertResult: { + group: string; metric: string; comparator: Comparator; threshold: Array; currentValue: number | string; -}) => string = ({ metric, comparator, threshold, currentValue }) => +}) => string = ({ group, metric, comparator, threshold, currentValue }) => i18n.translate('xpack.infra.metrics.alerting.threshold.recoveredAlertReason', { defaultMessage: - '{metric} is now {comparator} a threshold of {threshold} (current value is {currentValue})', + '{metric} is now {comparator} a threshold of {threshold} (current value is {currentValue}) for {group}', values: { metric, comparator: recoveredComparatorToI18n( @@ -143,19 +146,22 @@ export const buildRecoveredAlertReason: (alertResult: { ), threshold: thresholdToI18n(threshold), currentValue, + group, }, }); export const buildNoDataAlertReason: (alertResult: { + group: string; metric: string; timeSize: number; timeUnit: string; -}) => string = ({ metric, timeSize, timeUnit }) => +}) => string = ({ group, metric, timeSize, timeUnit }) => i18n.translate('xpack.infra.metrics.alerting.threshold.noDataAlertReason', { - defaultMessage: '{metric} has reported no data over the past {interval}', + defaultMessage: '{metric} has reported no data over the past {interval} for {group}', values: { metric, interval: `${timeSize}${timeUnit}`, + group, }, }); diff --git a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts index 5cd093c6f1472..3dd702126735d 100644 --- a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts +++ b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts @@ -102,18 +102,18 @@ export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) = ) ); const inventoryItems = Object.keys(first(results)!); - for (const item of inventoryItems) { + for (const group of inventoryItems) { // AND logic; all criteria must be across the threshold const shouldAlertFire = results.every((result) => { // Grab the result of the most recent bucket - return last(result[item].shouldFire); + return last(result[group].shouldFire); }); - const shouldAlertWarn = results.every((result) => last(result[item].shouldWarn)); + const shouldAlertWarn = results.every((result) => last(result[group].shouldWarn)); // AND logic; because we need to evaluate all criteria, if one of them reports no data then the // whole alert is in a No Data/Error state - const isNoData = results.some((result) => last(result[item].isNoData)); - const isError = results.some((result) => result[item].isError); + const isNoData = results.some((result) => last(result[group].isNoData)); + const isError = results.some((result) => result[group].isError); const nextState = isError ? AlertStates.ERROR @@ -129,7 +129,8 @@ export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) = reason = results .map((result) => buildReasonWithVerboseMetricName( - result[item], + group, + result[group], buildFiredAlertReason, nextState === AlertStates.WARNING ) @@ -142,19 +143,23 @@ export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) = */ // } else if (nextState === AlertStates.OK && prevState?.alertState === AlertStates.ALERT) { // reason = results - // .map((result) => buildReasonWithVerboseMetricName(result[item], buildRecoveredAlertReason)) + // .map((result) => buildReasonWithVerboseMetricName(group, result[group], buildRecoveredAlertReason)) // .join('\n'); } if (alertOnNoData) { if (nextState === AlertStates.NO_DATA) { reason = results - .filter((result) => result[item].isNoData) - .map((result) => buildReasonWithVerboseMetricName(result[item], buildNoDataAlertReason)) + .filter((result) => result[group].isNoData) + .map((result) => + buildReasonWithVerboseMetricName(group, result[group], buildNoDataAlertReason) + ) .join('\n'); } else if (nextState === AlertStates.ERROR) { reason = results - .filter((result) => result[item].isError) - .map((result) => buildReasonWithVerboseMetricName(result[item], buildErrorAlertReason)) + .filter((result) => result[group].isError) + .map((result) => + buildReasonWithVerboseMetricName(group, result[group], buildErrorAlertReason) + ) .join('\n'); } } @@ -166,7 +171,7 @@ export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) = ? WARNING_ACTIONS.id : FIRED_ACTIONS.id; - const alertInstance = alertInstanceFactory(`${item}`, reason); + const alertInstance = alertInstanceFactory(`${group}`, reason); alertInstance.scheduleActions( /** * TODO: We're lying to the compiler here as explicitly calling `scheduleActions` on @@ -174,12 +179,12 @@ export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) = */ actionGroupId as unknown as InventoryMetricThresholdAllowedActionGroups, { - group: item, + group, alertState: stateToAlertMessage[nextState], reason, timestamp: moment().toISOString(), value: mapToConditionsLookup(results, (result) => - formatMetric(result[item].metric, result[item].currentValue) + formatMetric(result[group].metric, result[group].currentValue) ), threshold: mapToConditionsLookup(criteria, (c) => c.threshold), metric: mapToConditionsLookup(criteria, (c) => c.metric), @@ -190,6 +195,7 @@ export const createInventoryMetricThresholdExecutor = (libs: InfraBackendLibs) = }); const buildReasonWithVerboseMetricName = ( + group: string, resultItem: any, buildReason: (r: any) => string, useWarningThreshold?: boolean @@ -197,6 +203,7 @@ const buildReasonWithVerboseMetricName = ( if (!resultItem) return ''; const resultWithVerboseMetricName = { ...resultItem, + group, metric: toMetricOpt(resultItem.metric)?.text || (resultItem.metric === 'custom' diff --git a/x-pack/plugins/infra/server/lib/alerting/log_threshold/reason_formatters.ts b/x-pack/plugins/infra/server/lib/alerting/log_threshold/reason_formatters.ts index cd579b9965b66..f70e0a0140ce8 100644 --- a/x-pack/plugins/infra/server/lib/alerting/log_threshold/reason_formatters.ts +++ b/x-pack/plugins/infra/server/lib/alerting/log_threshold/reason_formatters.ts @@ -34,7 +34,7 @@ export const getReasonMessageForGroupedCountAlert = ( ) => i18n.translate('xpack.infra.logs.alerting.threshold.groupedCountAlertReasonDescription', { defaultMessage: - '{groupName}: {actualCount, plural, one {{actualCount} log entry} other {{actualCount} log entries} } ({translatedComparator} {expectedCount}) match the conditions.', + '{actualCount, plural, one {{actualCount} log entry} other {{actualCount} log entries} } ({translatedComparator} {expectedCount}) match the conditions for {groupName}.', values: { actualCount, expectedCount, @@ -66,7 +66,7 @@ export const getReasonMessageForGroupedRatioAlert = ( ) => i18n.translate('xpack.infra.logs.alerting.threshold.groupedRatioAlertReasonDescription', { defaultMessage: - '{groupName}: The log entries ratio is {actualRatio} ({translatedComparator} {expectedRatio}).', + 'The log entries ratio is {actualRatio} ({translatedComparator} {expectedRatio}) for {groupName}.', values: { actualRatio, expectedRatio, diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts index af5f945eeb4bb..e4887e922bb66 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts @@ -143,9 +143,10 @@ export const createMetricThresholdExecutor = (libs: InfraBackendLibs) => if (nextState === AlertStates.ALERT || nextState === AlertStates.WARNING) { reason = alertResults .map((result) => - buildFiredAlertReason( - formatAlertResult(result[group], nextState === AlertStates.WARNING) - ) + buildFiredAlertReason({ + ...formatAlertResult(result[group], nextState === AlertStates.WARNING), + group, + }) ) .join('\n'); /* @@ -181,7 +182,7 @@ export const createMetricThresholdExecutor = (libs: InfraBackendLibs) => if (nextState === AlertStates.NO_DATA) { reason = alertResults .filter((result) => result[group].isNoData) - .map((result) => buildNoDataAlertReason(result[group])) + .map((result) => buildNoDataAlertReason({ ...result[group], group })) .join('\n'); } else if (nextState === AlertStates.ERROR) { reason = alertResults diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 59a8cdaab6413..aac0f651b8dee 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -13446,15 +13446,12 @@ "xpack.infra.metrics.alerting.threshold.errorAlertReason": "{metric}のデータのクエリを試行しているときに、Elasticsearchが失敗しました", "xpack.infra.metrics.alerting.threshold.errorState": "エラー", "xpack.infra.metrics.alerting.threshold.fired": "アラート", - "xpack.infra.metrics.alerting.threshold.firedAlertReason": "{metric}は{comparator} {threshold}のしきい値です(現在の値は{currentValue})", "xpack.infra.metrics.alerting.threshold.gtComparator": "より大きい", "xpack.infra.metrics.alerting.threshold.ltComparator": "より小さい", - "xpack.infra.metrics.alerting.threshold.noDataAlertReason": "{metric}は過去{interval}にデータを報告していません", "xpack.infra.metrics.alerting.threshold.noDataFormattedValue": "[データなし]", "xpack.infra.metrics.alerting.threshold.noDataState": "データなし", "xpack.infra.metrics.alerting.threshold.okState": "OK [回復済み]", "xpack.infra.metrics.alerting.threshold.outsideRangeComparator": "の間にない", - "xpack.infra.metrics.alerting.threshold.recoveredAlertReason": "{metric}は{comparator} {threshold}のしきい値です(現在の値は{currentValue})", "xpack.infra.metrics.alerting.threshold.thresholdRange": "{a}と{b}", "xpack.infra.metrics.alerting.threshold.warning": "警告", "xpack.infra.metrics.alerting.threshold.warningState": "警告", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 4e901f51d54da..50d90f5144585 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -13634,15 +13634,12 @@ "xpack.infra.metrics.alerting.threshold.errorAlertReason": "Elasticsearch 尝试查询 {metric} 的数据时出现故障", "xpack.infra.metrics.alerting.threshold.errorState": "错误", "xpack.infra.metrics.alerting.threshold.fired": "告警", - "xpack.infra.metrics.alerting.threshold.firedAlertReason": "{metric} {comparator}阈值 {threshold}(当前值为 {currentValue})", "xpack.infra.metrics.alerting.threshold.gtComparator": "大于", "xpack.infra.metrics.alerting.threshold.ltComparator": "小于", - "xpack.infra.metrics.alerting.threshold.noDataAlertReason": "{metric} 在过去 {interval}中未报告数据", "xpack.infra.metrics.alerting.threshold.noDataFormattedValue": "[无数据]", "xpack.infra.metrics.alerting.threshold.noDataState": "无数据", "xpack.infra.metrics.alerting.threshold.okState": "正常 [已恢复]", "xpack.infra.metrics.alerting.threshold.outsideRangeComparator": "不介于", - "xpack.infra.metrics.alerting.threshold.recoveredAlertReason": "{metric} 现在{comparator}阈值 {threshold}(当前值为 {currentValue})", "xpack.infra.metrics.alerting.threshold.thresholdRange": "{a} 和 {b}", "xpack.infra.metrics.alerting.threshold.warning": "警告", "xpack.infra.metrics.alerting.threshold.warningState": "警告", From f9afe67f1e554cf3b295c4c43bf2b3f68c103120 Mon Sep 17 00:00:00 2001 From: Andrew Goldstein Date: Tue, 19 Oct 2021 04:10:14 -0600 Subject: [PATCH 043/204] [Security Solution] Improves the formatting of array values and JSON in the Event and Alert Details panels (#115141) ## [Security Solution] Improves the formatting of array values and JSON in the Event and Alert Details panels This PR improves the formatting of array values and JSON in the Event and Alert details panels by: - in the `Table` tab, formatting array values such that each value appears on a separate line, (instead of joining the values on a single line) - in the `JSON` tab, displaying the raw search hit JSON, instead displaying a JSON representation based on the `Fields` API ### Table value formatting In the Event and Alert details `Table` tab, array values were joined on a single line, as shown in the _before_ screenshot below: ![event-details-value-formatting-before](https://user-images.githubusercontent.com/4459398/137524968-6450cd73-3154-457d-b850-32a3e7faaab2.png) _Above: (before) array values were joined on a single line_ Array values are now formatted such that each value appears on a separate line, as shown in the _after_ screenshot below: ![event-details-value-formatting-after](https://user-images.githubusercontent.com/4459398/137436705-b0bec735-5a83-402e-843a-2776e1c80da9.png) _Above: (after) array values each appear on a separte line_ ### JSON formatting The `JSON` tab previously displayed a JSON representation based on the `Fields` API. Array values were previously represented as a joined string, as shown in the _before_ screenshot below: ![event-details-json-formatting-before](https://user-images.githubusercontent.com/4459398/137525039-d1b14f21-5f9c-4201-905e-8b08f00bb5a0.png) _Above: (before) array values were previously represented as a joined string_ The `JSON` tab now displays the raw search hit JSON, per the _after_ screenshot below: ![event-details-json-formatting-after](https://user-images.githubusercontent.com/4459398/137437257-330c5b49-a4ad-418e-a976-923f7a35c0cf.png) _Above: (after) the `JSON` tab displays the raw search hit_ CC @monina-n @paulewing --- .../detection_alerts/alerts_details.spec.ts | 20 +- .../detection_alerts/cti_enrichments.spec.ts | 49 ++- .../cypress/screens/alerts_details.ts | 2 + .../alert_summary_view.test.tsx.snap | 120 ++++-- .../__snapshots__/json_view.test.tsx.snap | 343 ++++++++++++++++-- .../event_details/event_details.test.tsx | 3 +- .../event_details/event_details.tsx | 6 +- .../event_details/json_view.test.tsx | 49 +-- .../components/event_details/json_view.tsx | 23 +- .../table/field_value_cell.test.tsx | 193 ++++++++++ .../event_details/table/field_value_cell.tsx | 26 +- .../public/common/mock/mock_detail_item.ts | 188 ++++++++++ .../event_details/expandable_event.tsx | 3 + .../side_panel/event_details/index.tsx | 4 +- .../timelines/containers/details/index.tsx | 7 +- .../timeline/events/details/index.ts | 1 + .../timeline/factory/events/details/index.ts | 4 + 17 files changed, 872 insertions(+), 169 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/common/components/event_details/table/field_value_cell.test.tsx diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/alerts_details.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/alerts_details.spec.ts index 674114188632b..7b792f8d560f1 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/alerts_details.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/alerts_details.spec.ts @@ -5,14 +5,14 @@ * 2.0. */ -import { ALERT_FLYOUT, CELL_TEXT, JSON_LINES, TABLE_ROWS } from '../../screens/alerts_details'; +import { ALERT_FLYOUT, CELL_TEXT, JSON_TEXT, TABLE_ROWS } from '../../screens/alerts_details'; import { expandFirstAlert, waitForAlertsIndexToBeCreated, waitForAlertsPanelToBeLoaded, } from '../../tasks/alerts'; -import { openJsonView, openTable, scrollJsonViewToBottom } from '../../tasks/alerts_details'; +import { openJsonView, openTable } from '../../tasks/alerts_details'; import { createCustomRuleActivated } from '../../tasks/api_calls/rules'; import { cleanKibana } from '../../tasks/common'; import { esArchiverLoad } from '../../tasks/es_archiver'; @@ -36,20 +36,14 @@ describe('Alert details with unmapped fields', () => { }); it('Displays the unmapped field on the JSON view', () => { - const expectedUnmappedField = { line: 2, text: ' "unmapped": "This is the unmapped field"' }; + const expectedUnmappedValue = 'This is the unmapped field'; openJsonView(); - scrollJsonViewToBottom(); - cy.get(ALERT_FLYOUT) - .find(JSON_LINES) - .then((elements) => { - const length = elements.length; - cy.wrap(elements) - .eq(length - expectedUnmappedField.line) - .invoke('text') - .should('include', expectedUnmappedField.text); - }); + cy.get(JSON_TEXT).then((x) => { + const parsed = JSON.parse(x.text()); + expect(parsed._source.unmapped).to.equal(expectedUnmappedValue); + }); }); it('Displays the unmapped field on the table', () => { diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/cti_enrichments.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/cti_enrichments.spec.ts index b3c6abcd8e426..f15e7adbbca44 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/cti_enrichments.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/cti_enrichments.spec.ts @@ -10,7 +10,7 @@ import { cleanKibana, reload } from '../../tasks/common'; import { esArchiverLoad, esArchiverUnload } from '../../tasks/es_archiver'; import { loginAndWaitForPageWithoutDateRange } from '../../tasks/login'; import { - JSON_LINES, + JSON_TEXT, TABLE_CELL, TABLE_ROWS, THREAT_DETAILS_VIEW, @@ -28,11 +28,7 @@ import { viewThreatIntelTab, } from '../../tasks/alerts'; import { createCustomIndicatorRule } from '../../tasks/api_calls/rules'; -import { - openJsonView, - openThreatIndicatorDetails, - scrollJsonViewToBottom, -} from '../../tasks/alerts_details'; +import { openJsonView, openThreatIndicatorDetails } from '../../tasks/alerts_details'; import { ALERTS_URL } from '../../urls/navigation'; import { addsFieldsToTimeline } from '../../tasks/rule_details'; @@ -76,26 +72,39 @@ describe('CTI Enrichment', () => { it('Displays persisted enrichments on the JSON view', () => { const expectedEnrichment = [ - { line: 4, text: ' "threat": {' }, { - line: 3, - text: ' "enrichments": "{\\"indicator\\":{\\"first_seen\\":\\"2021-03-10T08:02:14.000Z\\",\\"file\\":{\\"size\\":80280,\\"pe\\":{},\\"type\\":\\"elf\\",\\"hash\\":{\\"sha256\\":\\"a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3\\",\\"tlsh\\":\\"6D7312E017B517CC1371A8353BED205E9128223972AE35302E97528DF957703BAB2DBE\\",\\"ssdeep\\":\\"1536:87vbq1lGAXSEYQjbChaAU2yU23M51DjZgSQAvcYkFtZTjzBht5:8D+CAXFYQChaAUk5ljnQssL\\",\\"md5\\":\\"9b6c3518a91d23ed77504b5416bfb5b3\\"}},\\"type\\":\\"file\\"},\\"matched\\":{\\"atomic\\":\\"a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3\\",\\"field\\":\\"myhash.mysha256\\",\\"id\\":\\"84cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb74f\\",\\"index\\":\\"logs-ti_abusech.malware\\",\\"type\\":\\"indicator_match_rule\\"}}"', + indicator: { + first_seen: '2021-03-10T08:02:14.000Z', + file: { + size: 80280, + pe: {}, + type: 'elf', + hash: { + sha256: 'a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3', + tlsh: '6D7312E017B517CC1371A8353BED205E9128223972AE35302E97528DF957703BAB2DBE', + ssdeep: + '1536:87vbq1lGAXSEYQjbChaAU2yU23M51DjZgSQAvcYkFtZTjzBht5:8D+CAXFYQChaAUk5ljnQssL', + md5: '9b6c3518a91d23ed77504b5416bfb5b3', + }, + }, + type: 'file', + }, + matched: { + atomic: 'a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3', + field: 'myhash.mysha256', + id: '84cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb74f', + index: 'logs-ti_abusech.malware', + type: 'indicator_match_rule', + }, }, - { line: 2, text: ' }' }, ]; expandFirstAlert(); openJsonView(); - scrollJsonViewToBottom(); - - cy.get(JSON_LINES).then((elements) => { - const length = elements.length; - expectedEnrichment.forEach((enrichment) => { - cy.wrap(elements) - .eq(length - enrichment.line) - .invoke('text') - .should('include', enrichment.text); - }); + + cy.get(JSON_TEXT).then((x) => { + const parsed = JSON.parse(x.text()); + expect(parsed._source.threat.enrichments).to.deep.equal(expectedEnrichment); }); }); diff --git a/x-pack/plugins/security_solution/cypress/screens/alerts_details.ts b/x-pack/plugins/security_solution/cypress/screens/alerts_details.ts index c740a669d059a..584fba05452f0 100644 --- a/x-pack/plugins/security_solution/cypress/screens/alerts_details.ts +++ b/x-pack/plugins/security_solution/cypress/screens/alerts_details.ts @@ -28,6 +28,8 @@ export const JSON_LINES = '.euiCodeBlock__line'; export const JSON_VIEW_TAB = '[data-test-subj="jsonViewTab"]'; +export const JSON_TEXT = '[data-test-subj="jsonView"]'; + export const TABLE_CELL = '.euiTableRowCell'; export const TABLE_TAB = '[data-test-subj="tableTab"]'; diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/__snapshots__/alert_summary_view.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/event_details/__snapshots__/alert_summary_view.test.tsx.snap index d367c68586be1..930e1282ebca5 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/__snapshots__/alert_summary_view.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/common/components/event_details/__snapshots__/alert_summary_view.test.tsx.snap @@ -138,12 +138,17 @@ exports[`AlertSummaryView Behavior event code renders additional summary rows 1` class="euiTableCellContent flyoutOverviewDescription euiTableCellContent--overflowingContent" >
- open +
+ open +
- xxx +
+ xxx +
- low +
+ low +
- 21 +
+ 21 +
- windows-native +
+ windows-native +
- administrator +
+ administrator +
- open +
+ open +
- xxx +
+ xxx +
- low +
+ low +
- 21 +
+ 21 +
- windows-native +
+ windows-native +
- administrator +
+ administrator +
{ - "_id": "pEMaMmkBUV60JmNWmWVi", - "_index": "filebeat-8.0.0-2019.02.19-000001", + "_index": ".ds-logs-endpoint.events.network-default-2021.09.28-000001", + "_id": "TUWyf3wBFCFU0qRJTauW", "_score": 1, - "_type": "_doc", - "@timestamp": "2019-02-28T16:50:54.621Z", - "agent": { - "ephemeral_id": "9d391ef2-a734-4787-8891-67031178c641", - "hostname": "siem-kibana", - "id": "5de03d5f-52f3-482e-91d4-853c7de073c3", - "type": "filebeat", - "version": "8.0.0" - }, - "cloud": { - "availability_zone": "projects/189716325846/zones/us-east1-b", - "instance": { - "id": "5412578377715150143", - "name": "siem-kibana" + "_source": { + "agent": { + "id": "2ac9e9b3-f6d5-4ce6-915d-8f1f8f413624", + "type": "endpoint", + "version": "8.0.0-SNAPSHOT" }, - "machine": { - "type": "projects/189716325846/machineTypes/n1-standard-1" + "process": { + "Ext": { + "ancestry": [ + "MmFjOWU5YjMtZjZkNS00Y2U2LTkxNWQtOGYxZjhmNDEzNjI0LTIyMzY0LTEzMjc4NjA2NTAyLjA=", + "MmFjOWU5YjMtZjZkNS00Y2U2LTkxNWQtOGYxZjhmNDEzNjI0LTEtMTMyNzA3Njg2OTIuMA==" + ] + }, + "name": "filebeat", + "pid": 22535, + "entity_id": "MmFjOWU5YjMtZjZkNS00Y2U2LTkxNWQtOGYxZjhmNDEzNjI0LTIyNTM1LTEzMjc4NjA2NTI4LjA=", + "executable": "/opt/Elastic/Agent/data/elastic-agent-058c40/install/filebeat-8.0.0-SNAPSHOT-linux-x86_64/filebeat" }, - "project": { - "id": "elastic-beats" + "destination": { + "address": "127.0.0.1", + "port": 9200, + "ip": "127.0.0.1" }, - "provider": "gce" - }, - "destination": { - "bytes": 584, - "ip": "10.47.8.200", - "packets": 4, - "port": 902 + "source": { + "address": "127.0.0.1", + "port": 54146, + "ip": "127.0.0.1" + }, + "message": "Endpoint network event", + "network": { + "transport": "tcp", + "type": "ipv4" + }, + "@timestamp": "2021-10-14T16:45:58.0310772Z", + "ecs": { + "version": "1.11.0" + }, + "data_stream": { + "namespace": "default", + "type": "logs", + "dataset": "endpoint.events.network" + }, + "elastic": { + "agent": { + "id": "12345" + } + }, + "host": { + "hostname": "test-linux-1", + "os": { + "Ext": { + "variant": "Debian" + }, + "kernel": "4.19.0-17-cloud-amd64 #1 SMP Debian 4.19.194-2 (2021-06-21)", + "name": "Linux", + "family": "debian", + "type": "linux", + "version": "10", + "platform": "debian", + "full": "Debian 10" + }, + "ip": [ + "127.0.0.1", + "::1", + "10.1.2.3", + "2001:0DB8:AC10:FE01::" + ], + "name": "test-linux-1", + "id": "76ea303129f249aa7382338e4263eac1", + "mac": [ + "aa:bb:cc:dd:ee:ff" + ], + "architecture": "x86_64" + }, + "event": { + "agent_id_status": "verified", + "sequence": 44872, + "ingested": "2021-10-14T16:46:04Z", + "created": "2021-10-14T16:45:58.0310772Z", + "kind": "event", + "module": "endpoint", + "action": "connection_attempted", + "id": "MKPXftjGeHiQzUNj++++nn6R", + "category": [ + "network" + ], + "type": [ + "start" + ], + "dataset": "endpoint.events.network", + "outcome": "unknown" + }, + "user": { + "Ext": { + "real": { + "name": "root", + "id": 0 + } + }, + "name": "root", + "id": 0 + }, + "group": { + "Ext": { + "real": { + "name": "root", + "id": 0 + } + }, + "name": "root", + "id": 0 + } }, - "event": { - "kind": "event" + "fields": { + "host.os.full.text": [ + "Debian 10" + ], + "event.category": [ + "network" + ], + "process.name.text": [ + "filebeat" + ], + "host.os.name.text": [ + "Linux" + ], + "host.os.full": [ + "Debian 10" + ], + "host.hostname": [ + "test-linux-1" + ], + "process.pid": [ + 22535 + ], + "host.mac": [ + "42:01:0a:c8:00:32" + ], + "elastic.agent.id": [ + "abcdefg-f6d5-4ce6-915d-8f1f8f413624" + ], + "host.os.version": [ + "10" + ], + "host.os.name": [ + "Linux" + ], + "source.ip": [ + "127.0.0.1" + ], + "destination.address": [ + "127.0.0.1" + ], + "host.name": [ + "test-linux-1" + ], + "event.agent_id_status": [ + "verified" + ], + "event.kind": [ + "event" + ], + "event.outcome": [ + "unknown" + ], + "group.name": [ + "root" + ], + "user.id": [ + "0" + ], + "host.os.type": [ + "linux" + ], + "process.Ext.ancestry": [ + "MmFjOWU5YjMtZjZkNS00Y2U2LTkxNWQtOGYxZjhmNDEzNjI0LTIyMzY0LTEzMjc4NjA2NTAyLjA=", + "MmFjOWU5YjMtZjZkNS00Y2U2LTkxNWQtOGYxZjhmNDEzNjI0LTEtMTMyNzA3Njg2OTIuMA==" + ], + "user.Ext.real.id": [ + "0" + ], + "data_stream.type": [ + "logs" + ], + "host.architecture": [ + "x86_64" + ], + "process.name": [ + "filebeat" + ], + "agent.id": [ + "2ac9e9b3-f6d5-4ce6-915d-8f1f8f413624" + ], + "source.port": [ + 54146 + ], + "ecs.version": [ + "1.11.0" + ], + "event.created": [ + "2021-10-14T16:45:58.031Z" + ], + "agent.version": [ + "8.0.0-SNAPSHOT" + ], + "host.os.family": [ + "debian" + ], + "destination.port": [ + 9200 + ], + "group.id": [ + "0" + ], + "user.name": [ + "root" + ], + "source.address": [ + "127.0.0.1" + ], + "process.entity_id": [ + "MmFjOWU5YjMtZjZkNS00Y2U2LTkxNWQtOGYxZjhmNDEzNjI0LTIyNTM1LTEzMjc4NjA2NTI4LjA=" + ], + "host.ip": [ + "127.0.0.1", + "::1", + "10.1.2.3", + "2001:0DB8:AC10:FE01::" + ], + "process.executable.caseless": [ + "/opt/elastic/agent/data/elastic-agent-058c40/install/filebeat-8.0.0-snapshot-linux-x86_64/filebeat" + ], + "event.sequence": [ + 44872 + ], + "agent.type": [ + "endpoint" + ], + "process.executable.text": [ + "/opt/Elastic/Agent/data/elastic-agent-058c40/install/filebeat-8.0.0-SNAPSHOT-linux-x86_64/filebeat" + ], + "group.Ext.real.name": [ + "root" + ], + "event.module": [ + "endpoint" + ], + "host.os.kernel": [ + "4.19.0-17-cloud-amd64 #1 SMP Debian 4.19.194-2 (2021-06-21)" + ], + "host.os.full.caseless": [ + "debian 10" + ], + "host.id": [ + "76ea303129f249aa7382338e4263eac1" + ], + "process.name.caseless": [ + "filebeat" + ], + "network.type": [ + "ipv4" + ], + "process.executable": [ + "/opt/Elastic/Agent/data/elastic-agent-058c40/install/filebeat-8.0.0-SNAPSHOT-linux-x86_64/filebeat" + ], + "user.Ext.real.name": [ + "root" + ], + "data_stream.namespace": [ + "default" + ], + "message": [ + "Endpoint network event" + ], + "destination.ip": [ + "127.0.0.1" + ], + "network.transport": [ + "tcp" + ], + "host.os.Ext.variant": [ + "Debian" + ], + "group.Ext.real.id": [ + "0" + ], + "event.ingested": [ + "2021-10-14T16:46:04.000Z" + ], + "event.action": [ + "connection_attempted" + ], + "@timestamp": [ + "2021-10-14T16:45:58.031Z" + ], + "host.os.platform": [ + "debian" + ], + "data_stream.dataset": [ + "endpoint.events.network" + ], + "event.type": [ + "start" + ], + "event.id": [ + "MKPXftjGeHiQzUNj++++nn6R" + ], + "host.os.name.caseless": [ + "linux" + ], + "event.dataset": [ + "endpoint.events.network" + ], + "user.name.text": [ + "root" + ] } } diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx index a8ba536a75541..37ca3b0b897a6 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx @@ -11,7 +11,7 @@ import React from 'react'; import '../../mock/match_media'; import '../../mock/react_beautiful_dnd'; -import { mockDetailItemData, mockDetailItemDataId, TestProviders } from '../../mock'; +import { mockDetailItemData, mockDetailItemDataId, rawEventData, TestProviders } from '../../mock'; import { EventDetails, EventsViewType } from './event_details'; import { mockBrowserFields } from '../../containers/source/mock'; @@ -48,6 +48,7 @@ describe('EventDetails', () => { timelineId: 'test', eventView: EventsViewType.summaryView, hostRisk: { fields: [], loading: true }, + rawEventData, }; const alertsProps = { diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx index e7092d9d6f466..a8305a635f157 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx @@ -61,6 +61,7 @@ interface Props { id: string; isAlert: boolean; isDraggable?: boolean; + rawEventData: object | undefined; timelineTabType: TimelineTabs | 'flyout'; timelineId: string; hostRisk: HostRisk | null; @@ -106,6 +107,7 @@ const EventDetailsComponent: React.FC = ({ id, isAlert, isDraggable, + rawEventData, timelineId, timelineTabType, hostRisk, @@ -278,12 +280,12 @@ const EventDetailsComponent: React.FC = ({ <> - + ), }), - [data] + [rawEventData] ); const tabs = useMemo(() => { diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/json_view.test.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/json_view.test.tsx index 696fac6016603..b20270266602d 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/json_view.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/json_view.test.tsx @@ -8,58 +8,15 @@ import { shallow } from 'enzyme'; import React from 'react'; -import { mockDetailItemData } from '../../mock'; +import { rawEventData } from '../../mock'; -import { buildJsonView, JsonView } from './json_view'; +import { JsonView } from './json_view'; describe('JSON View', () => { describe('rendering', () => { test('should match snapshot', () => { - const wrapper = shallow(); + const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); }); }); - - describe('buildJsonView', () => { - test('should match a json', () => { - const expectedData = { - '@timestamp': '2019-02-28T16:50:54.621Z', - _id: 'pEMaMmkBUV60JmNWmWVi', - _index: 'filebeat-8.0.0-2019.02.19-000001', - _score: 1, - _type: '_doc', - agent: { - ephemeral_id: '9d391ef2-a734-4787-8891-67031178c641', - hostname: 'siem-kibana', - id: '5de03d5f-52f3-482e-91d4-853c7de073c3', - type: 'filebeat', - version: '8.0.0', - }, - cloud: { - availability_zone: 'projects/189716325846/zones/us-east1-b', - instance: { - id: '5412578377715150143', - name: 'siem-kibana', - }, - machine: { - type: 'projects/189716325846/machineTypes/n1-standard-1', - }, - project: { - id: 'elastic-beats', - }, - provider: 'gce', - }, - destination: { - bytes: 584, - ip: '10.47.8.200', - packets: 4, - port: 902, - }, - event: { - kind: 'event', - }, - }; - expect(buildJsonView(mockDetailItemData)).toEqual(expectedData); - }); - }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/json_view.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/json_view.tsx index 0614f131bcd10..0227d44f32305 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/json_view.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/json_view.tsx @@ -6,15 +6,13 @@ */ import { EuiCodeBlock } from '@elastic/eui'; -import { set } from '@elastic/safer-lodash-set/fp'; import React, { useMemo } from 'react'; import styled from 'styled-components'; -import { TimelineEventsDetailsItem } from '../../../../common/search_strategy'; import { omitTypenameAndEmpty } from '../../../timelines/components/timeline/body/helpers'; interface Props { - data: TimelineEventsDetailsItem[]; + rawEventData: object | undefined; } const EuiCodeEditorContainer = styled.div` @@ -23,15 +21,15 @@ const EuiCodeEditorContainer = styled.div` } `; -export const JsonView = React.memo(({ data }) => { +export const JsonView = React.memo(({ rawEventData }) => { const value = useMemo( () => JSON.stringify( - buildJsonView(data), + rawEventData, omitTypenameAndEmpty, 2 // indent level ), - [data] + [rawEventData] ); return ( @@ -50,16 +48,3 @@ export const JsonView = React.memo(({ data }) => { }); JsonView.displayName = 'JsonView'; - -export const buildJsonView = (data: TimelineEventsDetailsItem[]) => - data - .sort((a, b) => a.field.localeCompare(b.field)) - .reduce( - (accumulator, item) => - set( - item.field, - Array.isArray(item.originalValue) ? item.originalValue.join() : item.originalValue, - accumulator - ), - {} - ); diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/table/field_value_cell.test.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/table/field_value_cell.test.tsx new file mode 100644 index 0000000000000..f6c43da2da8ac --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/event_details/table/field_value_cell.test.tsx @@ -0,0 +1,193 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { render, screen } from '@testing-library/react'; +import React from 'react'; + +import { BrowserField } from '../../../containers/source'; +import { FieldValueCell } from './field_value_cell'; +import { TestProviders } from '../../../mock'; +import { EventFieldsData } from '../types'; + +const contextId = 'test'; + +const eventId = 'TUWyf3wBFCFU0qRJTauW'; + +const hostIpData: EventFieldsData = { + aggregatable: true, + ariaRowindex: 35, + category: 'host', + description: 'Host ip addresses.', + example: '127.0.0.1', + field: 'host.ip', + fields: {}, + format: '', + indexes: ['auditbeat-*', 'filebeat-*', 'logs-*', 'winlogbeat-*'], + isObjectArray: false, + name: 'host.ip', + originalValue: ['127.0.0.1', '::1', '10.1.2.3', '2001:0DB8:AC10:FE01::'], + searchable: true, + type: 'ip', + values: ['127.0.0.1', '::1', '10.1.2.3', '2001:0DB8:AC10:FE01::'], +}; +const hostIpValues = ['127.0.0.1', '::1', '10.1.2.3', 'fe80::4001:aff:fec8:32']; + +describe('FieldValueCell', () => { + describe('common behavior', () => { + beforeEach(() => { + render( + + + + ); + }); + + test('it formats multiple values such that each value is displayed on a single line', () => { + expect(screen.getByTestId(`event-field-${hostIpData.field}`)).toHaveClass( + 'euiFlexGroup--directionColumn' + ); + }); + }); + + describe('when `BrowserField` metadata is NOT available', () => { + beforeEach(() => { + render( + + + + ); + }); + + test('it renders each of the expected values when `fieldFromBrowserField` is undefined', () => { + hostIpValues.forEach((value) => { + expect(screen.getByText(value)).toBeInTheDocument(); + }); + }); + + test('it renders values formatted as plain text (without `eventFieldsTable__fieldValue` formatting)', () => { + expect(screen.getByTestId(`event-field-${hostIpData.field}`).firstChild).not.toHaveClass( + 'eventFieldsTable__fieldValue' + ); + }); + }); + + describe('`message` field formatting', () => { + const messageData: EventFieldsData = { + aggregatable: false, + ariaRowindex: 50, + category: 'base', + description: + 'For log events the message field contains the log message, optimized for viewing in a log viewer. For structured logs without an original message field, other fields can be concatenated to form a human-readable summary of the event. If multiple messages exist, they can be combined into one message.', + example: 'Hello World', + field: 'message', + fields: {}, + format: '', + indexes: ['auditbeat-*', 'filebeat-*', 'logs-*', 'winlogbeat-*'], + isObjectArray: false, + name: 'message', + originalValue: ['Endpoint network event'], + searchable: true, + type: 'string', + values: ['Endpoint network event'], + }; + const messageValues = ['Endpoint network event']; + + const messageFieldFromBrowserField: BrowserField = { + aggregatable: false, + category: 'base', + description: + 'For log events the message field contains the log message, optimized for viewing in a log viewer. For structured logs without an original message field, other fields can be concatenated to form a human-readable summary of the event. If multiple messages exist, they can be combined into one message.', + example: 'Hello World', + fields: {}, + format: '', + indexes: ['auditbeat-*', 'filebeat-*', 'logs-*', 'winlogbeat-*'], + name: 'message', + searchable: true, + type: 'string', + }; + + beforeEach(() => { + render( + + + + ); + }); + + test('it renders special formatting for the `message` field', () => { + expect(screen.getByTestId('event-field-message')).toBeInTheDocument(); + }); + + test('it renders the expected message value', () => { + messageValues.forEach((value) => { + expect(screen.getByText(value)).toBeInTheDocument(); + }); + }); + }); + + describe('when `BrowserField` metadata IS available', () => { + const hostIpFieldFromBrowserField: BrowserField = { + aggregatable: true, + category: 'host', + description: 'Host ip addresses.', + example: '127.0.0.1', + fields: {}, + format: '', + indexes: ['auditbeat-*', 'filebeat-*', 'logs-*', 'winlogbeat-*'], + name: 'host.ip', + searchable: true, + type: 'ip', + }; + + beforeEach(() => { + render( + + + + ); + }); + + test('it renders values formatted with the expected class', () => { + expect(screen.getByTestId(`event-field-${hostIpData.field}`).firstChild).toHaveClass( + 'eventFieldsTable__fieldValue' + ); + }); + + test('it renders link buttons for each of the host ip addresses', () => { + expect(screen.getAllByRole('button').length).toBe(hostIpValues.length); + }); + + test('it renders each of the expected values when `fieldFromBrowserField` is provided', () => { + hostIpValues.forEach((value) => { + expect(screen.getByText(value)).toBeInTheDocument(); + }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/table/field_value_cell.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/table/field_value_cell.tsx index fc20f84d3650d..dc6c84b8138fe 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/table/field_value_cell.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/table/field_value_cell.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { EuiText } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; import { BrowserField } from '../../../containers/source'; import { OverflowField } from '../../tables/helpers'; import { FormattedFieldValue } from '../../../../timelines/components/timeline/body/renderers/formatted_field'; @@ -36,18 +36,28 @@ export const FieldValueCell = React.memo( values, }: FieldValueCellProps) => { return ( -
+ {values != null && values.map((value, i) => { if (fieldFromBrowserField == null) { return ( - - {value} - + + + {value} + + ); } return ( -
+ {data.field === MESSAGE_FIELD_NAME ? ( ) : ( @@ -63,10 +73,10 @@ export const FieldValueCell = React.memo( linkValue={(getLinkValue && getLinkValue(data.field)) ?? linkValue} /> )} -
+ ); })} -
+ ); } ); diff --git a/x-pack/plugins/security_solution/public/common/mock/mock_detail_item.ts b/x-pack/plugins/security_solution/public/common/mock/mock_detail_item.ts index 3712d389edeb1..035bdbbceff88 100644 --- a/x-pack/plugins/security_solution/public/common/mock/mock_detail_item.ts +++ b/x-pack/plugins/security_solution/public/common/mock/mock_detail_item.ts @@ -139,3 +139,191 @@ export const generateMockDetailItemData = (): TimelineEventsDetailsItem[] => [ ]; export const mockDetailItemData: TimelineEventsDetailsItem[] = generateMockDetailItemData(); + +export const rawEventData = { + _index: '.ds-logs-endpoint.events.network-default-2021.09.28-000001', + _id: 'TUWyf3wBFCFU0qRJTauW', + _score: 1, + _source: { + agent: { + id: '2ac9e9b3-f6d5-4ce6-915d-8f1f8f413624', + type: 'endpoint', + version: '8.0.0-SNAPSHOT', + }, + process: { + Ext: { + ancestry: [ + 'MmFjOWU5YjMtZjZkNS00Y2U2LTkxNWQtOGYxZjhmNDEzNjI0LTIyMzY0LTEzMjc4NjA2NTAyLjA=', + 'MmFjOWU5YjMtZjZkNS00Y2U2LTkxNWQtOGYxZjhmNDEzNjI0LTEtMTMyNzA3Njg2OTIuMA==', + ], + }, + name: 'filebeat', + pid: 22535, + entity_id: 'MmFjOWU5YjMtZjZkNS00Y2U2LTkxNWQtOGYxZjhmNDEzNjI0LTIyNTM1LTEzMjc4NjA2NTI4LjA=', + executable: + '/opt/Elastic/Agent/data/elastic-agent-058c40/install/filebeat-8.0.0-SNAPSHOT-linux-x86_64/filebeat', + }, + destination: { + address: '127.0.0.1', + port: 9200, + ip: '127.0.0.1', + }, + source: { + address: '127.0.0.1', + port: 54146, + ip: '127.0.0.1', + }, + message: 'Endpoint network event', + network: { + transport: 'tcp', + type: 'ipv4', + }, + '@timestamp': '2021-10-14T16:45:58.0310772Z', + ecs: { + version: '1.11.0', + }, + data_stream: { + namespace: 'default', + type: 'logs', + dataset: 'endpoint.events.network', + }, + elastic: { + agent: { + id: '12345', + }, + }, + host: { + hostname: 'test-linux-1', + os: { + Ext: { + variant: 'Debian', + }, + kernel: '4.19.0-17-cloud-amd64 #1 SMP Debian 4.19.194-2 (2021-06-21)', + name: 'Linux', + family: 'debian', + type: 'linux', + version: '10', + platform: 'debian', + full: 'Debian 10', + }, + ip: ['127.0.0.1', '::1', '10.1.2.3', '2001:0DB8:AC10:FE01::'], + name: 'test-linux-1', + id: '76ea303129f249aa7382338e4263eac1', + mac: ['aa:bb:cc:dd:ee:ff'], + architecture: 'x86_64', + }, + event: { + agent_id_status: 'verified', + sequence: 44872, + ingested: '2021-10-14T16:46:04Z', + created: '2021-10-14T16:45:58.0310772Z', + kind: 'event', + module: 'endpoint', + action: 'connection_attempted', + id: 'MKPXftjGeHiQzUNj++++nn6R', + category: ['network'], + type: ['start'], + dataset: 'endpoint.events.network', + outcome: 'unknown', + }, + user: { + Ext: { + real: { + name: 'root', + id: 0, + }, + }, + name: 'root', + id: 0, + }, + group: { + Ext: { + real: { + name: 'root', + id: 0, + }, + }, + name: 'root', + id: 0, + }, + }, + fields: { + 'host.os.full.text': ['Debian 10'], + 'event.category': ['network'], + 'process.name.text': ['filebeat'], + 'host.os.name.text': ['Linux'], + 'host.os.full': ['Debian 10'], + 'host.hostname': ['test-linux-1'], + 'process.pid': [22535], + 'host.mac': ['42:01:0a:c8:00:32'], + 'elastic.agent.id': ['abcdefg-f6d5-4ce6-915d-8f1f8f413624'], + 'host.os.version': ['10'], + 'host.os.name': ['Linux'], + 'source.ip': ['127.0.0.1'], + 'destination.address': ['127.0.0.1'], + 'host.name': ['test-linux-1'], + 'event.agent_id_status': ['verified'], + 'event.kind': ['event'], + 'event.outcome': ['unknown'], + 'group.name': ['root'], + 'user.id': ['0'], + 'host.os.type': ['linux'], + 'process.Ext.ancestry': [ + 'MmFjOWU5YjMtZjZkNS00Y2U2LTkxNWQtOGYxZjhmNDEzNjI0LTIyMzY0LTEzMjc4NjA2NTAyLjA=', + 'MmFjOWU5YjMtZjZkNS00Y2U2LTkxNWQtOGYxZjhmNDEzNjI0LTEtMTMyNzA3Njg2OTIuMA==', + ], + 'user.Ext.real.id': ['0'], + 'data_stream.type': ['logs'], + 'host.architecture': ['x86_64'], + 'process.name': ['filebeat'], + 'agent.id': ['2ac9e9b3-f6d5-4ce6-915d-8f1f8f413624'], + 'source.port': [54146], + 'ecs.version': ['1.11.0'], + 'event.created': ['2021-10-14T16:45:58.031Z'], + 'agent.version': ['8.0.0-SNAPSHOT'], + 'host.os.family': ['debian'], + 'destination.port': [9200], + 'group.id': ['0'], + 'user.name': ['root'], + 'source.address': ['127.0.0.1'], + 'process.entity_id': [ + 'MmFjOWU5YjMtZjZkNS00Y2U2LTkxNWQtOGYxZjhmNDEzNjI0LTIyNTM1LTEzMjc4NjA2NTI4LjA=', + ], + 'host.ip': ['127.0.0.1', '::1', '10.1.2.3', '2001:0DB8:AC10:FE01::'], + 'process.executable.caseless': [ + '/opt/elastic/agent/data/elastic-agent-058c40/install/filebeat-8.0.0-snapshot-linux-x86_64/filebeat', + ], + 'event.sequence': [44872], + 'agent.type': ['endpoint'], + 'process.executable.text': [ + '/opt/Elastic/Agent/data/elastic-agent-058c40/install/filebeat-8.0.0-SNAPSHOT-linux-x86_64/filebeat', + ], + 'group.Ext.real.name': ['root'], + 'event.module': ['endpoint'], + 'host.os.kernel': ['4.19.0-17-cloud-amd64 #1 SMP Debian 4.19.194-2 (2021-06-21)'], + 'host.os.full.caseless': ['debian 10'], + 'host.id': ['76ea303129f249aa7382338e4263eac1'], + 'process.name.caseless': ['filebeat'], + 'network.type': ['ipv4'], + 'process.executable': [ + '/opt/Elastic/Agent/data/elastic-agent-058c40/install/filebeat-8.0.0-SNAPSHOT-linux-x86_64/filebeat', + ], + 'user.Ext.real.name': ['root'], + 'data_stream.namespace': ['default'], + message: ['Endpoint network event'], + 'destination.ip': ['127.0.0.1'], + 'network.transport': ['tcp'], + 'host.os.Ext.variant': ['Debian'], + 'group.Ext.real.id': ['0'], + 'event.ingested': ['2021-10-14T16:46:04.000Z'], + 'event.action': ['connection_attempted'], + '@timestamp': ['2021-10-14T16:45:58.031Z'], + 'host.os.platform': ['debian'], + 'data_stream.dataset': ['endpoint.events.network'], + 'event.type': ['start'], + 'event.id': ['MKPXftjGeHiQzUNj++++nn6R'], + 'host.os.name.caseless': ['linux'], + 'event.dataset': ['endpoint.events.network'], + 'user.name.text': ['root'], + }, +}; diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/expandable_event.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/expandable_event.tsx index 17d43d80a5a9a..6a7f0602c3675 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/expandable_event.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/expandable_event.tsx @@ -33,6 +33,7 @@ interface Props { isDraggable?: boolean; loading: boolean; messageHeight?: number; + rawEventData: object | undefined; timelineTabType: TimelineTabs | 'flyout'; timelineId: string; hostRisk: HostRisk | null; @@ -93,6 +94,7 @@ export const ExpandableEvent = React.memo( loading, detailsData, hostRisk, + rawEventData, }) => { if (!event.eventId) { return {i18n.EVENT_DETAILS_PLACEHOLDER}; @@ -111,6 +113,7 @@ export const ExpandableEvent = React.memo( id={event.eventId} isAlert={isAlert} isDraggable={isDraggable} + rawEventData={rawEventData} timelineId={timelineId} timelineTabType={timelineTabType} hostRisk={hostRisk} diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx index f8786e0706834..b9d7e0a8c024f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx @@ -79,7 +79,7 @@ const EventDetailsPanelComponent: React.FC = ({ tabType, timelineId, }) => { - const [loading, detailsData] = useTimelineEventsDetails({ + const [loading, detailsData, rawEventData] = useTimelineEventsDetails({ docValueFields, entityType, indexName: expandedEvent.indexName ?? '', @@ -195,6 +195,7 @@ const EventDetailsPanelComponent: React.FC = ({ isAlert={isAlert} isDraggable={isDraggable} loading={loading} + rawEventData={rawEventData} timelineId={timelineId} timelineTabType="flyout" hostRisk={hostRisk} @@ -228,6 +229,7 @@ const EventDetailsPanelComponent: React.FC = ({ isAlert={isAlert} isDraggable={isDraggable} loading={loading} + rawEventData={rawEventData} timelineId={timelineId} timelineTabType={tabType} hostRisk={hostRisk} diff --git a/x-pack/plugins/security_solution/public/timelines/containers/details/index.tsx b/x-pack/plugins/security_solution/public/timelines/containers/details/index.tsx index e59eaeed4f2a6..f05966bd97870 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/details/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/containers/details/index.tsx @@ -42,7 +42,7 @@ export const useTimelineEventsDetails = ({ indexName, eventId, skip, -}: UseTimelineEventsDetailsProps): [boolean, EventsArgs['detailsData']] => { +}: UseTimelineEventsDetailsProps): [boolean, EventsArgs['detailsData'], object | undefined] => { const { data } = useKibana().services; const refetch = useRef(noop); const abortCtrl = useRef(new AbortController()); @@ -55,6 +55,8 @@ export const useTimelineEventsDetails = ({ const [timelineDetailsResponse, setTimelineDetailsResponse] = useState(null); + const [rawEventData, setRawEventData] = useState(undefined); + const timelineDetailsSearch = useCallback( (request: TimelineEventsDetailsRequestOptions | null) => { if (request == null || skip || isEmpty(request.eventId)) { @@ -78,6 +80,7 @@ export const useTimelineEventsDetails = ({ if (isCompleteResponse(response)) { setLoading(false); setTimelineDetailsResponse(response.data || []); + setRawEventData(response.rawResponse.hits.hits[0]); searchSubscription$.current.unsubscribe(); } else if (isErrorResponse(response)) { setLoading(false); @@ -125,5 +128,5 @@ export const useTimelineEventsDetails = ({ }; }, [timelineDetailsRequest, timelineDetailsSearch]); - return [loading, timelineDetailsResponse]; + return [loading, timelineDetailsResponse, rawEventData]; }; diff --git a/x-pack/plugins/timelines/common/search_strategy/timeline/events/details/index.ts b/x-pack/plugins/timelines/common/search_strategy/timeline/events/details/index.ts index 5bceb31081687..f9f6a2ea57917 100644 --- a/x-pack/plugins/timelines/common/search_strategy/timeline/events/details/index.ts +++ b/x-pack/plugins/timelines/common/search_strategy/timeline/events/details/index.ts @@ -24,6 +24,7 @@ export interface TimelineEventsDetailsItem { export interface TimelineEventsDetailsStrategyResponse extends IEsSearchResponse { data?: Maybe; inspect?: Maybe; + rawEventData?: Maybe; } export interface TimelineEventsDetailsRequestOptions diff --git a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/details/index.ts b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/details/index.ts index c82d9af938a98..b60add2515ec9 100644 --- a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/details/index.ts +++ b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/details/index.ts @@ -57,10 +57,14 @@ export const timelineEventsDetails: TimelineFactory Date: Tue, 19 Oct 2021 12:14:57 +0200 Subject: [PATCH 044/204] Allow elastic/fleet-server to call appropriate Fleet APIs (#113932) --- x-pack/plugins/fleet/server/mocks/index.ts | 12 +- x-pack/plugins/fleet/server/plugin.ts | 55 ++++-- .../fleet/server/routes/agent_policy/index.ts | 27 +-- .../routes/enrollment_api_key/handler.ts | 6 +- .../server/routes/enrollment_api_key/index.ts | 21 ++- .../plugins/fleet/server/routes/epm/index.ts | 39 ++-- .../fleet/server/routes/security.test.ts | 175 ++++++++++++++++++ .../plugins/fleet/server/routes/security.ts | 135 +++++++++++--- .../fleet/server/routes/setup/handlers.ts | 8 +- .../fleet/server/routes/setup/index.ts | 21 +-- .../fleet/server/types/request_context.ts | 7 + .../authorization/check_privileges.test.ts | 108 +++++++++++ .../server/authorization/check_privileges.ts | 31 +++- .../check_privileges_dynamically.test.ts | 22 ++- .../check_privileges_dynamically.ts | 14 +- .../security/server/authorization/types.ts | 26 ++- .../apis/security/privileges.ts | 2 +- .../apis/security/privileges_basic.ts | 2 +- .../apis/agents/services.ts | 26 ++- .../fleet_api_integration/apis/epm/setup.ts | 45 +++++ 20 files changed, 651 insertions(+), 131 deletions(-) create mode 100644 x-pack/plugins/fleet/server/routes/security.test.ts diff --git a/x-pack/plugins/fleet/server/mocks/index.ts b/x-pack/plugins/fleet/server/mocks/index.ts index c7f6b6fefc414..e6577426974a3 100644 --- a/x-pack/plugins/fleet/server/mocks/index.ts +++ b/x-pack/plugins/fleet/server/mocks/index.ts @@ -23,7 +23,17 @@ import type { FleetAppContext } from '../plugin'; // Export all mocks from artifacts export * from '../services/artifacts/mocks'; -export const createAppContextStartContractMock = (): FleetAppContext => { +export interface MockedFleetAppContext extends FleetAppContext { + elasticsearch: ReturnType; + data: ReturnType; + encryptedSavedObjectsStart?: ReturnType; + savedObjects: ReturnType; + securitySetup?: ReturnType; + securityStart?: ReturnType; + logger: ReturnType['get']>; +} + +export const createAppContextStartContractMock = (): MockedFleetAppContext => { const config = { agents: { enabled: true, elasticsearch: {} }, enabled: true, diff --git a/x-pack/plugins/fleet/server/plugin.ts b/x-pack/plugins/fleet/server/plugin.ts index aaee24b39685a..8a95065380b69 100644 --- a/x-pack/plugins/fleet/server/plugin.ts +++ b/x-pack/plugins/fleet/server/plugin.ts @@ -80,9 +80,10 @@ import { } from './services/agents'; import { registerFleetUsageCollector } from './collectors/register'; import { getInstallation, ensureInstalledPackage } from './services/epm/packages'; -import { makeRouterEnforcingSuperuser } from './routes/security'; +import { RouterWrappers } from './routes/security'; import { startFleetServerSetup } from './services/fleet_server'; import { FleetArtifactsClient } from './services/artifacts'; +import type { FleetRouter } from './types/request_context'; export interface FleetSetupDeps { licensing: LicensingPluginSetup; @@ -206,6 +207,24 @@ export class FleetPlugin category: DEFAULT_APP_CATEGORIES.management, app: [PLUGIN_ID, INTEGRATIONS_PLUGIN_ID, 'kibana'], catalogue: ['fleet'], + reserved: { + description: + 'Privilege to setup Fleet packages and configured policies. Intended for use by the elastic/fleet-server service account only.', + privileges: [ + { + id: 'fleet-setup', + privilege: { + excludeFromBasePrivileges: true, + api: ['fleet-setup'], + savedObject: { + all: [], + read: [], + }, + ui: [], + }, + }, + ], + }, privileges: { all: { api: [`${PLUGIN_ID}-read`, `${PLUGIN_ID}-all`], @@ -245,7 +264,7 @@ export class FleetPlugin }) ); - const router = core.http.createRouter(); + const router: FleetRouter = core.http.createRouter(); // Register usage collection registerFleetUsageCollector(core, config, deps.usageCollection); @@ -254,24 +273,34 @@ export class FleetPlugin registerAppRoutes(router); // Allow read-only users access to endpoints necessary for Integrations UI // Only some endpoints require superuser so we pass a raw IRouter here - registerEPMRoutes(router); // For all the routes we enforce the user to have role superuser - const routerSuperuserOnly = makeRouterEnforcingSuperuser(router); + const superuserRouter = RouterWrappers.require.superuser(router); + const fleetSetupRouter = RouterWrappers.require.fleetSetupPrivilege(router); + + // Some EPM routes use regular rbac to support integrations app + registerEPMRoutes({ rbac: router, superuser: superuserRouter }); + // Register rest of routes only if security is enabled if (deps.security) { - registerSetupRoutes(routerSuperuserOnly, config); - registerAgentPolicyRoutes(routerSuperuserOnly); - registerPackagePolicyRoutes(routerSuperuserOnly); - registerOutputRoutes(routerSuperuserOnly); - registerSettingsRoutes(routerSuperuserOnly); - registerDataStreamRoutes(routerSuperuserOnly); - registerPreconfigurationRoutes(routerSuperuserOnly); + registerSetupRoutes(fleetSetupRouter, config); + registerAgentPolicyRoutes({ + fleetSetup: fleetSetupRouter, + superuser: superuserRouter, + }); + registerPackagePolicyRoutes(superuserRouter); + registerOutputRoutes(superuserRouter); + registerSettingsRoutes(superuserRouter); + registerDataStreamRoutes(superuserRouter); + registerPreconfigurationRoutes(superuserRouter); // Conditional config routes if (config.agents.enabled) { - registerAgentAPIRoutes(routerSuperuserOnly, config); - registerEnrollmentApiKeyRoutes(routerSuperuserOnly); + registerAgentAPIRoutes(superuserRouter, config); + registerEnrollmentApiKeyRoutes({ + fleetSetup: fleetSetupRouter, + superuser: superuserRouter, + }); } } } diff --git a/x-pack/plugins/fleet/server/routes/agent_policy/index.ts b/x-pack/plugins/fleet/server/routes/agent_policy/index.ts index a66a9ab9cadc7..4c20358e15085 100644 --- a/x-pack/plugins/fleet/server/routes/agent_policy/index.ts +++ b/x-pack/plugins/fleet/server/routes/agent_policy/index.ts @@ -5,8 +5,6 @@ * 2.0. */ -import type { IRouter } from 'src/core/server'; - import { PLUGIN_ID, AGENT_POLICY_API_ROUTES } from '../../constants'; import { GetAgentPoliciesRequestSchema, @@ -17,6 +15,7 @@ import { DeleteAgentPolicyRequestSchema, GetFullAgentPolicyRequestSchema, } from '../../types'; +import type { FleetRouter } from '../../types/request_context'; import { getAgentPoliciesHandler, @@ -29,19 +28,21 @@ import { downloadFullAgentPolicy, } from './handlers'; -export const registerRoutes = (router: IRouter) => { - // List - router.get( +export const registerRoutes = (routers: { superuser: FleetRouter; fleetSetup: FleetRouter }) => { + // List - Fleet Server needs access to run setup + routers.fleetSetup.get( { path: AGENT_POLICY_API_ROUTES.LIST_PATTERN, validate: GetAgentPoliciesRequestSchema, - options: { tags: [`access:${PLUGIN_ID}-read`] }, + // Disable this tag and the automatic RBAC support until elastic/fleet-server access is removed in 8.0 + // Required to allow elastic/fleet-server to access this API. + // options: { tags: [`access:${PLUGIN_ID}-read`] }, }, getAgentPoliciesHandler ); // Get one - router.get( + routers.superuser.get( { path: AGENT_POLICY_API_ROUTES.INFO_PATTERN, validate: GetOneAgentPolicyRequestSchema, @@ -51,7 +52,7 @@ export const registerRoutes = (router: IRouter) => { ); // Create - router.post( + routers.superuser.post( { path: AGENT_POLICY_API_ROUTES.CREATE_PATTERN, validate: CreateAgentPolicyRequestSchema, @@ -61,7 +62,7 @@ export const registerRoutes = (router: IRouter) => { ); // Update - router.put( + routers.superuser.put( { path: AGENT_POLICY_API_ROUTES.UPDATE_PATTERN, validate: UpdateAgentPolicyRequestSchema, @@ -71,7 +72,7 @@ export const registerRoutes = (router: IRouter) => { ); // Copy - router.post( + routers.superuser.post( { path: AGENT_POLICY_API_ROUTES.COPY_PATTERN, validate: CopyAgentPolicyRequestSchema, @@ -81,7 +82,7 @@ export const registerRoutes = (router: IRouter) => { ); // Delete - router.post( + routers.superuser.post( { path: AGENT_POLICY_API_ROUTES.DELETE_PATTERN, validate: DeleteAgentPolicyRequestSchema, @@ -91,7 +92,7 @@ export const registerRoutes = (router: IRouter) => { ); // Get one full agent policy - router.get( + routers.superuser.get( { path: AGENT_POLICY_API_ROUTES.FULL_INFO_PATTERN, validate: GetFullAgentPolicyRequestSchema, @@ -101,7 +102,7 @@ export const registerRoutes = (router: IRouter) => { ); // Download one full agent policy - router.get( + routers.superuser.get( { path: AGENT_POLICY_API_ROUTES.FULL_INFO_DOWNLOAD_PATTERN, validate: GetFullAgentPolicyRequestSchema, diff --git a/x-pack/plugins/fleet/server/routes/enrollment_api_key/handler.ts b/x-pack/plugins/fleet/server/routes/enrollment_api_key/handler.ts index 0959a9a88704a..9cb07a9050f83 100644 --- a/x-pack/plugins/fleet/server/routes/enrollment_api_key/handler.ts +++ b/x-pack/plugins/fleet/server/routes/enrollment_api_key/handler.ts @@ -27,7 +27,8 @@ export const getEnrollmentApiKeysHandler: RequestHandler< undefined, TypeOf > = async (context, request, response) => { - const esClient = context.core.elasticsearch.client.asCurrentUser; + // Use kibana_system and depend on authz checks on HTTP layer to prevent abuse + const esClient = context.core.elasticsearch.client.asInternalUser; try { const { items, total, page, perPage } = await APIKeyService.listEnrollmentApiKeys(esClient, { @@ -87,7 +88,8 @@ export const deleteEnrollmentApiKeyHandler: RequestHandler< export const getOneEnrollmentApiKeyHandler: RequestHandler< TypeOf > = async (context, request, response) => { - const esClient = context.core.elasticsearch.client.asCurrentUser; + // Use kibana_system and depend on authz checks on HTTP layer to prevent abuse + const esClient = context.core.elasticsearch.client.asInternalUser; try { const apiKey = await APIKeyService.getEnrollmentAPIKey(esClient, request.params.keyId); const body: GetOneEnrollmentAPIKeyResponse = { item: apiKey }; diff --git a/x-pack/plugins/fleet/server/routes/enrollment_api_key/index.ts b/x-pack/plugins/fleet/server/routes/enrollment_api_key/index.ts index b37a88e70e085..6429d4d29d5c9 100644 --- a/x-pack/plugins/fleet/server/routes/enrollment_api_key/index.ts +++ b/x-pack/plugins/fleet/server/routes/enrollment_api_key/index.ts @@ -5,8 +5,6 @@ * 2.0. */ -import type { IRouter } from 'src/core/server'; - import { PLUGIN_ID, ENROLLMENT_API_KEY_ROUTES } from '../../constants'; import { GetEnrollmentAPIKeysRequestSchema, @@ -14,6 +12,7 @@ import { DeleteEnrollmentAPIKeyRequestSchema, PostEnrollmentAPIKeyRequestSchema, } from '../../types'; +import type { FleetRouter } from '../../types/request_context'; import { getEnrollmentApiKeysHandler, @@ -22,17 +21,19 @@ import { postEnrollmentApiKeyHandler, } from './handler'; -export const registerRoutes = (router: IRouter) => { - router.get( +export const registerRoutes = (routers: { superuser: FleetRouter; fleetSetup: FleetRouter }) => { + routers.fleetSetup.get( { path: ENROLLMENT_API_KEY_ROUTES.INFO_PATTERN, validate: GetOneEnrollmentAPIKeyRequestSchema, - options: { tags: [`access:${PLUGIN_ID}-read`] }, + // Disable this tag and the automatic RBAC support until elastic/fleet-server access is removed in 8.0 + // Required to allow elastic/fleet-server to access this API. + // options: { tags: [`access:${PLUGIN_ID}-read`] }, }, getOneEnrollmentApiKeyHandler ); - router.delete( + routers.superuser.delete( { path: ENROLLMENT_API_KEY_ROUTES.DELETE_PATTERN, validate: DeleteEnrollmentAPIKeyRequestSchema, @@ -41,16 +42,18 @@ export const registerRoutes = (router: IRouter) => { deleteEnrollmentApiKeyHandler ); - router.get( + routers.fleetSetup.get( { path: ENROLLMENT_API_KEY_ROUTES.LIST_PATTERN, validate: GetEnrollmentAPIKeysRequestSchema, - options: { tags: [`access:${PLUGIN_ID}-read`] }, + // Disable this tag and the automatic RBAC support until elastic/fleet-server access is removed in 8.0 + // Required to allow elastic/fleet-server to access this API. + // options: { tags: [`access:${PLUGIN_ID}-read`] }, }, getEnrollmentApiKeysHandler ); - router.post( + routers.superuser.post( { path: ENROLLMENT_API_KEY_ROUTES.CREATE_PATTERN, validate: PostEnrollmentAPIKeyRequestSchema, diff --git a/x-pack/plugins/fleet/server/routes/epm/index.ts b/x-pack/plugins/fleet/server/routes/epm/index.ts index 360f2ec1d446e..a2f2df4a00c55 100644 --- a/x-pack/plugins/fleet/server/routes/epm/index.ts +++ b/x-pack/plugins/fleet/server/routes/epm/index.ts @@ -5,10 +5,7 @@ * 2.0. */ -import type { IRouter } from 'src/core/server'; - import { PLUGIN_ID, EPM_API_ROUTES } from '../../constants'; -import type { FleetRequestHandlerContext } from '../../types'; import { GetCategoriesRequestSchema, GetPackagesRequestSchema, @@ -21,7 +18,7 @@ import { GetStatsRequestSchema, UpdatePackageRequestSchema, } from '../../types'; -import { enforceSuperUser } from '../security'; +import type { FleetRouter } from '../../types/request_context'; import { getCategoriesHandler, @@ -39,8 +36,8 @@ import { const MAX_FILE_SIZE_BYTES = 104857600; // 100MB -export const registerRoutes = (router: IRouter) => { - router.get( +export const registerRoutes = (routers: { rbac: FleetRouter; superuser: FleetRouter }) => { + routers.rbac.get( { path: EPM_API_ROUTES.CATEGORIES_PATTERN, validate: GetCategoriesRequestSchema, @@ -49,7 +46,7 @@ export const registerRoutes = (router: IRouter) => { getCategoriesHandler ); - router.get( + routers.rbac.get( { path: EPM_API_ROUTES.LIST_PATTERN, validate: GetPackagesRequestSchema, @@ -58,7 +55,7 @@ export const registerRoutes = (router: IRouter) => { getListHandler ); - router.get( + routers.rbac.get( { path: EPM_API_ROUTES.LIMITED_LIST_PATTERN, validate: false, @@ -67,7 +64,7 @@ export const registerRoutes = (router: IRouter) => { getLimitedListHandler ); - router.get( + routers.rbac.get( { path: EPM_API_ROUTES.STATS_PATTERN, validate: GetStatsRequestSchema, @@ -76,7 +73,7 @@ export const registerRoutes = (router: IRouter) => { getStatsHandler ); - router.get( + routers.rbac.get( { path: EPM_API_ROUTES.FILEPATH_PATTERN, validate: GetFileRequestSchema, @@ -85,7 +82,7 @@ export const registerRoutes = (router: IRouter) => { getFileHandler ); - router.get( + routers.rbac.get( { path: EPM_API_ROUTES.INFO_PATTERN, validate: GetInfoRequestSchema, @@ -94,34 +91,34 @@ export const registerRoutes = (router: IRouter) => { getInfoHandler ); - router.put( + routers.superuser.put( { path: EPM_API_ROUTES.INFO_PATTERN, validate: UpdatePackageRequestSchema, options: { tags: [`access:${PLUGIN_ID}-all`] }, }, - enforceSuperUser(updatePackageHandler) + updatePackageHandler ); - router.post( + routers.superuser.post( { path: EPM_API_ROUTES.INSTALL_FROM_REGISTRY_PATTERN, validate: InstallPackageFromRegistryRequestSchema, options: { tags: [`access:${PLUGIN_ID}-all`] }, }, - enforceSuperUser(installPackageFromRegistryHandler) + installPackageFromRegistryHandler ); - router.post( + routers.superuser.post( { path: EPM_API_ROUTES.BULK_INSTALL_PATTERN, validate: BulkUpgradePackagesFromRegistryRequestSchema, options: { tags: [`access:${PLUGIN_ID}-all`] }, }, - enforceSuperUser(bulkInstallPackagesFromRegistryHandler) + bulkInstallPackagesFromRegistryHandler ); - router.post( + routers.superuser.post( { path: EPM_API_ROUTES.INSTALL_BY_UPLOAD_PATTERN, validate: InstallPackageByUploadRequestSchema, @@ -134,15 +131,15 @@ export const registerRoutes = (router: IRouter) => { }, }, }, - enforceSuperUser(installPackageByUploadHandler) + installPackageByUploadHandler ); - router.delete( + routers.superuser.delete( { path: EPM_API_ROUTES.DELETE_PATTERN, validate: DeletePackageRequestSchema, options: { tags: [`access:${PLUGIN_ID}-all`] }, }, - enforceSuperUser(deletePackageHandler) + deletePackageHandler ); }; diff --git a/x-pack/plugins/fleet/server/routes/security.test.ts b/x-pack/plugins/fleet/server/routes/security.test.ts new file mode 100644 index 0000000000000..80ea142541530 --- /dev/null +++ b/x-pack/plugins/fleet/server/routes/security.test.ts @@ -0,0 +1,175 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { IRouter, RequestHandler, RouteConfig } from '../../../../../src/core/server'; +import { coreMock } from '../../../../../src/core/server/mocks'; +import type { AuthenticatedUser } from '../../../security/server'; +import type { CheckPrivilegesDynamically } from '../../../security/server/authorization/check_privileges_dynamically'; +import { createAppContextStartContractMock } from '../mocks'; +import { appContextService } from '../services'; + +import type { RouterWrapper } from './security'; +import { RouterWrappers } from './security'; + +describe('RouterWrappers', () => { + const runTest = async ({ + wrapper, + security: { + roles = [], + pluginEnabled = true, + licenseEnabled = true, + checkPrivilegesDynamically, + } = {}, + }: { + wrapper: RouterWrapper; + security?: { + roles?: string[]; + pluginEnabled?: boolean; + licenseEnabled?: boolean; + checkPrivilegesDynamically?: CheckPrivilegesDynamically; + }; + }) => { + const fakeRouter = { + get: jest.fn(), + } as unknown as jest.Mocked; + const fakeHandler: RequestHandler = jest.fn((ctx, req, res) => res.ok()); + + const mockContext = createAppContextStartContractMock(); + // @ts-expect-error type doesn't properly respect deeply mocked keys + mockContext.securityStart?.authz.actions.api.get.mockImplementation((priv) => `api:${priv}`); + + if (!pluginEnabled) { + mockContext.securitySetup = undefined; + mockContext.securityStart = undefined; + } else { + mockContext.securityStart?.authc.getCurrentUser.mockReturnValue({ + username: 'foo', + roles, + } as unknown as AuthenticatedUser); + + mockContext.securitySetup?.license.isEnabled.mockReturnValue(licenseEnabled); + if (licenseEnabled) { + mockContext.securityStart?.authz.mode.useRbacForRequest.mockReturnValue(true); + } + + if (checkPrivilegesDynamically) { + mockContext.securityStart?.authz.checkPrivilegesDynamicallyWithRequest.mockReturnValue( + checkPrivilegesDynamically + ); + } + } + + appContextService.start(mockContext); + + const wrappedRouter = wrapper(fakeRouter); + wrappedRouter.get({} as RouteConfig, fakeHandler); + const wrappedHandler = fakeRouter.get.mock.calls[0][1]; + const resFactory = { forbidden: jest.fn(() => 'forbidden'), ok: jest.fn(() => 'ok') }; + const res = await wrappedHandler( + { core: coreMock.createRequestHandlerContext() }, + {} as any, + resFactory as any + ); + + return res as unknown as 'forbidden' | 'ok'; + }; + + describe('require.superuser', () => { + it('allow users with the superuser role', async () => { + expect( + await runTest({ + wrapper: RouterWrappers.require.superuser, + security: { roles: ['superuser'] }, + }) + ).toEqual('ok'); + }); + + it('does not allow users without the superuser role', async () => { + expect( + await runTest({ + wrapper: RouterWrappers.require.superuser, + security: { roles: ['foo'] }, + }) + ).toEqual('forbidden'); + }); + + it('does not allow security plugin to be disabled', async () => { + expect( + await runTest({ + wrapper: RouterWrappers.require.superuser, + security: { pluginEnabled: false }, + }) + ).toEqual('forbidden'); + }); + + it('does not allow security license to be disabled', async () => { + expect( + await runTest({ + wrapper: RouterWrappers.require.superuser, + security: { licenseEnabled: false }, + }) + ).toEqual('forbidden'); + }); + }); + + describe('require.fleetSetupPrivilege', () => { + const mockCheckPrivileges: jest.Mock< + ReturnType, + Parameters + > = jest.fn().mockResolvedValue({ hasAllRequested: true }); + + it('executes custom authz check', async () => { + await runTest({ + wrapper: RouterWrappers.require.fleetSetupPrivilege, + security: { checkPrivilegesDynamically: mockCheckPrivileges }, + }); + expect(mockCheckPrivileges).toHaveBeenCalledWith( + { kibana: ['api:fleet-setup'] }, + { + requireLoginAction: false, + } + ); + }); + + it('allow users with required privileges', async () => { + expect( + await runTest({ + wrapper: RouterWrappers.require.fleetSetupPrivilege, + security: { checkPrivilegesDynamically: mockCheckPrivileges }, + }) + ).toEqual('ok'); + }); + + it('does not allow users without required privileges', async () => { + mockCheckPrivileges.mockResolvedValueOnce({ hasAllRequested: false } as any); + expect( + await runTest({ + wrapper: RouterWrappers.require.fleetSetupPrivilege, + security: { checkPrivilegesDynamically: mockCheckPrivileges }, + }) + ).toEqual('forbidden'); + }); + + it('does not allow security plugin to be disabled', async () => { + expect( + await runTest({ + wrapper: RouterWrappers.require.fleetSetupPrivilege, + security: { pluginEnabled: false }, + }) + ).toEqual('forbidden'); + }); + + it('does not allow security license to be disabled', async () => { + expect( + await runTest({ + wrapper: RouterWrappers.require.fleetSetupPrivilege, + security: { licenseEnabled: false }, + }) + ).toEqual('forbidden'); + }); + }); +}); diff --git a/x-pack/plugins/fleet/server/routes/security.ts b/x-pack/plugins/fleet/server/routes/security.ts index 33a510c27f04e..8a67a7066742a 100644 --- a/x-pack/plugins/fleet/server/routes/security.ts +++ b/x-pack/plugins/fleet/server/routes/security.ts @@ -5,56 +5,137 @@ * 2.0. */ -import type { IRouter, RequestHandler, RequestHandlerContext } from 'src/core/server'; +import type { + IRouter, + KibanaRequest, + RequestHandler, + RequestHandlerContext, +} from 'src/core/server'; import { appContextService } from '../services'; -export function enforceSuperUser( +const SUPERUSER_AUTHZ_MESSAGE = + 'Access to Fleet API requires the superuser role and for stack security features to be enabled.'; + +function checkSecurityEnabled() { + return appContextService.hasSecurity() && appContextService.getSecurityLicense().isEnabled(); +} + +function checkSuperuser(req: KibanaRequest) { + if (!checkSecurityEnabled()) { + return false; + } + + const security = appContextService.getSecurity(); + const user = security.authc.getCurrentUser(req); + if (!user) { + return false; + } + + const userRoles = user.roles || []; + if (!userRoles.includes('superuser')) { + return false; + } + + return true; +} + +function enforceSuperuser( handler: RequestHandler ): RequestHandler { return function enforceSuperHandler(context, req, res) { - if (!appContextService.hasSecurity() || !appContextService.getSecurityLicense().isEnabled()) { + const isSuperuser = checkSuperuser(req); + if (!isSuperuser) { return res.forbidden({ body: { - message: `Access to this API requires that security is enabled`, + message: SUPERUSER_AUTHZ_MESSAGE, }, }); } - const security = appContextService.getSecurity(); - const user = security.authc.getCurrentUser(req); - if (!user) { - return res.forbidden({ - body: { - message: - 'Access to Fleet API require the superuser role, and for stack security features to be enabled.', - }, - }); - } + return handler(context, req, res); + }; +} - const userRoles = user.roles || []; - if (!userRoles.includes('superuser')) { - return res.forbidden({ - body: { - message: 'Access to Fleet API require the superuser role.', - }, - }); +function makeRouterEnforcingSuperuser( + router: IRouter +): IRouter { + return { + get: (options, handler) => router.get(options, enforceSuperuser(handler)), + delete: (options, handler) => router.delete(options, enforceSuperuser(handler)), + post: (options, handler) => router.post(options, enforceSuperuser(handler)), + put: (options, handler) => router.put(options, enforceSuperuser(handler)), + patch: (options, handler) => router.patch(options, enforceSuperuser(handler)), + handleLegacyErrors: (handler) => router.handleLegacyErrors(handler), + getRoutes: () => router.getRoutes(), + routerPath: router.routerPath, + }; +} + +async function checkFleetSetupPrivilege(req: KibanaRequest) { + if (!checkSecurityEnabled()) { + return false; + } + + const security = appContextService.getSecurity(); + + if (security.authz.mode.useRbacForRequest(req)) { + const checkPrivileges = security.authz.checkPrivilegesDynamicallyWithRequest(req); + const { hasAllRequested } = await checkPrivileges( + { kibana: [security.authz.actions.api.get('fleet-setup')] }, + { requireLoginAction: false } // exclude login access requirement + ); + + return !!hasAllRequested; + } + + return true; +} + +function enforceFleetSetupPrivilege( + handler: RequestHandler +): RequestHandler { + return async (context, req, res) => { + const hasFleetSetupPrivilege = await checkFleetSetupPrivilege(req); + if (!hasFleetSetupPrivilege) { + return res.forbidden({ body: { message: SUPERUSER_AUTHZ_MESSAGE } }); } + return handler(context, req, res); }; } -export function makeRouterEnforcingSuperuser( +function makeRouterEnforcingFleetSetupPrivilege( router: IRouter ): IRouter { return { - get: (options, handler) => router.get(options, enforceSuperUser(handler)), - delete: (options, handler) => router.delete(options, enforceSuperUser(handler)), - post: (options, handler) => router.post(options, enforceSuperUser(handler)), - put: (options, handler) => router.put(options, enforceSuperUser(handler)), - patch: (options, handler) => router.patch(options, enforceSuperUser(handler)), + get: (options, handler) => router.get(options, enforceFleetSetupPrivilege(handler)), + delete: (options, handler) => router.delete(options, enforceFleetSetupPrivilege(handler)), + post: (options, handler) => router.post(options, enforceFleetSetupPrivilege(handler)), + put: (options, handler) => router.put(options, enforceFleetSetupPrivilege(handler)), + patch: (options, handler) => router.patch(options, enforceFleetSetupPrivilege(handler)), handleLegacyErrors: (handler) => router.handleLegacyErrors(handler), getRoutes: () => router.getRoutes(), routerPath: router.routerPath, }; } + +export type RouterWrapper = (route: IRouter) => IRouter; + +interface RouterWrappersSetup { + require: { + superuser: RouterWrapper; + fleetSetupPrivilege: RouterWrapper; + }; +} + +export const RouterWrappers: RouterWrappersSetup = { + require: { + superuser: (router) => { + return makeRouterEnforcingSuperuser(router); + }, + fleetSetupPrivilege: (router) => { + return makeRouterEnforcingFleetSetupPrivilege(router); + }, + }, +}; diff --git a/x-pack/plugins/fleet/server/routes/setup/handlers.ts b/x-pack/plugins/fleet/server/routes/setup/handlers.ts index c5b2ef0ade26f..fad5d93c3f5d5 100644 --- a/x-pack/plugins/fleet/server/routes/setup/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/setup/handlers.ts @@ -5,8 +5,6 @@ * 2.0. */ -import type { RequestHandler } from 'src/core/server'; - import { appContextService } from '../../services'; import type { GetFleetStatusResponse, PostFleetSetupResponse } from '../../../common'; import { setupFleet } from '../../services/setup'; @@ -14,12 +12,14 @@ import { hasFleetServers } from '../../services/fleet_server'; import { defaultIngestErrorHandler } from '../../errors'; import type { FleetRequestHandler } from '../../types'; -export const getFleetStatusHandler: RequestHandler = async (context, request, response) => { +export const getFleetStatusHandler: FleetRequestHandler = async (context, request, response) => { try { const isApiKeysEnabled = await appContextService .getSecurity() .authc.apiKeys.areAPIKeysEnabled(); - const isFleetServerSetup = await hasFleetServers(appContextService.getInternalUserESClient()); + const isFleetServerSetup = await hasFleetServers( + context.core.elasticsearch.client.asInternalUser + ); const missingRequirements: GetFleetStatusResponse['missing_requirements'] = []; if (!isApiKeysEnabled) { diff --git a/x-pack/plugins/fleet/server/routes/setup/index.ts b/x-pack/plugins/fleet/server/routes/setup/index.ts index 591b9c832172d..d191f1b78e9ae 100644 --- a/x-pack/plugins/fleet/server/routes/setup/index.ts +++ b/x-pack/plugins/fleet/server/routes/setup/index.ts @@ -5,55 +5,48 @@ * 2.0. */ -import type { IRouter } from 'src/core/server'; - import { PLUGIN_ID, AGENTS_SETUP_API_ROUTES, SETUP_API_ROUTE } from '../../constants'; import type { FleetConfigType } from '../../../common'; -import type { FleetRequestHandlerContext } from '../../types/request_context'; +import type { FleetRouter } from '../../types/request_context'; import { getFleetStatusHandler, fleetSetupHandler } from './handlers'; -export const registerFleetSetupRoute = (router: IRouter) => { +export const registerFleetSetupRoute = (router: FleetRouter) => { router.post( { path: SETUP_API_ROUTE, validate: false, - // if this route is set to `-all`, a read-only user get a 404 for this route - // and will see `Unable to initialize Ingest Manager` in the UI - options: { tags: [`access:${PLUGIN_ID}-read`] }, }, fleetSetupHandler ); }; // That route is used by agent to setup Fleet -export const registerCreateFleetSetupRoute = (router: IRouter) => { +export const registerCreateFleetSetupRoute = (router: FleetRouter) => { router.post( { path: AGENTS_SETUP_API_ROUTES.CREATE_PATTERN, validate: false, - options: { tags: [`access:${PLUGIN_ID}-all`] }, }, fleetSetupHandler ); }; -export const registerGetFleetStatusRoute = (router: IRouter) => { +export const registerGetFleetStatusRoute = (router: FleetRouter) => { router.get( { path: AGENTS_SETUP_API_ROUTES.INFO_PATTERN, validate: false, + // Disable this tag and the automatic RBAC support until elastic/fleet-server access is removed in 8.0 + // Required to allow elastic/fleet-server to access this API. options: { tags: [`access:${PLUGIN_ID}-read`] }, }, getFleetStatusHandler ); }; -export const registerRoutes = ( - router: IRouter, - config: FleetConfigType -) => { +export const registerRoutes = (router: FleetRouter, config: FleetConfigType) => { // Ingest manager setup registerFleetSetupRoute(router); diff --git a/x-pack/plugins/fleet/server/types/request_context.ts b/x-pack/plugins/fleet/server/types/request_context.ts index a3b414119b685..0d0da9145f073 100644 --- a/x-pack/plugins/fleet/server/types/request_context.ts +++ b/x-pack/plugins/fleet/server/types/request_context.ts @@ -11,6 +11,7 @@ import type { RequestHandlerContext, RouteMethod, SavedObjectsClientContract, + IRouter, } from '../../../../../src/core/server'; /** @internal */ @@ -37,3 +38,9 @@ export type FleetRequestHandler< Method extends RouteMethod = any, ResponseFactory extends KibanaResponseFactory = KibanaResponseFactory > = RequestHandler; + +/** + * Convenience type for routers in Fleet that includes the FleetRequestHandlerContext type + * @internal + */ +export type FleetRouter = IRouter; diff --git a/x-pack/plugins/security/server/authorization/check_privileges.test.ts b/x-pack/plugins/security/server/authorization/check_privileges.test.ts index 75c8229bb37d6..d8906d91f152b 100644 --- a/x-pack/plugins/security/server/authorization/check_privileges.test.ts +++ b/x-pack/plugins/security/server/authorization/check_privileges.test.ts @@ -878,6 +878,42 @@ describe('#atSpace', () => { `); }); }); + + test('omits login privilege when requireLoginAction: false', async () => { + const { mockClusterClient, mockScopedClusterClient } = createMockClusterClient({ + has_all_requested: true, + username: 'foo-username', + index: {}, + application: { + [application]: { + 'space:space_1': { + [mockActions.version]: true, + }, + }, + }, + }); + const checkPrivilegesWithRequest = checkPrivilegesWithRequestFactory( + mockActions, + () => Promise.resolve(mockClusterClient), + application + ); + const request = httpServerMock.createKibanaRequest(); + const checkPrivileges = checkPrivilegesWithRequest(request); + await checkPrivileges.atSpace('space_1', {}, { requireLoginAction: false }); + + expect(mockScopedClusterClient.asCurrentUser.security.hasPrivileges).toHaveBeenCalledWith({ + body: { + index: [], + application: [ + { + application, + resources: [`space:space_1`], + privileges: [mockActions.version], + }, + ], + }, + }); + }); }); describe('#atSpaces', () => { @@ -2083,6 +2119,42 @@ describe('#atSpaces', () => { `); }); }); + + test('omits login privilege when requireLoginAction: false', async () => { + const { mockClusterClient, mockScopedClusterClient } = createMockClusterClient({ + has_all_requested: true, + username: 'foo-username', + index: {}, + application: { + [application]: { + 'space:space_1': { + [mockActions.version]: true, + }, + }, + }, + }); + const checkPrivilegesWithRequest = checkPrivilegesWithRequestFactory( + mockActions, + () => Promise.resolve(mockClusterClient), + application + ); + const request = httpServerMock.createKibanaRequest(); + const checkPrivileges = checkPrivilegesWithRequest(request); + await checkPrivileges.atSpaces(['space_1'], {}, { requireLoginAction: false }); + + expect(mockScopedClusterClient.asCurrentUser.security.hasPrivileges).toHaveBeenCalledWith({ + body: { + index: [], + application: [ + { + application, + resources: [`space:space_1`], + privileges: [mockActions.version], + }, + ], + }, + }); + }); }); describe('#globally', () => { @@ -2937,4 +3009,40 @@ describe('#globally', () => { `); }); }); + + test('omits login privilege when requireLoginAction: false', async () => { + const { mockClusterClient, mockScopedClusterClient } = createMockClusterClient({ + has_all_requested: true, + username: 'foo-username', + index: {}, + application: { + [application]: { + [GLOBAL_RESOURCE]: { + [mockActions.version]: true, + }, + }, + }, + }); + const checkPrivilegesWithRequest = checkPrivilegesWithRequestFactory( + mockActions, + () => Promise.resolve(mockClusterClient), + application + ); + const request = httpServerMock.createKibanaRequest(); + const checkPrivileges = checkPrivilegesWithRequest(request); + await checkPrivileges.globally({}, { requireLoginAction: false }); + + expect(mockScopedClusterClient.asCurrentUser.security.hasPrivileges).toHaveBeenCalledWith({ + body: { + index: [], + application: [ + { + application, + resources: [GLOBAL_RESOURCE], + privileges: [mockActions.version], + }, + ], + }, + }); + }); }); diff --git a/x-pack/plugins/security/server/authorization/check_privileges.ts b/x-pack/plugins/security/server/authorization/check_privileges.ts index 3a35cf164ad85..36c364f1ff7da 100644 --- a/x-pack/plugins/security/server/authorization/check_privileges.ts +++ b/x-pack/plugins/security/server/authorization/check_privileges.ts @@ -13,6 +13,7 @@ import { GLOBAL_RESOURCE } from '../../common/constants'; import { ResourceSerializer } from './resource_serializer'; import type { CheckPrivileges, + CheckPrivilegesOptions, CheckPrivilegesPayload, CheckPrivilegesResponse, HasPrivilegesResponse, @@ -41,14 +42,20 @@ export function checkPrivilegesWithRequestFactory( return function checkPrivilegesWithRequest(request: KibanaRequest): CheckPrivileges { const checkPrivilegesAtResources = async ( resources: string[], - privileges: CheckPrivilegesPayload + privileges: CheckPrivilegesPayload, + { requireLoginAction = true }: CheckPrivilegesOptions = {} ): Promise => { const kibanaPrivileges = Array.isArray(privileges.kibana) ? privileges.kibana : privileges.kibana ? [privileges.kibana] : []; - const allApplicationPrivileges = uniq([actions.version, actions.login, ...kibanaPrivileges]); + + const allApplicationPrivileges = uniq([ + actions.version, + ...(requireLoginAction ? [actions.login] : []), + ...kibanaPrivileges, + ]); const clusterClient = await getClusterClient(); const { body } = await clusterClient.asScoped(request).asCurrentUser.security.hasPrivileges({ @@ -135,18 +142,26 @@ export function checkPrivilegesWithRequestFactory( }; return { - async atSpace(spaceId: string, privileges: CheckPrivilegesPayload) { + async atSpace( + spaceId: string, + privileges: CheckPrivilegesPayload, + options?: CheckPrivilegesOptions + ) { const spaceResource = ResourceSerializer.serializeSpaceResource(spaceId); - return await checkPrivilegesAtResources([spaceResource], privileges); + return await checkPrivilegesAtResources([spaceResource], privileges, options); }, - async atSpaces(spaceIds: string[], privileges: CheckPrivilegesPayload) { + async atSpaces( + spaceIds: string[], + privileges: CheckPrivilegesPayload, + options?: CheckPrivilegesOptions + ) { const spaceResources = spaceIds.map((spaceId) => ResourceSerializer.serializeSpaceResource(spaceId) ); - return await checkPrivilegesAtResources(spaceResources, privileges); + return await checkPrivilegesAtResources(spaceResources, privileges, options); }, - async globally(privileges: CheckPrivilegesPayload) { - return await checkPrivilegesAtResources([GLOBAL_RESOURCE], privileges); + async globally(privileges: CheckPrivilegesPayload, options?: CheckPrivilegesOptions) { + return await checkPrivilegesAtResources([GLOBAL_RESOURCE], privileges, options); }, }; }; diff --git a/x-pack/plugins/security/server/authorization/check_privileges_dynamically.test.ts b/x-pack/plugins/security/server/authorization/check_privileges_dynamically.test.ts index 547782bbd1ba1..9fd14c6d29806 100644 --- a/x-pack/plugins/security/server/authorization/check_privileges_dynamically.test.ts +++ b/x-pack/plugins/security/server/authorization/check_privileges_dynamically.test.ts @@ -8,6 +8,7 @@ import { httpServerMock } from 'src/core/server/mocks'; import { checkPrivilegesDynamicallyWithRequestFactory } from './check_privileges_dynamically'; +import type { CheckPrivilegesOptions } from './types'; test(`checkPrivileges.atSpace when spaces is enabled`, async () => { const expectedResult = Symbol(); @@ -25,13 +26,18 @@ test(`checkPrivileges.atSpace when spaces is enabled`, async () => { namespaceToSpaceId: jest.fn(), }) )(request); - const result = await checkPrivilegesDynamically({ kibana: privilegeOrPrivileges }); + const options: CheckPrivilegesOptions = { requireLoginAction: true }; + const result = await checkPrivilegesDynamically({ kibana: privilegeOrPrivileges }, options); expect(result).toBe(expectedResult); expect(mockCheckPrivilegesWithRequest).toHaveBeenCalledWith(request); - expect(mockCheckPrivileges.atSpace).toHaveBeenCalledWith(spaceId, { - kibana: privilegeOrPrivileges, - }); + expect(mockCheckPrivileges.atSpace).toHaveBeenCalledWith( + spaceId, + { + kibana: privilegeOrPrivileges, + }, + options + ); }); test(`checkPrivileges.globally when spaces is disabled`, async () => { @@ -46,9 +52,13 @@ test(`checkPrivileges.globally when spaces is disabled`, async () => { mockCheckPrivilegesWithRequest, () => undefined )(request); - const result = await checkPrivilegesDynamically({ kibana: privilegeOrPrivileges }); + const options: CheckPrivilegesOptions = { requireLoginAction: true }; + const result = await checkPrivilegesDynamically({ kibana: privilegeOrPrivileges }, options); expect(result).toBe(expectedResult); expect(mockCheckPrivilegesWithRequest).toHaveBeenCalledWith(request); - expect(mockCheckPrivileges.globally).toHaveBeenCalledWith({ kibana: privilegeOrPrivileges }); + expect(mockCheckPrivileges.globally).toHaveBeenCalledWith( + { kibana: privilegeOrPrivileges }, + options + ); }); diff --git a/x-pack/plugins/security/server/authorization/check_privileges_dynamically.ts b/x-pack/plugins/security/server/authorization/check_privileges_dynamically.ts index 4ce59c8706270..d4e335ba04058 100644 --- a/x-pack/plugins/security/server/authorization/check_privileges_dynamically.ts +++ b/x-pack/plugins/security/server/authorization/check_privileges_dynamically.ts @@ -9,13 +9,15 @@ import type { KibanaRequest } from 'src/core/server'; import type { SpacesService } from '../plugin'; import type { + CheckPrivilegesOptions, CheckPrivilegesPayload, CheckPrivilegesResponse, CheckPrivilegesWithRequest, } from './types'; export type CheckPrivilegesDynamically = ( - privileges: CheckPrivilegesPayload + privileges: CheckPrivilegesPayload, + options?: CheckPrivilegesOptions ) => Promise; export type CheckPrivilegesDynamicallyWithRequest = ( @@ -28,11 +30,15 @@ export function checkPrivilegesDynamicallyWithRequestFactory( ): CheckPrivilegesDynamicallyWithRequest { return function checkPrivilegesDynamicallyWithRequest(request: KibanaRequest) { const checkPrivileges = checkPrivilegesWithRequest(request); - return async function checkPrivilegesDynamically(privileges: CheckPrivilegesPayload) { + + return async function checkPrivilegesDynamically( + privileges: CheckPrivilegesPayload, + options?: CheckPrivilegesOptions + ) { const spacesService = getSpacesService(); return spacesService - ? await checkPrivileges.atSpace(spacesService.getSpaceId(request), privileges) - : await checkPrivileges.globally(privileges); + ? await checkPrivileges.atSpace(spacesService.getSpaceId(request), privileges, options) + : await checkPrivileges.globally(privileges, options); }; }; } diff --git a/x-pack/plugins/security/server/authorization/types.ts b/x-pack/plugins/security/server/authorization/types.ts index 8bfe892840637..aee059fb8becb 100644 --- a/x-pack/plugins/security/server/authorization/types.ts +++ b/x-pack/plugins/security/server/authorization/types.ts @@ -29,6 +29,18 @@ export interface HasPrivilegesResponse { }; } +/** + * Options to influce the privilege checks. + */ +export interface CheckPrivilegesOptions { + /** + * Whether or not the `login` action should be required (default: true). + * Setting this to false is not advised except for special circumstances, when you do not require + * the request to belong to a user capable of logging into Kibana. + */ + requireLoginAction?: boolean; +} + export interface CheckPrivilegesResponse { hasAllRequested: boolean; username: string; @@ -59,12 +71,20 @@ export interface CheckPrivilegesResponse { export type CheckPrivilegesWithRequest = (request: KibanaRequest) => CheckPrivileges; export interface CheckPrivileges { - atSpace(spaceId: string, privileges: CheckPrivilegesPayload): Promise; + atSpace( + spaceId: string, + privileges: CheckPrivilegesPayload, + options?: CheckPrivilegesOptions + ): Promise; atSpaces( spaceIds: string[], - privileges: CheckPrivilegesPayload + privileges: CheckPrivilegesPayload, + options?: CheckPrivilegesOptions + ): Promise; + globally( + privileges: CheckPrivilegesPayload, + options?: CheckPrivilegesOptions ): Promise; - globally(privileges: CheckPrivilegesPayload): Promise; } export interface CheckPrivilegesPayload { diff --git a/x-pack/test/api_integration/apis/security/privileges.ts b/x-pack/test/api_integration/apis/security/privileges.ts index 762fc1642a87a..f234855b84e17 100644 --- a/x-pack/test/api_integration/apis/security/privileges.ts +++ b/x-pack/test/api_integration/apis/security/privileges.ts @@ -73,7 +73,7 @@ export default function ({ getService }: FtrProviderContext) { 'packs_read', ], }, - reserved: ['ml_user', 'ml_admin', 'ml_apm_user', 'monitoring'], + reserved: ['fleet-setup', 'ml_user', 'ml_admin', 'ml_apm_user', 'monitoring'], }; await supertest diff --git a/x-pack/test/api_integration/apis/security/privileges_basic.ts b/x-pack/test/api_integration/apis/security/privileges_basic.ts index 0efaa25ee57da..ac69bfcd9d5d4 100644 --- a/x-pack/test/api_integration/apis/security/privileges_basic.ts +++ b/x-pack/test/api_integration/apis/security/privileges_basic.ts @@ -45,7 +45,7 @@ export default function ({ getService }: FtrProviderContext) { }, global: ['all', 'read'], space: ['all', 'read'], - reserved: ['ml_user', 'ml_admin', 'ml_apm_user', 'monitoring'], + reserved: ['fleet-setup', 'ml_user', 'ml_admin', 'ml_apm_user', 'monitoring'], }; await supertest diff --git a/x-pack/test/fleet_api_integration/apis/agents/services.ts b/x-pack/test/fleet_api_integration/apis/agents/services.ts index be5d2d438f76f..0e28ad647bbc4 100644 --- a/x-pack/test/fleet_api_integration/apis/agents/services.ts +++ b/x-pack/test/fleet_api_integration/apis/agents/services.ts @@ -32,12 +32,30 @@ export function getEsClientForAPIKey({ getService }: FtrProviderContext, esApiKe }); } -export function setupFleetAndAgents({ getService }: FtrProviderContext) { +export function setupFleetAndAgents(providerContext: FtrProviderContext) { before(async () => { - await getService('supertest').post(`/api/fleet/setup`).set('kbn-xsrf', 'xxx').send(); - await getService('supertest') + // Use elastic/fleet-server service account to execute setup to verify privilege configuration + const es = providerContext.getService('es'); + const { + body: { token }, + // @ts-expect-error SecurityCreateServiceTokenRequest should not require `name` + } = await es.security.createServiceToken({ + namespace: 'elastic', + service: 'fleet-server', + }); + const supetestWithoutAuth = getSupertestWithoutAuth(providerContext); + + await supetestWithoutAuth + .post(`/api/fleet/setup`) + .set('kbn-xsrf', 'xxx') + .set('Authorization', `Bearer ${token.value}`) + .send() + .expect(200); + await supetestWithoutAuth .post(`/api/fleet/agents/setup`) .set('kbn-xsrf', 'xxx') - .send({ forceRecreate: true }); + .set('Authorization', `Bearer ${token.value}`) + .send({ forceRecreate: true }) + .expect(200); }); } diff --git a/x-pack/test/fleet_api_integration/apis/epm/setup.ts b/x-pack/test/fleet_api_integration/apis/epm/setup.ts index 8567cf8069c58..051636ad11f5a 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/setup.ts +++ b/x-pack/test/fleet_api_integration/apis/epm/setup.ts @@ -14,7 +14,9 @@ import { setupFleetAndAgents } from '../agents/services'; export default function (providerContext: FtrProviderContext) { const { getService } = providerContext; const supertest = getService('supertest'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); const log = getService('log'); + const es = getService('es'); describe('setup api', async () => { skipIfNoDockerRegistry(providerContext); @@ -47,5 +49,48 @@ export default function (providerContext: FtrProviderContext) { ); }); }); + + it('allows elastic/fleet-server user to call required APIs', async () => { + const { + body: { token }, + // @ts-expect-error SecurityCreateServiceTokenRequest should not require `name` + } = await es.security.createServiceToken({ + namespace: 'elastic', + service: 'fleet-server', + }); + + // elastic/fleet-server needs access to these APIs: + // POST /api/fleet/setup + // POST /api/fleet/agents/setup + // GET /api/fleet/agent_policies + // GET /api/fleet/enrollment-api-keys + // GET /api/fleet/enrollment-api-keys/ + await supertestWithoutAuth + .post('/api/fleet/setup') + .set('Authorization', `Bearer ${token.value}`) + .set('kbn-xsrf', 'xxx') + .expect(200); + await supertestWithoutAuth + .post('/api/fleet/agents/setup') + .set('Authorization', `Bearer ${token.value}`) + .set('kbn-xsrf', 'xxx') + .expect(200); + await supertestWithoutAuth + .get('/api/fleet/agent_policies') + .set('Authorization', `Bearer ${token.value}`) + .set('kbn-xsrf', 'xxx') + .expect(200); + const response = await supertestWithoutAuth + .get('/api/fleet/enrollment-api-keys') + .set('Authorization', `Bearer ${token.value}`) + .set('kbn-xsrf', 'xxx') + .expect(200); + const enrollmentApiKeyId = response.body.list[0].id; + await supertestWithoutAuth + .get(`/api/fleet/enrollment-api-keys/${enrollmentApiKeyId}`) + .set('Authorization', `Bearer ${token.value}`) + .set('kbn-xsrf', 'xxx') + .expect(200); + }); }); } From f6a9afea6165c6072bd0c3fdf00439b7a98de1fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loix?= Date: Tue, 19 Oct 2021 11:33:57 +0100 Subject: [PATCH 045/204] [Stack management apps] Deprecate "enabled" Kibana setting (#114768) --- docs/dev-tools/console/console.asciidoc | 9 + docs/setup/settings.asciidoc | 34 +++ src/plugins/console/public/index.ts | 7 +- src/plugins/console/public/plugin.ts | 122 ++++++----- src/plugins/console/public/types/config.ts | 13 ++ src/plugins/console/public/types/index.ts | 2 + src/plugins/console/public/types/locator.ts | 12 ++ .../public/types/plugin_dependencies.ts | 8 +- src/plugins/console/server/config.ts | 199 ++++++++++++++---- src/plugins/console/server/index.ts | 1 + src/plugins/console/server/plugin.ts | 10 +- .../components/manage_data/manage_data.tsx | 3 +- .../components/details/req_code_viewer.tsx | 12 +- .../common/constants/index.ts | 2 + .../server/config.ts | 96 ++++++++- .../cross_cluster_replication/server/index.ts | 13 +- .../common/constants/index.ts | 2 + .../server/config.ts | 96 ++++++++- .../server/index.ts | 13 +- .../plugins/index_management/public/plugin.ts | 50 +++-- .../plugins/index_management/public/types.ts | 6 + .../plugins/index_management/server/config.ts | 89 +++++++- .../plugins/index_management/server/index.ts | 10 +- .../common/constants/index.ts | 2 +- .../common/constants/plugin.ts | 2 + .../license_management/server/config.ts | 90 +++++++- .../license_management/server/index.ts | 13 +- .../remote_clusters/common/constants.ts | 2 + .../plugins/remote_clusters/server/config.ts | 89 +++++++- .../plugins/remote_clusters/server/plugin.ts | 4 +- x-pack/plugins/rollup/common/index.ts | 2 + x-pack/plugins/rollup/public/index.ts | 3 +- x-pack/plugins/rollup/public/plugin.ts | 57 ++--- x-pack/plugins/rollup/public/types.ts | 12 ++ x-pack/plugins/rollup/server/config.ts | 89 +++++++- x-pack/plugins/rollup/server/index.ts | 10 +- .../snapshot_restore/common/constants.ts | 2 + .../plugins/snapshot_restore/public/plugin.ts | 84 ++++---- .../plugins/snapshot_restore/public/types.ts | 1 + .../plugins/snapshot_restore/server/config.ts | 98 ++++++++- .../plugins/snapshot_restore/server/index.ts | 13 +- .../plugins/snapshot_restore/server/plugin.ts | 9 +- .../client_integration/helpers/index.ts | 2 +- .../helpers/setup_environment.tsx | 12 +- .../overview/overview.test.tsx | 5 +- .../upgrade_assistant/common/config.ts | 20 -- .../upgrade_assistant/common/constants.ts | 6 +- .../reindex/flyout/warning_step.test.tsx | 18 +- .../upgrade_assistant/public/plugin.ts | 88 ++++---- .../plugins/upgrade_assistant/public/types.ts | 7 + .../upgrade_assistant/server/config.ts | 107 ++++++++++ .../plugins/upgrade_assistant/server/index.ts | 13 +- .../server/lib/__fixtures__/version.ts | 8 +- .../server/lib/es_version_precheck.test.ts | 4 +- .../lib/reindexing/index_settings.test.ts | 8 +- .../lib/reindexing/reindex_actions.test.ts | 4 +- .../lib/reindexing/reindex_service.test.ts | 4 +- 57 files changed, 1279 insertions(+), 418 deletions(-) create mode 100644 src/plugins/console/public/types/config.ts create mode 100644 src/plugins/console/public/types/locator.ts create mode 100644 x-pack/plugins/rollup/public/types.ts delete mode 100644 x-pack/plugins/upgrade_assistant/common/config.ts create mode 100644 x-pack/plugins/upgrade_assistant/server/config.ts diff --git a/docs/dev-tools/console/console.asciidoc b/docs/dev-tools/console/console.asciidoc index 48fe936dd2db5..21334c31011f4 100644 --- a/docs/dev-tools/console/console.asciidoc +++ b/docs/dev-tools/console/console.asciidoc @@ -129,3 +129,12 @@ image::dev-tools/console/images/console-settings.png["Console Settings", width=6 For a list of available keyboard shortcuts, click *Help*. + +[float] +[[console-settings]] +=== Disable Console + +If you don’t want to use *Console*, you can disable it by setting `console.ui.enabled` +to `false` in your `kibana.yml` configuration file. Changing this setting +causes the server to regenerate assets on the next startup, +which might cause a delay before pages start being served. \ No newline at end of file diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index 4802a4da8182c..af22ad4ad157f 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -20,6 +20,11 @@ configuration using `${MY_ENV_VAR}` syntax. [cols="2*<"] |=== +| `console.ui.enabled:` +Toggling this causes the server to regenerate assets on the next startup, +which may cause a delay before pages start being served. +Set to `false` to disable Console. *Default: `true`* + | `csp.rules:` | deprecated:[7.14.0,"In 8.0 and later, this setting will no longer be supported."] A https://w3c.github.io/webappsec-csp/[Content Security Policy] template @@ -681,6 +686,10 @@ out through *Advanced Settings*. *Default: `true`* | Set this value to true to allow Vega to use any URL to access external data sources and images. When false, Vega can only get data from {es}. *Default: `false`* +| `xpack.ccr.ui.enabled` +Set this value to false to disable the Cross-Cluster Replication UI. +*Default: `true`* + |[[settings-explore-data-in-context]] `xpack.discoverEnhanced.actions.` `exploreDataInContextMenu.enabled` | Enables the *Explore underlying data* option that allows you to open *Discover* from a dashboard panel and view the panel data. *Default: `false`* @@ -689,6 +698,31 @@ sources and images. When false, Vega can only get data from {es}. *Default: `fal `exploreDataInChart.enabled` | Enables you to view the underlying documents in a data series from a dashboard panel. *Default: `false`* +| `xpack.ilm.ui.enabled` +Set this value to false to disable the Index Lifecycle Policies UI. +*Default: `true`* + +| `xpack.index_management.ui.enabled` +Set this value to false to disable the Index Management UI. +*Default: `true`* + +| `xpack.license_management.ui.enabled` +Set this value to false to disable the License Management UI. +*Default: `true`* + +| `xpack.remote_clusters.ui.enabled` +Set this value to false to disable the Remote Clusters UI. +*Default: `true`* + +| `xpack.rollup.ui.enabled:` +Set this value to false to disable the Rollup Jobs UI. *Default: true* + +| `xpack.snapshot_restore.ui.enabled:` +Set this value to false to disable the Snapshot and Restore UI. *Default: true* + +| `xpack.upgrade_assistant.ui.enabled:` +Set this value to false to disable the Upgrade Assistant UI. *Default: true* + | `i18n.locale` {ess-icon} | Set this value to change the {kib} interface language. Valid locales are: `en`, `zh-CN`, `ja-JP`. *Default: `en`* diff --git a/src/plugins/console/public/index.ts b/src/plugins/console/public/index.ts index 8c4a107108565..9a9c5896cd26d 100644 --- a/src/plugins/console/public/index.ts +++ b/src/plugins/console/public/index.ts @@ -7,13 +7,14 @@ */ import './index.scss'; +import { PluginInitializerContext } from 'src/core/public'; import { ConsoleUIPlugin } from './plugin'; -export type { ConsoleUILocatorParams } from './plugin'; +export type { ConsoleUILocatorParams, ConsolePluginSetup } from './types'; export { ConsoleUIPlugin as Plugin }; -export function plugin() { - return new ConsoleUIPlugin(); +export function plugin(ctx: PluginInitializerContext) { + return new ConsoleUIPlugin(ctx); } diff --git a/src/plugins/console/public/plugin.ts b/src/plugins/console/public/plugin.ts index e3791df6a2db6..d61769c23dfe0 100644 --- a/src/plugins/console/public/plugin.ts +++ b/src/plugins/console/public/plugin.ts @@ -7,77 +7,87 @@ */ import { i18n } from '@kbn/i18n'; -import { SerializableRecord } from '@kbn/utility-types'; -import { Plugin, CoreSetup } from 'src/core/public'; +import { Plugin, CoreSetup, PluginInitializerContext } from 'src/core/public'; import { FeatureCatalogueCategory } from '../../home/public'; -import { AppSetupUIPluginDependencies } from './types'; - -export interface ConsoleUILocatorParams extends SerializableRecord { - loadFrom?: string; -} +import { + AppSetupUIPluginDependencies, + ClientConfigType, + ConsolePluginSetup, + ConsoleUILocatorParams, +} from './types'; export class ConsoleUIPlugin implements Plugin { + constructor(private ctx: PluginInitializerContext) {} + public setup( { notifications, getStartServices, http }: CoreSetup, { devTools, home, share, usageCollection }: AppSetupUIPluginDependencies - ) { - if (home) { - home.featureCatalogue.register({ + ): ConsolePluginSetup { + const { + ui: { enabled: isConsoleUiEnabled }, + } = this.ctx.config.get(); + + if (isConsoleUiEnabled) { + if (home) { + home.featureCatalogue.register({ + id: 'console', + title: i18n.translate('console.devToolsTitle', { + defaultMessage: 'Interact with the Elasticsearch API', + }), + description: i18n.translate('console.devToolsDescription', { + defaultMessage: 'Skip cURL and use a JSON interface to work with your data in Console.', + }), + icon: 'consoleApp', + path: '/app/dev_tools#/console', + showOnHomePage: false, + category: FeatureCatalogueCategory.ADMIN, + }); + } + + devTools.register({ id: 'console', - title: i18n.translate('console.devToolsTitle', { - defaultMessage: 'Interact with the Elasticsearch API', - }), - description: i18n.translate('console.devToolsDescription', { - defaultMessage: 'Skip cURL and use a JSON interface to work with your data in Console.', + order: 1, + title: i18n.translate('console.consoleDisplayName', { + defaultMessage: 'Console', }), - icon: 'consoleApp', - path: '/app/dev_tools#/console', - showOnHomePage: false, - category: FeatureCatalogueCategory.ADMIN, - }); - } + enableRouting: false, + mount: async ({ element }) => { + const [core] = await getStartServices(); - devTools.register({ - id: 'console', - order: 1, - title: i18n.translate('console.consoleDisplayName', { - defaultMessage: 'Console', - }), - enableRouting: false, - mount: async ({ element }) => { - const [core] = await getStartServices(); + const { + i18n: { Context: I18nContext }, + docLinks: { DOC_LINK_VERSION }, + } = core; - const { - i18n: { Context: I18nContext }, - docLinks: { DOC_LINK_VERSION }, - } = core; + const { renderApp } = await import('./application'); - const { renderApp } = await import('./application'); + return renderApp({ + http, + docLinkVersion: DOC_LINK_VERSION, + I18nContext, + notifications, + usageCollection, + element, + }); + }, + }); - return renderApp({ - http, - docLinkVersion: DOC_LINK_VERSION, - I18nContext, - notifications, - usageCollection, - element, - }); - }, - }); + const locator = share.url.locators.create({ + id: 'CONSOLE_APP_LOCATOR', + getLocation: async ({ loadFrom }) => { + return { + app: 'dev_tools', + path: `#/console${loadFrom ? `?load_from=${loadFrom}` : ''}`, + state: { loadFrom }, + }; + }, + }); - const locator = share.url.locators.create({ - id: 'CONSOLE_APP_LOCATOR', - getLocation: async ({ loadFrom }) => { - return { - app: 'dev_tools', - path: `#/console${loadFrom ? `?load_from=${loadFrom}` : ''}`, - state: { loadFrom }, - }; - }, - }); + return { locator }; + } - return { locator }; + return {}; } public start() {} diff --git a/src/plugins/console/public/types/config.ts b/src/plugins/console/public/types/config.ts new file mode 100644 index 0000000000000..da41eef6f5484 --- /dev/null +++ b/src/plugins/console/public/types/config.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export interface ClientConfigType { + ui: { + enabled: boolean; + }; +} diff --git a/src/plugins/console/public/types/index.ts b/src/plugins/console/public/types/index.ts index b98adbf5610cd..d8b6aaf7b12c4 100644 --- a/src/plugins/console/public/types/index.ts +++ b/src/plugins/console/public/types/index.ts @@ -11,3 +11,5 @@ export * from './core_editor'; export * from './token'; export * from './tokens_provider'; export * from './common'; +export { ClientConfigType } from './config'; +export { ConsoleUILocatorParams } from './locator'; diff --git a/src/plugins/console/public/types/locator.ts b/src/plugins/console/public/types/locator.ts new file mode 100644 index 0000000000000..f3a42338aaadc --- /dev/null +++ b/src/plugins/console/public/types/locator.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import { SerializableRecord } from '@kbn/utility-types'; + +export interface ConsoleUILocatorParams extends SerializableRecord { + loadFrom?: string; +} diff --git a/src/plugins/console/public/types/plugin_dependencies.ts b/src/plugins/console/public/types/plugin_dependencies.ts index 444776f47ea13..afc49f9a5a986 100644 --- a/src/plugins/console/public/types/plugin_dependencies.ts +++ b/src/plugins/console/public/types/plugin_dependencies.ts @@ -9,7 +9,9 @@ import { HomePublicPluginSetup } from '../../../home/public'; import { DevToolsSetup } from '../../../dev_tools/public'; import { UsageCollectionSetup } from '../../../usage_collection/public'; -import { SharePluginSetup } from '../../../share/public'; +import { SharePluginSetup, LocatorPublic } from '../../../share/public'; + +import { ConsoleUILocatorParams } from './locator'; export interface AppSetupUIPluginDependencies { home?: HomePublicPluginSetup; @@ -17,3 +19,7 @@ export interface AppSetupUIPluginDependencies { share: SharePluginSetup; usageCollection?: UsageCollectionSetup; } + +export interface ConsolePluginSetup { + locator?: LocatorPublic; +} diff --git a/src/plugins/console/server/config.ts b/src/plugins/console/server/config.ts index 6d667fed081e8..024777aa8d252 100644 --- a/src/plugins/console/server/config.ts +++ b/src/plugins/console/server/config.ts @@ -7,6 +7,8 @@ */ import { SemVer } from 'semver'; +import { i18n } from '@kbn/i18n'; +import { get } from 'lodash'; import { schema, TypeOf } from '@kbn/config-schema'; import { PluginConfigDescriptor } from 'kibana/server'; @@ -14,62 +16,171 @@ import { MAJOR_VERSION } from '../common/constants'; const kibanaVersion = new SemVer(MAJOR_VERSION); -const baseSettings = { - enabled: schema.boolean({ defaultValue: true }), - ssl: schema.object({ verify: schema.boolean({ defaultValue: false }) }, {}), -}; - -// Settings only available in 7.x -const deprecatedSettings = { - proxyFilter: schema.arrayOf(schema.string(), { defaultValue: ['.*'] }), - proxyConfig: schema.arrayOf( - schema.object({ - match: schema.object({ - protocol: schema.string({ defaultValue: '*' }), - host: schema.string({ defaultValue: '*' }), - port: schema.string({ defaultValue: '*' }), - path: schema.string({ defaultValue: '*' }), - }), - - timeout: schema.number(), - ssl: schema.object( - { - verify: schema.boolean(), - ca: schema.arrayOf(schema.string()), - cert: schema.string(), - key: schema.string(), - }, - { defaultValue: undefined } - ), - }), - { defaultValue: [] } - ), -}; - -const configSchema = schema.object( +// ------------------------------- +// >= 8.x +// ------------------------------- +const schemaLatest = schema.object( { - ...baseSettings, + ssl: schema.object({ verify: schema.boolean({ defaultValue: false }) }, {}), + ui: schema.object({ + enabled: schema.boolean({ defaultValue: true }), + }), }, { defaultValue: undefined } ); -const configSchema7x = schema.object( +const configLatest: PluginConfigDescriptor = { + exposeToBrowser: { + ui: true, + }, + schema: schemaLatest, + deprecations: () => [], +}; + +export type ConsoleConfig = TypeOf; + +// ------------------------------- +// 7.x +// ------------------------------- +const schema7x = schema.object( { - ...baseSettings, - ...deprecatedSettings, + enabled: schema.boolean({ defaultValue: true }), + proxyFilter: schema.arrayOf(schema.string(), { defaultValue: ['.*'] }), + proxyConfig: schema.arrayOf( + schema.object({ + match: schema.object({ + protocol: schema.string({ defaultValue: '*' }), + host: schema.string({ defaultValue: '*' }), + port: schema.string({ defaultValue: '*' }), + path: schema.string({ defaultValue: '*' }), + }), + + timeout: schema.number(), + ssl: schema.object( + { + verify: schema.boolean(), + ca: schema.arrayOf(schema.string()), + cert: schema.string(), + key: schema.string(), + }, + { defaultValue: undefined } + ), + }), + { defaultValue: [] } + ), + ssl: schema.object({ verify: schema.boolean({ defaultValue: false }) }, {}), + ui: schema.object({ + enabled: schema.boolean({ defaultValue: true }), + }), }, { defaultValue: undefined } ); -export type ConfigType = TypeOf; -export type ConfigType7x = TypeOf; +export type ConsoleConfig7x = TypeOf; -export const config: PluginConfigDescriptor = { - schema: kibanaVersion.major < 8 ? configSchema7x : configSchema, +const config7x: PluginConfigDescriptor = { + exposeToBrowser: { + ui: true, + }, + schema: schema7x, deprecations: ({ deprecate, unused }) => [ - deprecate('enabled', '8.0.0'), - deprecate('proxyFilter', '8.0.0'), - deprecate('proxyConfig', '8.0.0'), unused('ssl'), + (completeConfig, rootPath, addDeprecation) => { + if (get(completeConfig, 'console.enabled') === undefined) { + return completeConfig; + } + + addDeprecation({ + configPath: 'console.enabled', + level: 'critical', + title: i18n.translate('console.deprecations.enabledTitle', { + defaultMessage: 'Setting "console.enabled" is deprecated', + }), + message: i18n.translate('console.deprecations.enabledMessage', { + defaultMessage: + 'To disallow users from accessing the Console UI, use the "console.ui.enabled" setting instead of "console.enabled".', + }), + correctiveActions: { + manualSteps: [ + i18n.translate('console.deprecations.enabled.manualStepOneMessage', { + defaultMessage: 'Open the kibana.yml config file.', + }), + i18n.translate('console.deprecations.enabled.manualStepTwoMessage', { + defaultMessage: 'Change the "console.enabled" setting to "console.ui.enabled".', + }), + ], + }, + }); + return completeConfig; + }, + (completeConfig, rootPath, addDeprecation) => { + if (get(completeConfig, 'console.proxyConfig') === undefined) { + return completeConfig; + } + + addDeprecation({ + configPath: 'console.proxyConfig', + level: 'critical', + title: i18n.translate('console.deprecations.proxyConfigTitle', { + defaultMessage: 'Setting "console.proxyConfig" is deprecated', + }), + message: i18n.translate('console.deprecations.proxyConfigMessage', { + defaultMessage: + 'Configuring "console.proxyConfig" is deprecated and will be removed in 8.0.0. To secure your connection between Kibana and Elasticsearch use the standard "server.ssl.*" settings instead.', + }), + documentationUrl: 'https://ela.st/encrypt-kibana-browser', + correctiveActions: { + manualSteps: [ + i18n.translate('console.deprecations.proxyConfig.manualStepOneMessage', { + defaultMessage: 'Open the kibana.yml config file.', + }), + i18n.translate('console.deprecations.proxyConfig.manualStepTwoMessage', { + defaultMessage: 'Remove the "console.proxyConfig" setting.', + }), + i18n.translate('console.deprecations.proxyConfig.manualStepThreeMessage', { + defaultMessage: + 'Configure the secure connection between Kibana and Elasticsearch using the "server.ssl.*" settings.', + }), + ], + }, + }); + return completeConfig; + }, + (completeConfig, rootPath, addDeprecation) => { + if (get(completeConfig, 'console.proxyFilter') === undefined) { + return completeConfig; + } + + addDeprecation({ + configPath: 'console.proxyFilter', + level: 'critical', + title: i18n.translate('console.deprecations.proxyFilterTitle', { + defaultMessage: 'Setting "console.proxyFilter" is deprecated', + }), + message: i18n.translate('console.deprecations.proxyFilterMessage', { + defaultMessage: + 'Configuring "console.proxyFilter" is deprecated and will be removed in 8.0.0. To secure your connection between Kibana and Elasticsearch use the standard "server.ssl.*" settings instead.', + }), + documentationUrl: 'https://ela.st/encrypt-kibana-browser', + correctiveActions: { + manualSteps: [ + i18n.translate('console.deprecations.proxyFilter.manualStepOneMessage', { + defaultMessage: 'Open the kibana.yml config file.', + }), + i18n.translate('console.deprecations.proxyFilter.manualStepTwoMessage', { + defaultMessage: 'Remove the "console.proxyFilter" setting.', + }), + i18n.translate('console.deprecations.proxyFilter.manualStepThreeMessage', { + defaultMessage: + 'Configure the secure connection between Kibana and Elasticsearch using the "server.ssl.*" settings.', + }), + ], + }, + }); + return completeConfig; + }, ], }; + +export const config: PluginConfigDescriptor = + kibanaVersion.major < 8 ? config7x : configLatest; diff --git a/src/plugins/console/server/index.ts b/src/plugins/console/server/index.ts index 6ae518f5dc796..b270b89a3d45a 100644 --- a/src/plugins/console/server/index.ts +++ b/src/plugins/console/server/index.ts @@ -11,6 +11,7 @@ import { PluginInitializerContext } from 'kibana/server'; import { ConsoleServerPlugin } from './plugin'; export { ConsoleSetup, ConsoleStart } from './types'; + export { config } from './config'; export const plugin = (ctx: PluginInitializerContext) => new ConsoleServerPlugin(ctx); diff --git a/src/plugins/console/server/plugin.ts b/src/plugins/console/server/plugin.ts index 613337b286fbf..5543c40d03cb0 100644 --- a/src/plugins/console/server/plugin.ts +++ b/src/plugins/console/server/plugin.ts @@ -11,7 +11,7 @@ import { SemVer } from 'semver'; import { ProxyConfigCollection } from './lib'; import { SpecDefinitionsService, EsLegacyConfigService } from './services'; -import { ConfigType, ConfigType7x } from './config'; +import { ConsoleConfig, ConsoleConfig7x } from './config'; import { registerRoutes } from './routes'; @@ -24,11 +24,11 @@ export class ConsoleServerPlugin implements Plugin { esLegacyConfigService = new EsLegacyConfigService(); - constructor(private readonly ctx: PluginInitializerContext) { + constructor(private readonly ctx: PluginInitializerContext) { this.log = this.ctx.logger.get(); } - setup({ http, capabilities, getStartServices, elasticsearch }: CoreSetup) { + setup({ http, capabilities, elasticsearch }: CoreSetup) { capabilities.registerProvider(() => ({ dev_tools: { show: true, @@ -43,8 +43,8 @@ export class ConsoleServerPlugin implements Plugin { let proxyConfigCollection: ProxyConfigCollection | undefined; if (kibanaVersion.major < 8) { // "pathFilters" and "proxyConfig" are only used in 7.x - pathFilters = (config as ConfigType7x).proxyFilter.map((str: string) => new RegExp(str)); - proxyConfigCollection = new ProxyConfigCollection((config as ConfigType7x).proxyConfig); + pathFilters = (config as ConsoleConfig7x).proxyFilter.map((str: string) => new RegExp(str)); + proxyConfigCollection = new ProxyConfigCollection((config as ConsoleConfig7x).proxyConfig); } this.esLegacyConfigService.setup(elasticsearch.legacy.config$); diff --git a/src/plugins/home/public/application/components/manage_data/manage_data.tsx b/src/plugins/home/public/application/components/manage_data/manage_data.tsx index b374bdd2e1612..0f465dfcf965f 100644 --- a/src/plugins/home/public/application/components/manage_data/manage_data.tsx +++ b/src/plugins/home/public/application/components/manage_data/manage_data.tsx @@ -61,7 +61,8 @@ export const ManageData: FC = ({ addBasePath, application, features }) => {isDevToolsEnabled || isManagementEnabled ? ( - {isDevToolsEnabled ? ( + {/* Check if both the Dev Tools UI and the Console UI are enabled. */} + {isDevToolsEnabled && consoleHref !== undefined ? ( (); const navigateToUrl = services.application?.navigateToUrl; - const canShowDevTools = services.application?.capabilities?.dev_tools.show; const devToolsDataUri = compressToEncodedURIComponent(`GET ${indexPattern}/_search\n${json}`); - const devToolsHref = services.share.url.locators + const consoleHref = services.share.url.locators .get('CONSOLE_APP_LOCATOR') ?.useUrl({ loadFrom: `data:text/plain,${devToolsDataUri}` }); + // Check if both the Dev Tools UI and the Console UI are enabled. + const canShowDevTools = + services.application?.capabilities?.dev_tools.show && consoleHref !== undefined; const shouldShowDevToolsLink = !!(indexPattern && canShowDevTools); const handleDevToolsLinkClick = useCallback( - () => devToolsHref && navigateToUrl && navigateToUrl(devToolsHref), - [devToolsHref, navigateToUrl] + () => consoleHref && navigateToUrl && navigateToUrl(consoleHref), + [consoleHref, navigateToUrl] ); return ( @@ -79,7 +81,7 @@ export const RequestCodeViewer = ({ indexPattern, json }: RequestCodeViewerProps size="xs" flush="right" iconType="wrench" - href={devToolsHref} + href={consoleHref} onClick={handleDevToolsLinkClick} data-test-subj="inspectorRequestOpenInConsoleButton" > diff --git a/x-pack/plugins/cross_cluster_replication/common/constants/index.ts b/x-pack/plugins/cross_cluster_replication/common/constants/index.ts index f1b327aed6389..a800afcf77ae4 100644 --- a/x-pack/plugins/cross_cluster_replication/common/constants/index.ts +++ b/x-pack/plugins/cross_cluster_replication/common/constants/index.ts @@ -19,6 +19,8 @@ export const PLUGIN = { minimumLicenseType: platinumLicense, }; +export const MAJOR_VERSION = '8.0.0'; + export const APPS = { CCR_APP: 'ccr', REMOTE_CLUSTER_APP: 'remote_cluster', diff --git a/x-pack/plugins/cross_cluster_replication/server/config.ts b/x-pack/plugins/cross_cluster_replication/server/config.ts index 50cca903f8a2b..732137e308a0d 100644 --- a/x-pack/plugins/cross_cluster_replication/server/config.ts +++ b/x-pack/plugins/cross_cluster_replication/server/config.ts @@ -4,14 +4,96 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - +import { SemVer } from 'semver'; +import { i18n } from '@kbn/i18n'; +import { get } from 'lodash'; import { schema, TypeOf } from '@kbn/config-schema'; +import { PluginConfigDescriptor } from 'src/core/server'; + +import { MAJOR_VERSION } from '../common/constants'; + +const kibanaVersion = new SemVer(MAJOR_VERSION); + +// ------------------------------- +// >= 8.x +// ------------------------------- +const schemaLatest = schema.object( + { + ui: schema.object({ + enabled: schema.boolean({ defaultValue: true }), + }), + }, + { defaultValue: undefined } +); + +const configLatest: PluginConfigDescriptor = { + exposeToBrowser: { + ui: true, + }, + schema: schemaLatest, + deprecations: () => [], +}; -export const configSchema = schema.object({ - enabled: schema.boolean({ defaultValue: true }), - ui: schema.object({ +export type CrossClusterReplicationConfig = TypeOf; + +// ------------------------------- +// 7.x +// ------------------------------- +const schema7x = schema.object( + { enabled: schema.boolean({ defaultValue: true }), - }), -}); + ui: schema.object({ + enabled: schema.boolean({ defaultValue: true }), + }), + }, + { defaultValue: undefined } +); + +export type CrossClusterReplicationConfig7x = TypeOf; + +const config7x: PluginConfigDescriptor = { + exposeToBrowser: { + ui: true, + }, + schema: schema7x, + deprecations: () => [ + (completeConfig, rootPath, addDeprecation) => { + if (get(completeConfig, 'xpack.ccr.enabled') === undefined) { + return completeConfig; + } + + addDeprecation({ + configPath: 'xpack.ccr.enabled', + level: 'critical', + title: i18n.translate('xpack.crossClusterReplication.deprecations.enabledTitle', { + defaultMessage: 'Setting "xpack.ccr.enabled" is deprecated', + }), + message: i18n.translate('xpack.crossClusterReplication.deprecations.enabledMessage', { + defaultMessage: + 'To disallow users from accessing the Cross-Cluster Replication UI, use the "xpack.ccr.ui.enabled" setting instead of "xpack.ccr.enabled".', + }), + correctiveActions: { + manualSteps: [ + i18n.translate( + 'xpack.crossClusterReplication.deprecations.enabled.manualStepOneMessage', + { + defaultMessage: 'Open the kibana.yml config file.', + } + ), + i18n.translate( + 'xpack.crossClusterReplication.deprecations.enabled.manualStepTwoMessage', + { + defaultMessage: 'Change the "xpack.ccr.enabled" setting to "xpack.ccr.ui.enabled".', + } + ), + ], + }, + }); + return completeConfig; + }, + ], +}; -export type CrossClusterReplicationConfig = TypeOf; +export const config: PluginConfigDescriptor< + CrossClusterReplicationConfig | CrossClusterReplicationConfig7x +> = kibanaVersion.major < 8 ? config7x : configLatest; diff --git a/x-pack/plugins/cross_cluster_replication/server/index.ts b/x-pack/plugins/cross_cluster_replication/server/index.ts index a6a3ec0fe5753..7a0984a6117bf 100644 --- a/x-pack/plugins/cross_cluster_replication/server/index.ts +++ b/x-pack/plugins/cross_cluster_replication/server/index.ts @@ -5,17 +5,10 @@ * 2.0. */ -import { PluginInitializerContext, PluginConfigDescriptor } from 'src/core/server'; +import { PluginInitializerContext } from 'src/core/server'; import { CrossClusterReplicationServerPlugin } from './plugin'; -import { configSchema, CrossClusterReplicationConfig } from './config'; + +export { config } from './config'; export const plugin = (pluginInitializerContext: PluginInitializerContext) => new CrossClusterReplicationServerPlugin(pluginInitializerContext); - -export const config: PluginConfigDescriptor = { - schema: configSchema, - exposeToBrowser: { - ui: true, - }, - deprecations: ({ deprecate }) => [deprecate('enabled', '8.0.0')], -}; diff --git a/x-pack/plugins/index_lifecycle_management/common/constants/index.ts b/x-pack/plugins/index_lifecycle_management/common/constants/index.ts index 7107489f4e2ba..329f479e128e2 100644 --- a/x-pack/plugins/index_lifecycle_management/common/constants/index.ts +++ b/x-pack/plugins/index_lifecycle_management/common/constants/index.ts @@ -19,6 +19,8 @@ export const PLUGIN = { }), }; +export const MAJOR_VERSION = '8.0.0'; + export const API_BASE_PATH = '/api/index_lifecycle_management'; export { MIN_SEARCHABLE_SNAPSHOT_LICENSE, MIN_PLUGIN_LICENSE }; diff --git a/x-pack/plugins/index_lifecycle_management/server/config.ts b/x-pack/plugins/index_lifecycle_management/server/config.ts index f3fdf59cec55b..691cc06708bb5 100644 --- a/x-pack/plugins/index_lifecycle_management/server/config.ts +++ b/x-pack/plugins/index_lifecycle_management/server/config.ts @@ -4,16 +4,94 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - +import { SemVer } from 'semver'; +import { i18n } from '@kbn/i18n'; +import { get } from 'lodash'; import { schema, TypeOf } from '@kbn/config-schema'; +import { PluginConfigDescriptor } from 'src/core/server'; + +import { MAJOR_VERSION } from '../common/constants'; + +const kibanaVersion = new SemVer(MAJOR_VERSION); + +// ------------------------------- +// >= 8.x +// ------------------------------- +const schemaLatest = schema.object( + { + ui: schema.object({ + enabled: schema.boolean({ defaultValue: true }), + }), + // Cloud requires the ability to hide internal node attributes from users. + filteredNodeAttributes: schema.arrayOf(schema.string(), { defaultValue: [] }), + }, + { defaultValue: undefined } +); + +const configLatest: PluginConfigDescriptor = { + exposeToBrowser: { + ui: true, + }, + schema: schemaLatest, + deprecations: () => [], +}; -export const configSchema = schema.object({ - enabled: schema.boolean({ defaultValue: true }), - ui: schema.object({ +export type IndexLifecycleManagementConfig = TypeOf; + +// ------------------------------- +// 7.x +// ------------------------------- +const schema7x = schema.object( + { enabled: schema.boolean({ defaultValue: true }), - }), - // Cloud requires the ability to hide internal node attributes from users. - filteredNodeAttributes: schema.arrayOf(schema.string(), { defaultValue: [] }), -}); + ui: schema.object({ + enabled: schema.boolean({ defaultValue: true }), + }), + // Cloud requires the ability to hide internal node attributes from users. + filteredNodeAttributes: schema.arrayOf(schema.string(), { defaultValue: [] }), + }, + { defaultValue: undefined } +); + +export type IndexLifecycleManagementConfig7x = TypeOf; + +const config7x: PluginConfigDescriptor = { + exposeToBrowser: { + ui: true, + }, + schema: schema7x, + deprecations: () => [ + (completeConfig, rootPath, addDeprecation) => { + if (get(completeConfig, 'xpack.ilm.enabled') === undefined) { + return completeConfig; + } + + addDeprecation({ + configPath: 'xpack.ilm.enabled', + level: 'critical', + title: i18n.translate('xpack.indexLifecycleMgmt.deprecations.enabledTitle', { + defaultMessage: 'Setting "xpack.ilm.enabled" is deprecated', + }), + message: i18n.translate('xpack.indexLifecycleMgmt.deprecations.enabledMessage', { + defaultMessage: + 'To disallow users from accessing the Index Lifecycle Policies UI, use the "xpack.ilm.ui.enabled" setting instead of "xpack.ilm.enabled".', + }), + correctiveActions: { + manualSteps: [ + i18n.translate('xpack.indexLifecycleMgmt.deprecations.enabled.manualStepOneMessage', { + defaultMessage: 'Open the kibana.yml config file.', + }), + i18n.translate('xpack.indexLifecycleMgmt.deprecations.enabled.manualStepTwoMessage', { + defaultMessage: 'Change the "xpack.ilm.enabled" setting to "xpack.ilm.ui.enabled".', + }), + ], + }, + }); + return completeConfig; + }, + ], +}; -export type IndexLifecycleManagementConfig = TypeOf; +export const config: PluginConfigDescriptor< + IndexLifecycleManagementConfig | IndexLifecycleManagementConfig7x +> = kibanaVersion.major < 8 ? config7x : configLatest; diff --git a/x-pack/plugins/index_lifecycle_management/server/index.ts b/x-pack/plugins/index_lifecycle_management/server/index.ts index 1f8b01913fd3e..6a74b4c80b2d3 100644 --- a/x-pack/plugins/index_lifecycle_management/server/index.ts +++ b/x-pack/plugins/index_lifecycle_management/server/index.ts @@ -5,17 +5,10 @@ * 2.0. */ -import { PluginInitializerContext, PluginConfigDescriptor } from 'kibana/server'; +import { PluginInitializerContext } from 'kibana/server'; import { IndexLifecycleManagementServerPlugin } from './plugin'; -import { configSchema, IndexLifecycleManagementConfig } from './config'; + +export { config } from './config'; export const plugin = (ctx: PluginInitializerContext) => new IndexLifecycleManagementServerPlugin(ctx); - -export const config: PluginConfigDescriptor = { - schema: configSchema, - exposeToBrowser: { - ui: true, - }, - deprecations: ({ deprecate }) => [deprecate('enabled', '8.0.0')], -}; diff --git a/x-pack/plugins/index_management/public/plugin.ts b/x-pack/plugins/index_management/public/plugin.ts index 4e123b6f474f8..2394167ca61b2 100644 --- a/x-pack/plugins/index_management/public/plugin.ts +++ b/x-pack/plugins/index_management/public/plugin.ts @@ -13,7 +13,12 @@ import { setExtensionsService } from './application/store/selectors/extension_se import { ExtensionsService } from './services'; -import { IndexManagementPluginSetup, SetupDependencies, StartDependencies } from './types'; +import { + IndexManagementPluginSetup, + SetupDependencies, + StartDependencies, + ClientConfigType, +} from './types'; // avoid import from index files in plugin.ts, use specific import paths import { PLUGIN } from '../common/constants/plugin'; @@ -31,25 +36,30 @@ export class IndexMgmtUIPlugin { coreSetup: CoreSetup, plugins: SetupDependencies ): IndexManagementPluginSetup { - const { fleet, usageCollection, management } = plugins; - const kibanaVersion = new SemVer(this.ctx.env.packageInfo.version); - - management.sections.section.data.registerApp({ - id: PLUGIN.id, - title: i18n.translate('xpack.idxMgmt.appTitle', { defaultMessage: 'Index Management' }), - order: 0, - mount: async (params) => { - const { mountManagementSection } = await import('./application/mount_management_section'); - return mountManagementSection( - coreSetup, - usageCollection, - params, - this.extensionsService, - Boolean(fleet), - kibanaVersion - ); - }, - }); + const { + ui: { enabled: isIndexManagementUiEnabled }, + } = this.ctx.config.get(); + + if (isIndexManagementUiEnabled) { + const { fleet, usageCollection, management } = plugins; + const kibanaVersion = new SemVer(this.ctx.env.packageInfo.version); + management.sections.section.data.registerApp({ + id: PLUGIN.id, + title: i18n.translate('xpack.idxMgmt.appTitle', { defaultMessage: 'Index Management' }), + order: 0, + mount: async (params) => { + const { mountManagementSection } = await import('./application/mount_management_section'); + return mountManagementSection( + coreSetup, + usageCollection, + params, + this.extensionsService, + Boolean(fleet), + kibanaVersion + ); + }, + }); + } return { extensionsService: this.extensionsService.setup(), diff --git a/x-pack/plugins/index_management/public/types.ts b/x-pack/plugins/index_management/public/types.ts index 05c486e299c7a..e0af6b160cf11 100644 --- a/x-pack/plugins/index_management/public/types.ts +++ b/x-pack/plugins/index_management/public/types.ts @@ -23,3 +23,9 @@ export interface SetupDependencies { export interface StartDependencies { share: SharePluginStart; } + +export interface ClientConfigType { + ui: { + enabled: boolean; + }; +} diff --git a/x-pack/plugins/index_management/server/config.ts b/x-pack/plugins/index_management/server/config.ts index 0a314c7654b16..88a714db5edca 100644 --- a/x-pack/plugins/index_management/server/config.ts +++ b/x-pack/plugins/index_management/server/config.ts @@ -4,11 +4,90 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - +import { SemVer } from 'semver'; +import { i18n } from '@kbn/i18n'; +import { get } from 'lodash'; import { schema, TypeOf } from '@kbn/config-schema'; +import { PluginConfigDescriptor } from 'src/core/server'; + +import { MAJOR_VERSION } from '../common/constants'; + +const kibanaVersion = new SemVer(MAJOR_VERSION); + +// ------------------------------- +// >= 8.x +// ------------------------------- +const schemaLatest = schema.object( + { + ui: schema.object({ + enabled: schema.boolean({ defaultValue: true }), + }), + }, + { defaultValue: undefined } +); + +const configLatest: PluginConfigDescriptor = { + exposeToBrowser: { + ui: true, + }, + schema: schemaLatest, + deprecations: () => [], +}; + +export type IndexManagementConfig = TypeOf; + +// ------------------------------- +// 7.x +// ------------------------------- +const schema7x = schema.object( + { + enabled: schema.boolean({ defaultValue: true }), + ui: schema.object({ + enabled: schema.boolean({ defaultValue: true }), + }), + }, + { defaultValue: undefined } +); + +export type IndexManagementConfig7x = TypeOf; + +const config7x: PluginConfigDescriptor = { + exposeToBrowser: { + ui: true, + }, + schema: schema7x, + deprecations: () => [ + (completeConfig, rootPath, addDeprecation) => { + if (get(completeConfig, 'xpack.index_management.enabled') === undefined) { + return completeConfig; + } -export const configSchema = schema.object({ - enabled: schema.boolean({ defaultValue: true }), -}); + addDeprecation({ + configPath: 'xpack.index_management.enabled', + level: 'critical', + title: i18n.translate('xpack.idxMgmt.deprecations.enabledTitle', { + defaultMessage: 'Setting "xpack.index_management.enabled" is deprecated', + }), + message: i18n.translate('xpack.idxMgmt.deprecations.enabledMessage', { + defaultMessage: + 'To disallow users from accessing the Index Management UI, use the "xpack.index_management.ui.enabled" setting instead of "xpack.index_management.enabled".', + }), + correctiveActions: { + manualSteps: [ + i18n.translate('xpack.idxMgmt.deprecations.enabled.manualStepOneMessage', { + defaultMessage: 'Open the kibana.yml config file.', + }), + i18n.translate('xpack.idxMgmt.deprecations.enabled.manualStepTwoMessage', { + defaultMessage: + 'Change the "xpack.index_management.enabled" setting to "xpack.index_management.ui.enabled".', + }), + ], + }, + }); + return completeConfig; + }, + ], +}; -export type IndexManagementConfig = TypeOf; +export const config: PluginConfigDescriptor = + kibanaVersion.major < 8 ? config7x : configLatest; diff --git a/x-pack/plugins/index_management/server/index.ts b/x-pack/plugins/index_management/server/index.ts index 14b67e2ffd581..29291116e44fc 100644 --- a/x-pack/plugins/index_management/server/index.ts +++ b/x-pack/plugins/index_management/server/index.ts @@ -5,17 +5,13 @@ * 2.0. */ -import { PluginInitializerContext, PluginConfigDescriptor } from 'src/core/server'; +import { PluginInitializerContext } from 'src/core/server'; import { IndexMgmtServerPlugin } from './plugin'; -import { configSchema } from './config'; -export const plugin = (context: PluginInitializerContext) => new IndexMgmtServerPlugin(context); +export { config } from './config'; -export const config: PluginConfigDescriptor = { - schema: configSchema, - deprecations: ({ deprecate }) => [deprecate('enabled', '8.0.0')], -}; +export const plugin = (context: PluginInitializerContext) => new IndexMgmtServerPlugin(context); /** @public */ export { Dependencies } from './types'; diff --git a/x-pack/plugins/license_management/common/constants/index.ts b/x-pack/plugins/license_management/common/constants/index.ts index 0567b0008f0c8..9735eabeb1e40 100644 --- a/x-pack/plugins/license_management/common/constants/index.ts +++ b/x-pack/plugins/license_management/common/constants/index.ts @@ -5,7 +5,7 @@ * 2.0. */ -export { PLUGIN } from './plugin'; +export { PLUGIN, MAJOR_VERSION } from './plugin'; export { API_BASE_PATH } from './base_path'; export { EXTERNAL_LINKS } from './external_links'; export { APP_PERMISSION } from './permissions'; diff --git a/x-pack/plugins/license_management/common/constants/plugin.ts b/x-pack/plugins/license_management/common/constants/plugin.ts index ae7fd0f6e8a2e..76f4d94a0188a 100644 --- a/x-pack/plugins/license_management/common/constants/plugin.ts +++ b/x-pack/plugins/license_management/common/constants/plugin.ts @@ -13,3 +13,5 @@ export const PLUGIN = { }), id: 'license_management', }; + +export const MAJOR_VERSION = '8.0.0'; diff --git a/x-pack/plugins/license_management/server/config.ts b/x-pack/plugins/license_management/server/config.ts index 0e4de29b718be..e378a10191684 100644 --- a/x-pack/plugins/license_management/server/config.ts +++ b/x-pack/plugins/license_management/server/config.ts @@ -4,14 +4,90 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - +import { SemVer } from 'semver'; +import { i18n } from '@kbn/i18n'; +import { get } from 'lodash'; import { schema, TypeOf } from '@kbn/config-schema'; +import { PluginConfigDescriptor } from 'src/core/server'; + +import { MAJOR_VERSION } from '../common/constants'; + +const kibanaVersion = new SemVer(MAJOR_VERSION); + +// ------------------------------- +// >= 8.x +// ------------------------------- +const schemaLatest = schema.object( + { + ui: schema.object({ + enabled: schema.boolean({ defaultValue: true }), + }), + }, + { defaultValue: undefined } +); + +const configLatest: PluginConfigDescriptor = { + exposeToBrowser: { + ui: true, + }, + schema: schemaLatest, + deprecations: () => [], +}; -export const configSchema = schema.object({ - enabled: schema.boolean({ defaultValue: true }), - ui: schema.object({ +export type LicenseManagementConfig = TypeOf; + +// ------------------------------- +// 7.x +// ------------------------------- +const schema7x = schema.object( + { enabled: schema.boolean({ defaultValue: true }), - }), -}); + ui: schema.object({ + enabled: schema.boolean({ defaultValue: true }), + }), + }, + { defaultValue: undefined } +); + +export type LicenseManagementConfig7x = TypeOf; + +const config7x: PluginConfigDescriptor = { + exposeToBrowser: { + ui: true, + }, + schema: schema7x, + deprecations: () => [ + (completeConfig, rootPath, addDeprecation) => { + if (get(completeConfig, 'xpack.license_management.enabled') === undefined) { + return completeConfig; + } + + addDeprecation({ + configPath: 'xpack.license_management.enabled', + level: 'critical', + title: i18n.translate('xpack.licenseMgmt.deprecations.enabledTitle', { + defaultMessage: 'Setting "xpack.license_management.enabled" is deprecated', + }), + message: i18n.translate('xpack.licenseMgmt.deprecations.enabledMessage', { + defaultMessage: + 'To disallow users from accessing the License Management UI, use the "xpack.license_management.ui.enabled" setting instead of "xpack.license_management.enabled".', + }), + correctiveActions: { + manualSteps: [ + i18n.translate('xpack.licenseMgmt.deprecations.enabled.manualStepOneMessage', { + defaultMessage: 'Open the kibana.yml config file.', + }), + i18n.translate('xpack.licenseMgmt.deprecations.enabled.manualStepTwoMessage', { + defaultMessage: + 'Change the "xpack.license_management.enabled" setting to "xpack.license_management.ui.enabled".', + }), + ], + }, + }); + return completeConfig; + }, + ], +}; -export type LicenseManagementConfig = TypeOf; +export const config: PluginConfigDescriptor = + kibanaVersion.major < 8 ? config7x : configLatest; diff --git a/x-pack/plugins/license_management/server/index.ts b/x-pack/plugins/license_management/server/index.ts index e78ffe07b50c0..7aa6bfb06d54d 100644 --- a/x-pack/plugins/license_management/server/index.ts +++ b/x-pack/plugins/license_management/server/index.ts @@ -5,17 +5,10 @@ * 2.0. */ -import { PluginInitializerContext, PluginConfigDescriptor } from 'src/core/server'; +import { PluginInitializerContext } from 'src/core/server'; import { LicenseManagementServerPlugin } from './plugin'; -import { configSchema, LicenseManagementConfig } from './config'; -export const plugin = (ctx: PluginInitializerContext) => new LicenseManagementServerPlugin(); +export { config } from './config'; -export const config: PluginConfigDescriptor = { - schema: configSchema, - exposeToBrowser: { - ui: true, - }, - deprecations: ({ deprecate }) => [deprecate('enabled', '8.0.0')], -}; +export const plugin = (ctx: PluginInitializerContext) => new LicenseManagementServerPlugin(); diff --git a/x-pack/plugins/remote_clusters/common/constants.ts b/x-pack/plugins/remote_clusters/common/constants.ts index b11292141672d..5a36924b26433 100644 --- a/x-pack/plugins/remote_clusters/common/constants.ts +++ b/x-pack/plugins/remote_clusters/common/constants.ts @@ -20,6 +20,8 @@ export const PLUGIN = { }, }; +export const MAJOR_VERSION = '8.0.0'; + export const API_BASE_PATH = '/api/remote_clusters'; export const SNIFF_MODE = 'sniff'; diff --git a/x-pack/plugins/remote_clusters/server/config.ts b/x-pack/plugins/remote_clusters/server/config.ts index 8f379ec5613c8..5b4972f0a5259 100644 --- a/x-pack/plugins/remote_clusters/server/config.ts +++ b/x-pack/plugins/remote_clusters/server/config.ts @@ -4,23 +4,90 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - +import { SemVer } from 'semver'; +import { i18n } from '@kbn/i18n'; +import { get } from 'lodash'; import { schema, TypeOf } from '@kbn/config-schema'; -import { PluginConfigDescriptor } from 'kibana/server'; +import { PluginConfigDescriptor } from 'src/core/server'; + +import { MAJOR_VERSION } from '../common/constants'; + +const kibanaVersion = new SemVer(MAJOR_VERSION); + +// ------------------------------- +// >= 8.x +// ------------------------------- +const schemaLatest = schema.object( + { + ui: schema.object({ + enabled: schema.boolean({ defaultValue: true }), + }), + }, + { defaultValue: undefined } +); + +const configLatest: PluginConfigDescriptor = { + exposeToBrowser: { + ui: true, + }, + schema: schemaLatest, + deprecations: () => [], +}; + +export type RemoteClustersConfig = TypeOf; -export const configSchema = schema.object({ - enabled: schema.boolean({ defaultValue: true }), - ui: schema.object({ +// ------------------------------- +// 7.x +// ------------------------------- +const schema7x = schema.object( + { enabled: schema.boolean({ defaultValue: true }), - }), -}); + ui: schema.object({ + enabled: schema.boolean({ defaultValue: true }), + }), + }, + { defaultValue: undefined } +); -export type ConfigType = TypeOf; +export type RemoteClustersConfig7x = TypeOf; -export const config: PluginConfigDescriptor = { - deprecations: ({ deprecate }) => [deprecate('enabled', '8.0.0')], - schema: configSchema, +const config7x: PluginConfigDescriptor = { exposeToBrowser: { ui: true, }, + schema: schema7x, + deprecations: () => [ + (completeConfig, rootPath, addDeprecation) => { + if (get(completeConfig, 'xpack.remote_clusters.enabled') === undefined) { + return completeConfig; + } + + addDeprecation({ + configPath: 'xpack.remote_clusters.enabled', + level: 'critical', + title: i18n.translate('xpack.remoteClusters.deprecations.enabledTitle', { + defaultMessage: 'Setting "xpack.remote_clusters.enabled" is deprecated', + }), + message: i18n.translate('xpack.remoteClusters.deprecations.enabledMessage', { + defaultMessage: + 'To disallow users from accessing the Remote Clusters UI, use the "xpack.remote_clusters.ui.enabled" setting instead of "xpack.remote_clusters.enabled".', + }), + correctiveActions: { + manualSteps: [ + i18n.translate('xpack.remoteClusters.deprecations.enabled.manualStepOneMessage', { + defaultMessage: 'Open the kibana.yml config file.', + }), + i18n.translate('xpack.remoteClusters.deprecations.enabled.manualStepTwoMessage', { + defaultMessage: + 'Change the "xpack.remote_clusters.enabled" setting to "xpack.remote_clusters.ui.enabled".', + }), + ], + }, + }); + return completeConfig; + }, + ], }; + +export const config: PluginConfigDescriptor = + kibanaVersion.major < 8 ? config7x : configLatest; diff --git a/x-pack/plugins/remote_clusters/server/plugin.ts b/x-pack/plugins/remote_clusters/server/plugin.ts index b13773c27034a..fde71677b8448 100644 --- a/x-pack/plugins/remote_clusters/server/plugin.ts +++ b/x-pack/plugins/remote_clusters/server/plugin.ts @@ -11,7 +11,7 @@ import { CoreSetup, Logger, Plugin, PluginInitializerContext } from 'src/core/se import { PLUGIN } from '../common/constants'; import { Dependencies, LicenseStatus, RouteDependencies } from './types'; -import { ConfigType } from './config'; +import { RemoteClustersConfig, RemoteClustersConfig7x } from './config'; import { registerGetRoute, registerAddRoute, @@ -30,7 +30,7 @@ export class RemoteClustersServerPlugin { licenseStatus: LicenseStatus; log: Logger; - config: ConfigType; + config: RemoteClustersConfig | RemoteClustersConfig7x; constructor({ logger, config }: PluginInitializerContext) { this.log = logger.get(); diff --git a/x-pack/plugins/rollup/common/index.ts b/x-pack/plugins/rollup/common/index.ts index dffbfbd182092..c912a905d061d 100644 --- a/x-pack/plugins/rollup/common/index.ts +++ b/x-pack/plugins/rollup/common/index.ts @@ -14,6 +14,8 @@ export const PLUGIN = { minimumLicenseType: basicLicense, }; +export const MAJOR_VERSION = '8.0.0'; + export const CONFIG_ROLLUPS = 'rollups:enableIndexPatterns'; export const API_BASE_PATH = '/api/rollup'; diff --git a/x-pack/plugins/rollup/public/index.ts b/x-pack/plugins/rollup/public/index.ts index b70ce86493382..f740971b4bcb0 100644 --- a/x-pack/plugins/rollup/public/index.ts +++ b/x-pack/plugins/rollup/public/index.ts @@ -4,7 +4,8 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import { PluginInitializerContext } from 'src/core/public'; import { RollupPlugin } from './plugin'; -export const plugin = () => new RollupPlugin(); +export const plugin = (ctx: PluginInitializerContext) => new RollupPlugin(ctx); diff --git a/x-pack/plugins/rollup/public/plugin.ts b/x-pack/plugins/rollup/public/plugin.ts index 0d345e326193c..e458a13ee0e0e 100644 --- a/x-pack/plugins/rollup/public/plugin.ts +++ b/x-pack/plugins/rollup/public/plugin.ts @@ -6,7 +6,7 @@ */ import { i18n } from '@kbn/i18n'; -import { CoreSetup, CoreStart, Plugin } from 'kibana/public'; +import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from 'kibana/public'; import { rollupBadgeExtension, rollupToggleExtension } from './extend_index_management'; // @ts-ignore import { RollupIndexPatternCreationConfig } from './index_pattern_creation/rollup_index_pattern_creation_config'; @@ -23,6 +23,7 @@ import { IndexManagementPluginSetup } from '../../index_management/public'; import { setHttp, init as initDocumentation } from './crud_app/services/index'; import { setNotifications, setFatalErrors, setUiStatsReporter } from './kibana_services'; import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/public'; +import { ClientConfigType } from './types'; export interface RollupPluginSetupDependencies { home?: HomePublicPluginSetup; @@ -32,10 +33,16 @@ export interface RollupPluginSetupDependencies { } export class RollupPlugin implements Plugin { + constructor(private ctx: PluginInitializerContext) {} + setup( core: CoreSetup, { home, management, indexManagement, usageCollection }: RollupPluginSetupDependencies ) { + const { + ui: { enabled: isRollupUiEnabled }, + } = this.ctx.config.get(); + setFatalErrors(core.fatalErrors); if (usageCollection) { setUiStatsReporter(usageCollection.reportUiCounter.bind(usageCollection, UIM_APP_NAME)); @@ -46,7 +53,7 @@ export class RollupPlugin implements Plugin { indexManagement.extensionsService.addToggle(rollupToggleExtension); } - if (home) { + if (home && isRollupUiEnabled) { home.featureCatalogue.register({ id: 'rollup_jobs', title: 'Rollups', @@ -61,33 +68,35 @@ export class RollupPlugin implements Plugin { }); } - const pluginName = i18n.translate('xpack.rollupJobs.appTitle', { - defaultMessage: 'Rollup Jobs', - }); + if (isRollupUiEnabled) { + const pluginName = i18n.translate('xpack.rollupJobs.appTitle', { + defaultMessage: 'Rollup Jobs', + }); - management.sections.section.data.registerApp({ - id: 'rollup_jobs', - title: pluginName, - order: 4, - async mount(params) { - const [coreStart] = await core.getStartServices(); + management.sections.section.data.registerApp({ + id: 'rollup_jobs', + title: pluginName, + order: 4, + async mount(params) { + const [coreStart] = await core.getStartServices(); - const { - chrome: { docTitle }, - } = coreStart; + const { + chrome: { docTitle }, + } = coreStart; - docTitle.change(pluginName); - params.setBreadcrumbs([{ text: pluginName }]); + docTitle.change(pluginName); + params.setBreadcrumbs([{ text: pluginName }]); - const { renderApp } = await import('./application'); - const unmountAppCallback = await renderApp(core, params); + const { renderApp } = await import('./application'); + const unmountAppCallback = await renderApp(core, params); - return () => { - docTitle.reset(); - unmountAppCallback(); - }; - }, - }); + return () => { + docTitle.reset(); + unmountAppCallback(); + }; + }, + }); + } } start(core: CoreStart) { diff --git a/x-pack/plugins/rollup/public/types.ts b/x-pack/plugins/rollup/public/types.ts new file mode 100644 index 0000000000000..dc5e55e9268f8 --- /dev/null +++ b/x-pack/plugins/rollup/public/types.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export interface ClientConfigType { + ui: { + enabled: boolean; + }; +} diff --git a/x-pack/plugins/rollup/server/config.ts b/x-pack/plugins/rollup/server/config.ts index d20b317422107..c0cca4bbb4d33 100644 --- a/x-pack/plugins/rollup/server/config.ts +++ b/x-pack/plugins/rollup/server/config.ts @@ -4,11 +4,90 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - +import { SemVer } from 'semver'; +import { i18n } from '@kbn/i18n'; +import { get } from 'lodash'; import { schema, TypeOf } from '@kbn/config-schema'; +import { PluginConfigDescriptor } from 'src/core/server'; + +import { MAJOR_VERSION } from '../common'; + +const kibanaVersion = new SemVer(MAJOR_VERSION); + +// ------------------------------- +// >= 8.x +// ------------------------------- +const schemaLatest = schema.object( + { + ui: schema.object({ + enabled: schema.boolean({ defaultValue: true }), + }), + }, + { defaultValue: undefined } +); + +const configLatest: PluginConfigDescriptor = { + exposeToBrowser: { + ui: true, + }, + schema: schemaLatest, + deprecations: () => [], +}; + +export type RollupConfig = TypeOf; + +// ------------------------------- +// 7.x +// ------------------------------- +const schema7x = schema.object( + { + enabled: schema.boolean({ defaultValue: true }), + ui: schema.object({ + enabled: schema.boolean({ defaultValue: true }), + }), + }, + { defaultValue: undefined } +); + +export type RollupConfig7x = TypeOf; + +const config7x: PluginConfigDescriptor = { + exposeToBrowser: { + ui: true, + }, + schema: schema7x, + deprecations: () => [ + (completeConfig, rootPath, addDeprecation) => { + if (get(completeConfig, 'xpack.rollup.enabled') === undefined) { + return completeConfig; + } -export const configSchema = schema.object({ - enabled: schema.boolean({ defaultValue: true }), -}); + addDeprecation({ + configPath: 'xpack.rollup.enabled', + level: 'critical', + title: i18n.translate('xpack.rollupJobs.deprecations.enabledTitle', { + defaultMessage: 'Setting "xpack.rollup.enabled" is deprecated', + }), + message: i18n.translate('xpack.rollupJobs.deprecations.enabledMessage', { + defaultMessage: + 'To disallow users from accessing the Rollup Jobs UI, use the "xpack.rollup.ui.enabled" setting instead of "xpack.rollup.enabled".', + }), + correctiveActions: { + manualSteps: [ + i18n.translate('xpack.rollupJobs.deprecations.enabled.manualStepOneMessage', { + defaultMessage: 'Open the kibana.yml config file.', + }), + i18n.translate('xpack.rollupJobs.deprecations.enabled.manualStepTwoMessage', { + defaultMessage: + 'Change the "xpack.rollup.enabled" setting to "xpack.rollup.ui.enabled".', + }), + ], + }, + }); + return completeConfig; + }, + ], +}; -export type RollupConfig = TypeOf; +export const config: PluginConfigDescriptor = + kibanaVersion.major < 8 ? config7x : configLatest; diff --git a/x-pack/plugins/rollup/server/index.ts b/x-pack/plugins/rollup/server/index.ts index e77e0e6f15d72..6ae1d9f24b8b9 100644 --- a/x-pack/plugins/rollup/server/index.ts +++ b/x-pack/plugins/rollup/server/index.ts @@ -5,14 +5,10 @@ * 2.0. */ -import { PluginInitializerContext, PluginConfigDescriptor } from 'src/core/server'; +import { PluginInitializerContext } from 'src/core/server'; import { RollupPlugin } from './plugin'; -import { configSchema, RollupConfig } from './config'; + +export { config } from './config'; export const plugin = (pluginInitializerContext: PluginInitializerContext) => new RollupPlugin(pluginInitializerContext); - -export const config: PluginConfigDescriptor = { - deprecations: ({ deprecate }) => [deprecate('enabled', '8.0.0')], - schema: configSchema, -}; diff --git a/x-pack/plugins/snapshot_restore/common/constants.ts b/x-pack/plugins/snapshot_restore/common/constants.ts index b18e118dc5ff6..df13bd4c2f1f0 100644 --- a/x-pack/plugins/snapshot_restore/common/constants.ts +++ b/x-pack/plugins/snapshot_restore/common/constants.ts @@ -20,6 +20,8 @@ export const PLUGIN = { }, }; +export const MAJOR_VERSION = '8.0.0'; + export const API_BASE_PATH = '/api/snapshot_restore/'; export enum REPOSITORY_TYPES { diff --git a/x-pack/plugins/snapshot_restore/public/plugin.ts b/x-pack/plugins/snapshot_restore/public/plugin.ts index bb091a1fd1831..0351716fad5b5 100644 --- a/x-pack/plugins/snapshot_restore/public/plugin.ts +++ b/x-pack/plugins/snapshot_restore/public/plugin.ts @@ -42,52 +42,58 @@ export class SnapshotRestoreUIPlugin { public setup(coreSetup: CoreSetup, plugins: PluginsDependencies): void { const config = this.initializerContext.config.get(); - const { http } = coreSetup; - const { home, management, usageCollection } = plugins; + const { + ui: { enabled: isSnapshotRestoreUiEnabled }, + } = config; - // Initialize services - this.uiMetricService.setup(usageCollection); - textService.setup(i18n); - httpService.setup(http); + if (isSnapshotRestoreUiEnabled) { + const { http } = coreSetup; + const { home, management, usageCollection } = plugins; - management.sections.section.data.registerApp({ - id: PLUGIN.id, - title: i18n.translate('xpack.snapshotRestore.appTitle', { - defaultMessage: 'Snapshot and Restore', - }), - order: 3, - mount: async (params) => { - const { mountManagementSection } = await import('./application/mount_management_section'); - const services = { - uiMetricService: this.uiMetricService, - }; - return await mountManagementSection(coreSetup, services, config, params); - }, - }); + // Initialize services + this.uiMetricService.setup(usageCollection); + textService.setup(i18n); + httpService.setup(http); - if (home) { - home.featureCatalogue.register({ + management.sections.section.data.registerApp({ id: PLUGIN.id, - title: i18n.translate('xpack.snapshotRestore.featureCatalogueTitle', { - defaultMessage: 'Back up and restore', + title: i18n.translate('xpack.snapshotRestore.appTitle', { + defaultMessage: 'Snapshot and Restore', }), - description: i18n.translate('xpack.snapshotRestore.featureCatalogueDescription', { - defaultMessage: - 'Save snapshots to a backup repository, and restore to recover index and cluster state.', - }), - icon: 'storage', - path: '/app/management/data/snapshot_restore', - showOnHomePage: true, - category: FeatureCatalogueCategory.ADMIN, - order: 630, + order: 3, + mount: async (params) => { + const { mountManagementSection } = await import('./application/mount_management_section'); + const services = { + uiMetricService: this.uiMetricService, + }; + return await mountManagementSection(coreSetup, services, config, params); + }, }); - } - plugins.share.url.locators.create( - new SnapshotRestoreLocatorDefinition({ - managementAppLocator: plugins.management.locator, - }) - ); + if (home) { + home.featureCatalogue.register({ + id: PLUGIN.id, + title: i18n.translate('xpack.snapshotRestore.featureCatalogueTitle', { + defaultMessage: 'Back up and restore', + }), + description: i18n.translate('xpack.snapshotRestore.featureCatalogueDescription', { + defaultMessage: + 'Save snapshots to a backup repository, and restore to recover index and cluster state.', + }), + icon: 'storage', + path: '/app/management/data/snapshot_restore', + showOnHomePage: true, + category: FeatureCatalogueCategory.ADMIN, + order: 630, + }); + } + + plugins.share.url.locators.create( + new SnapshotRestoreLocatorDefinition({ + managementAppLocator: plugins.management.locator, + }) + ); + } } public start() {} diff --git a/x-pack/plugins/snapshot_restore/public/types.ts b/x-pack/plugins/snapshot_restore/public/types.ts index b73170ad9d578..c58c942b4bc16 100644 --- a/x-pack/plugins/snapshot_restore/public/types.ts +++ b/x-pack/plugins/snapshot_restore/public/types.ts @@ -7,4 +7,5 @@ export interface ClientConfigType { slm_ui: { enabled: boolean }; + ui: { enabled: boolean }; } diff --git a/x-pack/plugins/snapshot_restore/server/config.ts b/x-pack/plugins/snapshot_restore/server/config.ts index f0ca416ef2032..cc430f4756610 100644 --- a/x-pack/plugins/snapshot_restore/server/config.ts +++ b/x-pack/plugins/snapshot_restore/server/config.ts @@ -4,14 +4,98 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - +import { SemVer } from 'semver'; +import { i18n } from '@kbn/i18n'; +import { get } from 'lodash'; import { schema, TypeOf } from '@kbn/config-schema'; +import { PluginConfigDescriptor } from 'src/core/server'; + +import { MAJOR_VERSION } from '../common/constants'; + +const kibanaVersion = new SemVer(MAJOR_VERSION); + +// ------------------------------- +// >= 8.x +// ------------------------------- +const schemaLatest = schema.object( + { + ui: schema.object({ + enabled: schema.boolean({ defaultValue: true }), + }), + slm_ui: schema.object({ + enabled: schema.boolean({ defaultValue: true }), + }), + }, + { defaultValue: undefined } +); + +const configLatest: PluginConfigDescriptor = { + exposeToBrowser: { + ui: true, + slm_ui: true, + }, + schema: schemaLatest, + deprecations: () => [], +}; -export const configSchema = schema.object({ - enabled: schema.boolean({ defaultValue: true }), - slm_ui: schema.object({ +export type SnapshotRestoreConfig = TypeOf; + +// ------------------------------- +// 7.x +// ------------------------------- +const schema7x = schema.object( + { enabled: schema.boolean({ defaultValue: true }), - }), -}); + ui: schema.object({ + enabled: schema.boolean({ defaultValue: true }), + }), + slm_ui: schema.object({ + enabled: schema.boolean({ defaultValue: true }), + }), + }, + { defaultValue: undefined } +); + +export type SnapshotRestoreConfig7x = TypeOf; + +const config7x: PluginConfigDescriptor = { + exposeToBrowser: { + ui: true, + slm_ui: true, + }, + schema: schema7x, + deprecations: () => [ + (completeConfig, rootPath, addDeprecation) => { + if (get(completeConfig, 'xpack.snapshot_restore.enabled') === undefined) { + return completeConfig; + } + + addDeprecation({ + configPath: 'xpack.snapshot_restore.enabled', + level: 'critical', + title: i18n.translate('xpack.snapshotRestore.deprecations.enabledTitle', { + defaultMessage: 'Setting "xpack.snapshot_restore.enabled" is deprecated', + }), + message: i18n.translate('xpack.snapshotRestore.deprecations.enabledMessage', { + defaultMessage: + 'To disallow users from accessing the Snapshot and Restore UI, use the "xpack.snapshot_restore.ui.enabled" setting instead of "xpack.snapshot_restore.enabled".', + }), + correctiveActions: { + manualSteps: [ + i18n.translate('xpack.snapshotRestore.deprecations.enabled.manualStepOneMessage', { + defaultMessage: 'Open the kibana.yml config file.', + }), + i18n.translate('xpack.snapshotRestore.deprecations.enabled.manualStepTwoMessage', { + defaultMessage: + 'Change the "xpack.snapshot_restore.enabled" setting to "xpack.snapshot_restore.ui.enabled".', + }), + ], + }, + }); + return completeConfig; + }, + ], +}; -export type SnapshotRestoreConfig = TypeOf; +export const config: PluginConfigDescriptor = + kibanaVersion.major < 8 ? config7x : configLatest; diff --git a/x-pack/plugins/snapshot_restore/server/index.ts b/x-pack/plugins/snapshot_restore/server/index.ts index e10bffd6073d2..1e9d2b55aa20b 100644 --- a/x-pack/plugins/snapshot_restore/server/index.ts +++ b/x-pack/plugins/snapshot_restore/server/index.ts @@ -5,16 +5,9 @@ * 2.0. */ -import { PluginInitializerContext, PluginConfigDescriptor } from 'kibana/server'; +import { PluginInitializerContext } from 'kibana/server'; import { SnapshotRestoreServerPlugin } from './plugin'; -import { configSchema, SnapshotRestoreConfig } from './config'; -export const plugin = (ctx: PluginInitializerContext) => new SnapshotRestoreServerPlugin(ctx); +export { config } from './config'; -export const config: PluginConfigDescriptor = { - deprecations: ({ deprecate }) => [deprecate('enabled', '8.0.0')], - schema: configSchema, - exposeToBrowser: { - slm_ui: true, - }, -}; +export const plugin = (ctx: PluginInitializerContext) => new SnapshotRestoreServerPlugin(ctx); diff --git a/x-pack/plugins/snapshot_restore/server/plugin.ts b/x-pack/plugins/snapshot_restore/server/plugin.ts index 4414e3735959b..d737807ec8dad 100644 --- a/x-pack/plugins/snapshot_restore/server/plugin.ts +++ b/x-pack/plugins/snapshot_restore/server/plugin.ts @@ -28,16 +28,9 @@ export class SnapshotRestoreServerPlugin implements Plugin this.license = new License(); } - public setup( - { http, getStartServices }: CoreSetup, - { licensing, features, security, cloud }: Dependencies - ): void { + public setup({ http }: CoreSetup, { licensing, features, security, cloud }: Dependencies): void { const pluginConfig = this.context.config.get(); - if (!pluginConfig.enabled) { - return; - } - const router = http.createRouter(); this.license.setup( diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/index.ts b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/index.ts index b19c8b3d0f082..b2a1c4e80ec7d 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/index.ts +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/index.ts @@ -9,4 +9,4 @@ export { setup as setupOverviewPage, OverviewTestBed } from './overview.helpers' export { setup as setupElasticsearchPage, ElasticsearchTestBed } from './elasticsearch.helpers'; export { setup as setupKibanaPage, KibanaTestBed } from './kibana.helpers'; -export { setupEnvironment } from './setup_environment'; +export { setupEnvironment, kibanaVersion } from './setup_environment'; diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/setup_environment.tsx b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/setup_environment.tsx index a1cdfaa3446cb..fbbbc0e07853c 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/setup_environment.tsx +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/helpers/setup_environment.tsx @@ -9,7 +9,7 @@ import React from 'react'; import axios from 'axios'; // @ts-ignore import axiosXhrAdapter from 'axios/lib/adapters/xhr'; - +import SemVer from 'semver/classes/semver'; import { deprecationsServiceMock, docLinksServiceMock, @@ -19,7 +19,7 @@ import { import { HttpSetup } from 'src/core/public'; import { KibanaContextProvider } from '../../../public/shared_imports'; -import { mockKibanaSemverVersion } from '../../../common/constants'; +import { MAJOR_VERSION } from '../../../common/constants'; import { AppContextProvider } from '../../../public/application/app_context'; import { apiService } from '../../../public/application/lib/api'; import { breadcrumbService } from '../../../public/application/lib/breadcrumbs'; @@ -31,6 +31,8 @@ const { GlobalFlyoutProvider } = GlobalFlyout; const mockHttpClient = axios.create({ adapter: axiosXhrAdapter }); +export const kibanaVersion = new SemVer(MAJOR_VERSION); + export const WithAppDependencies = (Comp: any, overrides: Record = {}) => (props: Record) => { @@ -41,9 +43,9 @@ export const WithAppDependencies = http: mockHttpClient as unknown as HttpSetup, docLinks: docLinksServiceMock.createStartContract(), kibanaVersionInfo: { - currentMajor: mockKibanaSemverVersion.major, - prevMajor: mockKibanaSemverVersion.major - 1, - nextMajor: mockKibanaSemverVersion.major + 1, + currentMajor: kibanaVersion.major, + prevMajor: kibanaVersion.major - 1, + nextMajor: kibanaVersion.major + 1, }, notifications: notificationServiceMock.createStartContract(), isReadOnlyMode: false, diff --git a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/overview.test.tsx b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/overview.test.tsx index 0acf5ae65c6cc..7831ab0110e4f 100644 --- a/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/overview.test.tsx +++ b/x-pack/plugins/upgrade_assistant/__jest__/client_integration/overview/overview.test.tsx @@ -5,8 +5,7 @@ * 2.0. */ -import { mockKibanaSemverVersion } from '../../../common/constants'; -import { OverviewTestBed, setupOverviewPage, setupEnvironment } from '../helpers'; +import { OverviewTestBed, setupOverviewPage, setupEnvironment, kibanaVersion } from '../helpers'; describe('Overview Page', () => { let testBed: OverviewTestBed; @@ -24,7 +23,7 @@ describe('Overview Page', () => { describe('Documentation links', () => { test('Has a whatsNew link and it references nextMajor version', () => { const { exists, find } = testBed; - const nextMajor = mockKibanaSemverVersion.major + 1; + const nextMajor = kibanaVersion.major + 1; expect(exists('whatsNewLink')).toBe(true); expect(find('whatsNewLink').text()).toContain(`${nextMajor}.0`); diff --git a/x-pack/plugins/upgrade_assistant/common/config.ts b/x-pack/plugins/upgrade_assistant/common/config.ts deleted file mode 100644 index e74fe5cc1bf16..0000000000000 --- a/x-pack/plugins/upgrade_assistant/common/config.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { schema, TypeOf } from '@kbn/config-schema'; - -export const configSchema = schema.object({ - enabled: schema.boolean({ defaultValue: true }), - /* - * This will default to true up until the last minor before the next major. - * In readonly mode, the user will not be able to perform any actions in the UI - * and will be presented with a message indicating as such. - */ - readonly: schema.boolean({ defaultValue: true }), -}); - -export type Config = TypeOf; diff --git a/x-pack/plugins/upgrade_assistant/common/constants.ts b/x-pack/plugins/upgrade_assistant/common/constants.ts index 893d61d329534..68a6b9e9cdb83 100644 --- a/x-pack/plugins/upgrade_assistant/common/constants.ts +++ b/x-pack/plugins/upgrade_assistant/common/constants.ts @@ -4,15 +4,11 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import SemVer from 'semver/classes/semver'; - /* - * These constants are used only in tests to add conditional logic based on Kibana version * On master, the version should represent the next major version (e.g., master --> 8.0.0) * The release branch should match the release version (e.g., 7.x --> 7.0.0) */ -export const mockKibanaVersion = '8.0.0'; -export const mockKibanaSemverVersion = new SemVer(mockKibanaVersion); +export const MAJOR_VERSION = '8.0.0'; /* * Map of 7.0 --> 8.0 index setting deprecation log messages and associated settings diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/warning_step.test.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/warning_step.test.tsx index ff11b9f1a8450..d2cafd69e94eb 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/warning_step.test.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/es_deprecations/deprecation_types/reindex/flyout/warning_step.test.tsx @@ -8,12 +8,20 @@ import { I18nProvider } from '@kbn/i18n/react'; import { mount, shallow } from 'enzyme'; import React from 'react'; +import SemVer from 'semver/classes/semver'; import { ReindexWarning } from '../../../../../../../common/types'; -import { mockKibanaSemverVersion } from '../../../../../../../common/constants'; +import { MAJOR_VERSION } from '../../../../../../../common/constants'; import { idForWarning, WarningsFlyoutStep } from './warnings_step'; +const kibanaVersion = new SemVer(MAJOR_VERSION); +const mockKibanaVersionInfo = { + currentMajor: kibanaVersion.major, + prevMajor: kibanaVersion.major - 1, + nextMajor: kibanaVersion.major + 1, +}; + jest.mock('../../../../../app_context', () => { const { docLinksServiceMock } = jest.requireActual( '../../../../../../../../../../src/core/public/doc_links/doc_links_service.mock' @@ -23,11 +31,7 @@ jest.mock('../../../../../app_context', () => { useAppContext: () => { return { docLinks: docLinksServiceMock.createStartContract(), - kibanaVersionInfo: { - currentMajor: mockKibanaSemverVersion.major, - prevMajor: mockKibanaSemverVersion.major - 1, - nextMajor: mockKibanaSemverVersion.major + 1, - }, + kibanaVersionInfo: mockKibanaVersionInfo, }; }, }; @@ -45,7 +49,7 @@ describe('WarningsFlyoutStep', () => { expect(shallow()).toMatchSnapshot(); }); - if (mockKibanaSemverVersion.major === 7) { + if (kibanaVersion.major === 7) { it('does not allow proceeding until all are checked', () => { const defaultPropsWithWarnings = { ...defaultProps, diff --git a/x-pack/plugins/upgrade_assistant/public/plugin.ts b/x-pack/plugins/upgrade_assistant/public/plugin.ts index 5edb638e1bc5b..32e825fbdc20d 100644 --- a/x-pack/plugins/upgrade_assistant/public/plugin.ts +++ b/x-pack/plugins/upgrade_assistant/public/plugin.ts @@ -9,59 +9,69 @@ import SemVer from 'semver/classes/semver'; import { i18n } from '@kbn/i18n'; import { Plugin, CoreSetup, PluginInitializerContext } from 'src/core/public'; -import { SetupDependencies, StartDependencies, AppServicesContext } from './types'; -import { Config } from '../common/config'; +import { + SetupDependencies, + StartDependencies, + AppServicesContext, + ClientConfigType, +} from './types'; export class UpgradeAssistantUIPlugin implements Plugin { constructor(private ctx: PluginInitializerContext) {} + setup(coreSetup: CoreSetup, { management, cloud }: SetupDependencies) { - const { readonly } = this.ctx.config.get(); + const { + readonly, + ui: { enabled: isUpgradeAssistantUiEnabled }, + } = this.ctx.config.get(); - const appRegistrar = management.sections.section.stack; - const kibanaVersion = new SemVer(this.ctx.env.packageInfo.version); + if (isUpgradeAssistantUiEnabled) { + const appRegistrar = management.sections.section.stack; + const kibanaVersion = new SemVer(this.ctx.env.packageInfo.version); - const kibanaVersionInfo = { - currentMajor: kibanaVersion.major, - prevMajor: kibanaVersion.major - 1, - nextMajor: kibanaVersion.major + 1, - }; + const kibanaVersionInfo = { + currentMajor: kibanaVersion.major, + prevMajor: kibanaVersion.major - 1, + nextMajor: kibanaVersion.major + 1, + }; - const pluginName = i18n.translate('xpack.upgradeAssistant.appTitle', { - defaultMessage: '{version} Upgrade Assistant', - values: { version: `${kibanaVersionInfo.nextMajor}.0` }, - }); + const pluginName = i18n.translate('xpack.upgradeAssistant.appTitle', { + defaultMessage: '{version} Upgrade Assistant', + values: { version: `${kibanaVersionInfo.nextMajor}.0` }, + }); - appRegistrar.registerApp({ - id: 'upgrade_assistant', - title: pluginName, - order: 1, - async mount(params) { - const [coreStart, { discover, data }] = await coreSetup.getStartServices(); - const services: AppServicesContext = { discover, data, cloud }; + appRegistrar.registerApp({ + id: 'upgrade_assistant', + title: pluginName, + order: 1, + async mount(params) { + const [coreStart, { discover, data }] = await coreSetup.getStartServices(); + const services: AppServicesContext = { discover, data, cloud }; - const { - chrome: { docTitle }, - } = coreStart; + const { + chrome: { docTitle }, + } = coreStart; - docTitle.change(pluginName); + docTitle.change(pluginName); - const { mountManagementSection } = await import('./application/mount_management_section'); - const unmountAppCallback = await mountManagementSection( - coreSetup, - params, - kibanaVersionInfo, - readonly, - services - ); + const { mountManagementSection } = await import('./application/mount_management_section'); + const unmountAppCallback = await mountManagementSection( + coreSetup, + params, + kibanaVersionInfo, + readonly, + services + ); - return () => { - docTitle.reset(); - unmountAppCallback(); - }; - }, - }); + return () => { + docTitle.reset(); + unmountAppCallback(); + }; + }, + }); + } } start() {} diff --git a/x-pack/plugins/upgrade_assistant/public/types.ts b/x-pack/plugins/upgrade_assistant/public/types.ts index a2b49305c32d4..cbeaf22bb095b 100644 --- a/x-pack/plugins/upgrade_assistant/public/types.ts +++ b/x-pack/plugins/upgrade_assistant/public/types.ts @@ -26,3 +26,10 @@ export interface StartDependencies { discover: DiscoverStart; data: DataPublicPluginStart; } + +export interface ClientConfigType { + readonly: boolean; + ui: { + enabled: boolean; + }; +} diff --git a/x-pack/plugins/upgrade_assistant/server/config.ts b/x-pack/plugins/upgrade_assistant/server/config.ts new file mode 100644 index 0000000000000..4183ea337def1 --- /dev/null +++ b/x-pack/plugins/upgrade_assistant/server/config.ts @@ -0,0 +1,107 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { SemVer } from 'semver'; +import { i18n } from '@kbn/i18n'; +import { get } from 'lodash'; +import { schema, TypeOf } from '@kbn/config-schema'; +import { PluginConfigDescriptor } from 'src/core/server'; + +import { MAJOR_VERSION } from '../common/constants'; + +const kibanaVersion = new SemVer(MAJOR_VERSION); + +// ------------------------------- +// >= 8.x +// ------------------------------- +const schemaLatest = schema.object( + { + ui: schema.object({ + enabled: schema.boolean({ defaultValue: true }), + }), + /* + * This will default to true up until the last minor before the next major. + * In readonly mode, the user will not be able to perform any actions in the UI + * and will be presented with a message indicating as such. + */ + readonly: schema.boolean({ defaultValue: true }), + }, + { defaultValue: undefined } +); + +const configLatest: PluginConfigDescriptor = { + exposeToBrowser: { + ui: true, + readonly: true, + }, + schema: schemaLatest, + deprecations: () => [], +}; + +export type UpgradeAssistantConfig = TypeOf; + +// ------------------------------- +// 7.x +// ------------------------------- +const schema7x = schema.object( + { + enabled: schema.boolean({ defaultValue: true }), + ui: schema.object({ + enabled: schema.boolean({ defaultValue: true }), + }), + /* + * This will default to true up until the last minor before the next major. + * In readonly mode, the user will not be able to perform any actions in the UI + * and will be presented with a message indicating as such. + */ + readonly: schema.boolean({ defaultValue: true }), + }, + { defaultValue: undefined } +); + +export type UpgradeAssistantConfig7x = TypeOf; + +const config7x: PluginConfigDescriptor = { + exposeToBrowser: { + ui: true, + readonly: true, + }, + schema: schema7x, + deprecations: () => [ + (completeConfig, rootPath, addDeprecation) => { + if (get(completeConfig, 'xpack.upgrade_assistant.enabled') === undefined) { + return completeConfig; + } + + addDeprecation({ + configPath: 'xpack.upgrade_assistant.enabled', + level: 'critical', + title: i18n.translate('xpack.upgradeAssistant.deprecations.enabledTitle', { + defaultMessage: 'Setting "xpack.upgrade_assistant.enabled" is deprecated', + }), + message: i18n.translate('xpack.upgradeAssistant.deprecations.enabledMessage', { + defaultMessage: + 'To disallow users from accessing the Upgrade Assistant UI, use the "xpack.upgrade_assistant.ui.enabled" setting instead of "xpack.upgrade_assistant.enabled".', + }), + correctiveActions: { + manualSteps: [ + i18n.translate('xpack.upgradeAssistant.deprecations.enabled.manualStepOneMessage', { + defaultMessage: 'Open the kibana.yml config file.', + }), + i18n.translate('xpack.upgradeAssistant.deprecations.enabled.manualStepTwoMessage', { + defaultMessage: + 'Change the "xpack.upgrade_assistant.enabled" setting to "xpack.upgrade_assistant.ui.enabled".', + }), + ], + }, + }); + return completeConfig; + }, + ], +}; + +export const config: PluginConfigDescriptor = + kibanaVersion.major < 8 ? config7x : configLatest; diff --git a/x-pack/plugins/upgrade_assistant/server/index.ts b/x-pack/plugins/upgrade_assistant/server/index.ts index 5591276b2fa34..660aa107292e8 100644 --- a/x-pack/plugins/upgrade_assistant/server/index.ts +++ b/x-pack/plugins/upgrade_assistant/server/index.ts @@ -5,18 +5,11 @@ * 2.0. */ -import { PluginInitializerContext, PluginConfigDescriptor } from 'src/core/server'; +import { PluginInitializerContext } from 'src/core/server'; import { UpgradeAssistantServerPlugin } from './plugin'; -import { configSchema, Config } from '../common/config'; + +export { config } from './config'; export const plugin = (ctx: PluginInitializerContext) => { return new UpgradeAssistantServerPlugin(ctx); }; - -export const config: PluginConfigDescriptor = { - deprecations: ({ deprecate }) => [deprecate('enabled', '8.0.0')], - schema: configSchema, - exposeToBrowser: { - readonly: true, - }, -}; diff --git a/x-pack/plugins/upgrade_assistant/server/lib/__fixtures__/version.ts b/x-pack/plugins/upgrade_assistant/server/lib/__fixtures__/version.ts index d93fe7920f1d7..5f39e902c75d9 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/__fixtures__/version.ts +++ b/x-pack/plugins/upgrade_assistant/server/lib/__fixtures__/version.ts @@ -4,14 +4,16 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import { SemVer } from 'semver'; +import { MAJOR_VERSION } from '../../../common/constants'; -import { mockKibanaSemverVersion } from '../../../common/constants'; +const kibanaVersion = new SemVer(MAJOR_VERSION); export const getMockVersionInfo = () => { - const currentMajor = mockKibanaSemverVersion.major; + const currentMajor = kibanaVersion.major; return { - currentVersion: mockKibanaSemverVersion, + currentVersion: kibanaVersion, currentMajor, prevMajor: currentMajor - 1, nextMajor: currentMajor + 1, diff --git a/x-pack/plugins/upgrade_assistant/server/lib/es_version_precheck.test.ts b/x-pack/plugins/upgrade_assistant/server/lib/es_version_precheck.test.ts index e1817ef63927d..1785491e5da45 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/es_version_precheck.test.ts +++ b/x-pack/plugins/upgrade_assistant/server/lib/es_version_precheck.test.ts @@ -9,7 +9,7 @@ import { SemVer } from 'semver'; import { IScopedClusterClient, kibanaResponseFactory } from 'src/core/server'; import { coreMock } from 'src/core/server/mocks'; import { licensingMock } from '../../../../plugins/licensing/server/mocks'; -import { mockKibanaVersion } from '../../common/constants'; +import { MAJOR_VERSION } from '../../common/constants'; import { getMockVersionInfo } from './__fixtures__/version'; import { @@ -98,7 +98,7 @@ describe('verifyAllMatchKibanaVersion', () => { describe('EsVersionPrecheck', () => { beforeEach(() => { - versionService.setup(mockKibanaVersion); + versionService.setup(MAJOR_VERSION); }); it('returns a 403 when callCluster fails with a 403', async () => { diff --git a/x-pack/plugins/upgrade_assistant/server/lib/reindexing/index_settings.test.ts b/x-pack/plugins/upgrade_assistant/server/lib/reindexing/index_settings.test.ts index 30093a9fb6e50..957198cde8da9 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/reindexing/index_settings.test.ts +++ b/x-pack/plugins/upgrade_assistant/server/lib/reindexing/index_settings.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { mockKibanaSemverVersion, mockKibanaVersion } from '../../../common/constants'; +import { MAJOR_VERSION } from '../../../common/constants'; import { versionService } from '../version'; import { getMockVersionInfo } from '../__fixtures__/version'; @@ -131,7 +131,7 @@ describe('transformFlatSettings', () => { describe('sourceNameForIndex', () => { beforeEach(() => { - versionService.setup(mockKibanaVersion); + versionService.setup(MAJOR_VERSION); }); it('parses internal indices', () => { @@ -152,7 +152,7 @@ describe('transformFlatSettings', () => { describe('generateNewIndexName', () => { beforeEach(() => { - versionService.setup(mockKibanaVersion); + versionService.setup(MAJOR_VERSION); }); it('parses internal indices', () => { @@ -186,7 +186,7 @@ describe('transformFlatSettings', () => { ).toEqual([]); }); - if (mockKibanaSemverVersion.major === 7) { + if (currentMajor === 7) { describe('[7.x] customTypeName warning', () => { it('returns customTypeName warning for non-_doc mapping types', () => { expect( diff --git a/x-pack/plugins/upgrade_assistant/server/lib/reindexing/reindex_actions.test.ts b/x-pack/plugins/upgrade_assistant/server/lib/reindexing/reindex_actions.test.ts index 3cfdb1fdd3167..ce1e8e11eb2d1 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/reindexing/reindex_actions.test.ts +++ b/x-pack/plugins/upgrade_assistant/server/lib/reindexing/reindex_actions.test.ts @@ -19,7 +19,7 @@ import { ReindexStatus, ReindexStep, } from '../../../common/types'; -import { mockKibanaVersion } from '../../../common/constants'; +import { MAJOR_VERSION } from '../../../common/constants'; import { versionService } from '../version'; import { LOCK_WINDOW, ReindexActions, reindexActionsFactory } from './reindex_actions'; import { getMockVersionInfo } from '../__fixtures__/version'; @@ -54,7 +54,7 @@ describe('ReindexActions', () => { describe('createReindexOp', () => { beforeEach(() => { - versionService.setup(mockKibanaVersion); + versionService.setup(MAJOR_VERSION); client.create.mockResolvedValue(); }); diff --git a/x-pack/plugins/upgrade_assistant/server/lib/reindexing/reindex_service.test.ts b/x-pack/plugins/upgrade_assistant/server/lib/reindexing/reindex_service.test.ts index 7a5bf1c187698..6017691a9328d 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/reindexing/reindex_service.test.ts +++ b/x-pack/plugins/upgrade_assistant/server/lib/reindexing/reindex_service.test.ts @@ -20,7 +20,7 @@ import { ReindexStatus, ReindexStep, } from '../../../common/types'; -import { mockKibanaVersion } from '../../../common/constants'; +import { MAJOR_VERSION } from '../../../common/constants'; import { licensingMock } from '../../../../licensing/server/mocks'; import { LicensingPluginSetup } from '../../../../licensing/server'; @@ -89,7 +89,7 @@ describe('reindexService', () => { licensingPluginSetup ); - versionService.setup(mockKibanaVersion); + versionService.setup(MAJOR_VERSION); }); describe('hasRequiredPrivileges', () => { From c1b0565acdbbcf7432a46a0664a91c34f299dab3 Mon Sep 17 00:00:00 2001 From: Tre Date: Tue, 19 Oct 2021 06:56:35 -0400 Subject: [PATCH 046/204] [QA][refactor] Use ui settings - sample data (#114530) --- test/functional/apps/home/_sample_data.ts | 21 +++++++++------------ test/functional/page_objects/common_page.ts | 9 +++++++++ 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/test/functional/apps/home/_sample_data.ts b/test/functional/apps/home/_sample_data.ts index 3cf387133bc9c..e0a96940337e2 100644 --- a/test/functional/apps/home/_sample_data.ts +++ b/test/functional/apps/home/_sample_data.ts @@ -31,6 +31,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { after(async () => { await security.testUser.restoreDefaults(); + await PageObjects.common.unsetTime(); }); it('should display registered flights sample data sets', async () => { @@ -74,6 +75,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('dashboard', () => { beforeEach(async () => { + await time(); await PageObjects.common.navigateToUrl('home', '/tutorial_directory/sampleData', { useActualUrl: true, }); @@ -84,10 +86,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.home.launchSampleDashboard('flights'); await PageObjects.header.waitUntilLoadingHasFinished(); await renderable.waitForRender(); - const todayYearMonthDay = moment().format('MMM D, YYYY'); - const fromTime = `${todayYearMonthDay} @ 00:00:00.000`; - const toTime = `${todayYearMonthDay} @ 23:59:59.999`; - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); const panelCount = await PageObjects.dashboard.getPanelCount(); expect(panelCount).to.be(17); }); @@ -112,10 +110,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.home.launchSampleDashboard('logs'); await PageObjects.header.waitUntilLoadingHasFinished(); await renderable.waitForRender(); - const todayYearMonthDay = moment().format('MMM D, YYYY'); - const fromTime = `${todayYearMonthDay} @ 00:00:00.000`; - const toTime = `${todayYearMonthDay} @ 23:59:59.999`; - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); const panelCount = await PageObjects.dashboard.getPanelCount(); expect(panelCount).to.be(13); }); @@ -124,10 +118,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.home.launchSampleDashboard('ecommerce'); await PageObjects.header.waitUntilLoadingHasFinished(); await renderable.waitForRender(); - const todayYearMonthDay = moment().format('MMM D, YYYY'); - const fromTime = `${todayYearMonthDay} @ 00:00:00.000`; - const toTime = `${todayYearMonthDay} @ 23:59:59.999`; - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); const panelCount = await PageObjects.dashboard.getPanelCount(); expect(panelCount).to.be(15); }); @@ -160,5 +150,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(isInstalled).to.be(false); }); }); + + async function time() { + const today = moment().format('MMM D, YYYY'); + const from = `${today} @ 00:00:00.000`; + const to = `${today} @ 23:59:59.999`; + await PageObjects.common.setTime({ from, to }); + } }); } diff --git a/test/functional/page_objects/common_page.ts b/test/functional/page_objects/common_page.ts index 64fb184f40e48..a40465b00dbeb 100644 --- a/test/functional/page_objects/common_page.ts +++ b/test/functional/page_objects/common_page.ts @@ -30,6 +30,7 @@ export class CommonPageObject extends FtrService { private readonly globalNav = this.ctx.getService('globalNav'); private readonly testSubjects = this.ctx.getService('testSubjects'); private readonly loginPage = this.ctx.getPageObject('login'); + private readonly kibanaServer = this.ctx.getService('kibanaServer'); private readonly defaultTryTimeout = this.config.get('timeouts.try'); private readonly defaultFindTimeout = this.config.get('timeouts.find'); @@ -500,4 +501,12 @@ export class CommonPageObject extends FtrService { await this.testSubjects.exists(validator); } } + + async setTime(time: { from: string; to: string }) { + await this.kibanaServer.uiSettings.replace({ 'timepicker:timeDefaults': JSON.stringify(time) }); + } + + async unsetTime() { + await this.kibanaServer.uiSettings.unset('timepicker:timeDefaults'); + } } From f8041e6005a10b73fd771b9b8e2c8d9a22bfce84 Mon Sep 17 00:00:00 2001 From: Pete Harverson Date: Tue, 19 Oct 2021 11:57:10 +0100 Subject: [PATCH 047/204] [ML] Delete annotation directly from the index it is stored in (#115328) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../ml/common/constants/index_patterns.ts | 1 - .../ml/server/lib/check_annotations/index.ts | 11 ++----- .../annotation_service/annotation.test.ts | 3 +- .../models/annotation_service/annotation.ts | 33 ++++++++++++++++--- 4 files changed, 32 insertions(+), 16 deletions(-) diff --git a/x-pack/plugins/ml/common/constants/index_patterns.ts b/x-pack/plugins/ml/common/constants/index_patterns.ts index d7d6c343e282b..9a8e5c1b8ae78 100644 --- a/x-pack/plugins/ml/common/constants/index_patterns.ts +++ b/x-pack/plugins/ml/common/constants/index_patterns.ts @@ -7,7 +7,6 @@ export const ML_ANNOTATIONS_INDEX_ALIAS_READ = '.ml-annotations-read'; export const ML_ANNOTATIONS_INDEX_ALIAS_WRITE = '.ml-annotations-write'; -export const ML_ANNOTATIONS_INDEX_PATTERN = '.ml-annotations-6'; export const ML_RESULTS_INDEX_PATTERN = '.ml-anomalies-*'; export const ML_NOTIFICATION_INDEX_PATTERN = '.ml-notifications*'; diff --git a/x-pack/plugins/ml/server/lib/check_annotations/index.ts b/x-pack/plugins/ml/server/lib/check_annotations/index.ts index a388a24d082a6..e64b4658588cb 100644 --- a/x-pack/plugins/ml/server/lib/check_annotations/index.ts +++ b/x-pack/plugins/ml/server/lib/check_annotations/index.ts @@ -11,22 +11,15 @@ import { mlLog } from '../../lib/log'; import { ML_ANNOTATIONS_INDEX_ALIAS_READ, ML_ANNOTATIONS_INDEX_ALIAS_WRITE, - ML_ANNOTATIONS_INDEX_PATTERN, } from '../../../common/constants/index_patterns'; // Annotations Feature is available if: -// - ML_ANNOTATIONS_INDEX_PATTERN index is present // - ML_ANNOTATIONS_INDEX_ALIAS_READ alias is present // - ML_ANNOTATIONS_INDEX_ALIAS_WRITE alias is present +// Note there is no need to check for the existence of the indices themselves as aliases are stored +// in the metadata of the indices they point to, so it's impossible to have an alias that doesn't point to any index. export async function isAnnotationsFeatureAvailable({ asInternalUser }: IScopedClusterClient) { try { - const indexParams = { index: ML_ANNOTATIONS_INDEX_PATTERN }; - - const { body: annotationsIndexExists } = await asInternalUser.indices.exists(indexParams); - if (!annotationsIndexExists) { - return false; - } - const { body: annotationsReadAliasExists } = await asInternalUser.indices.existsAlias({ index: ML_ANNOTATIONS_INDEX_ALIAS_READ, name: ML_ANNOTATIONS_INDEX_ALIAS_READ, diff --git a/x-pack/plugins/ml/server/models/annotation_service/annotation.test.ts b/x-pack/plugins/ml/server/models/annotation_service/annotation.test.ts index 725e0ac494944..975070e92a7ec 100644 --- a/x-pack/plugins/ml/server/models/annotation_service/annotation.test.ts +++ b/x-pack/plugins/ml/server/models/annotation_service/annotation.test.ts @@ -9,7 +9,6 @@ import getAnnotationsRequestMock from './__mocks__/get_annotations_request.json' import getAnnotationsResponseMock from './__mocks__/get_annotations_response.json'; import { ANNOTATION_TYPE } from '../../../common/constants/annotations'; -import { ML_ANNOTATIONS_INDEX_ALIAS_WRITE } from '../../../common/constants/index_patterns'; import { Annotation, isAnnotations } from '../../../common/types/annotations'; import { DeleteParams, GetResponse, IndexAnnotationArgs } from './annotation'; @@ -42,7 +41,7 @@ describe('annotation_service', () => { const annotationMockId = 'mockId'; const deleteParamsMock: DeleteParams = { - index: ML_ANNOTATIONS_INDEX_ALIAS_WRITE, + index: '.ml-annotations-6', id: annotationMockId, refresh: 'wait_for', }; diff --git a/x-pack/plugins/ml/server/models/annotation_service/annotation.ts b/x-pack/plugins/ml/server/models/annotation_service/annotation.ts index c6ed72de18d05..5807d181cc566 100644 --- a/x-pack/plugins/ml/server/models/annotation_service/annotation.ts +++ b/x-pack/plugins/ml/server/models/annotation_service/annotation.ts @@ -71,6 +71,7 @@ export interface IndexParams { index: string; body: Annotation; refresh: boolean | 'wait_for' | undefined; + require_alias?: boolean; id?: string; } @@ -99,6 +100,7 @@ export function annotationProvider({ asInternalUser }: IScopedClusterClient) { index: ML_ANNOTATIONS_INDEX_ALIAS_WRITE, body: annotation, refresh: 'wait_for', + require_alias: true, }; if (typeof annotation._id !== 'undefined') { @@ -407,14 +409,37 @@ export function annotationProvider({ asInternalUser }: IScopedClusterClient) { } async function deleteAnnotation(id: string) { - const params: DeleteParams = { - index: ML_ANNOTATIONS_INDEX_ALIAS_WRITE, + // Find the index the annotation is stored in. + const searchParams: estypes.SearchRequest = { + index: ML_ANNOTATIONS_INDEX_ALIAS_READ, + size: 1, + body: { + query: { + ids: { + values: [id], + }, + }, + }, + }; + + const { body } = await asInternalUser.search(searchParams); + const totalCount = + typeof body.hits.total === 'number' ? body.hits.total : body.hits.total.value; + + if (totalCount === 0) { + throw Boom.notFound(`Cannot find annotation with ID ${id}`); + } + + const index = body.hits.hits[0]._index; + + const deleteParams: DeleteParams = { + index, id, refresh: 'wait_for', }; - const { body } = await asInternalUser.delete(params); - return body; + const { body: deleteResponse } = await asInternalUser.delete(deleteParams); + return deleteResponse; } return { From 0e5f2524b46da0fe147b4726b741c38283789ed7 Mon Sep 17 00:00:00 2001 From: Diana Derevyankina <54894989+DziyanaDzeraviankina@users.noreply.github.com> Date: Tue, 19 Oct 2021 15:08:05 +0300 Subject: [PATCH 048/204] Respect external URL allow list in TSVB (#114093) * Respect external URL allow list in TSVB * Remove showExternalUrlErrorModal and onContextMenu handler for table * Update modal message Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../lib/external_url_error_modal.tsx | 60 +++++++++++++++++++ .../components/vis_types/table/vis.js | 57 ++++++++++++++---- .../components/vis_types/top_n/vis.js | 21 ++++++- 3 files changed, 125 insertions(+), 13 deletions(-) create mode 100644 src/plugins/vis_types/timeseries/public/application/components/lib/external_url_error_modal.tsx diff --git a/src/plugins/vis_types/timeseries/public/application/components/lib/external_url_error_modal.tsx b/src/plugins/vis_types/timeseries/public/application/components/lib/external_url_error_modal.tsx new file mode 100644 index 0000000000000..ebb806387d9cf --- /dev/null +++ b/src/plugins/vis_types/timeseries/public/application/components/lib/external_url_error_modal.tsx @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { + EuiButton, + EuiModal, + EuiModalBody, + EuiModalFooter, + EuiModalHeader, + EuiModalHeaderTitle, + EuiTextColor, +} from '@elastic/eui'; + +interface ExternalUrlErrorModalProps { + url: string; + handleClose: () => void; +} + +export const ExternalUrlErrorModal = ({ url, handleClose }: ExternalUrlErrorModalProps) => ( + + + + + + + + + {url} + + ), + externalUrlPolicy: 'externalUrl.policy', + kibanaConfigFileName: 'kibana.yml', + }} + /> + + + + + + + +); diff --git a/src/plugins/vis_types/timeseries/public/application/components/vis_types/table/vis.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/table/vis.js index 7b1db4b362647..b3a48a997b301 100644 --- a/src/plugins/vis_types/timeseries/public/application/components/vis_types/table/vis.js +++ b/src/plugins/vis_types/timeseries/public/application/components/vis_types/table/vis.js @@ -17,6 +17,7 @@ import { createFieldFormatter } from '../../lib/create_field_formatter'; import { isSortable } from './is_sortable'; import { EuiToolTip, EuiIcon } from '@elastic/eui'; import { replaceVars } from '../../lib/replace_vars'; +import { ExternalUrlErrorModal } from '../../lib/external_url_error_modal'; import { FIELD_FORMAT_IDS } from '../../../../../../../../plugins/field_formats/common'; import { FormattedMessage } from '@kbn/i18n/react'; import { getFieldFormats, getCoreStart } from '../../../../services'; @@ -53,12 +54,26 @@ class TableVis extends Component { const DateFormat = fieldFormatsService.getType(FIELD_FORMAT_IDS.DATE); this.dateFormatter = new DateFormat({}, this.props.getConfig); + + this.state = { + accessDeniedDrilldownUrl: null, + }; } get visibleSeries() { return get(this.props, 'model.series', []).filter((series) => !series.hidden); } + createDrilldownUrlClickHandler = (url) => (event) => { + const validatedUrl = getCoreStart().http.externalUrl.validateUrl(url); + if (validatedUrl) { + this.setState({ accessDeniedDrilldownUrl: null }); + } else { + event.preventDefault(); + this.setState({ accessDeniedDrilldownUrl: url }); + } + }; + renderRow = (row) => { const { model, fieldFormatMap, getConfig } = this.props; @@ -74,7 +89,16 @@ class TableVis extends Component { if (model.drilldown_url) { const url = replaceVars(model.drilldown_url, {}, { key: row.key }); - rowDisplay = {rowDisplay}; + const handleDrilldownUrlClick = this.createDrilldownUrlClickHandler(url); + rowDisplay = ( + + {rowDisplay} + + ); } const columns = row.series @@ -213,8 +237,11 @@ class TableVis extends Component { ); } + closeExternalUrlErrorModal = () => this.setState({ accessDeniedDrilldownUrl: null }); + render() { const { visData, model } = this.props; + const { accessDeniedDrilldownUrl } = this.state; const header = this.renderHeader(); let rows; @@ -239,16 +266,24 @@ class TableVis extends Component { ); } return ( - - - {header} - {rows} -
-
+ <> + + + {header} + {rows} +
+
+ {accessDeniedDrilldownUrl && ( + + )} + ); } } diff --git a/src/plugins/vis_types/timeseries/public/application/components/vis_types/top_n/vis.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/top_n/vis.js index 8176f6ece2805..5eb850a753384 100644 --- a/src/plugins/vis_types/timeseries/public/application/components/vis_types/top_n/vis.js +++ b/src/plugins/vis_types/timeseries/public/application/components/vis_types/top_n/vis.js @@ -15,10 +15,11 @@ import { getLastValue } from '../../../../../common/last_value_utils'; import { isBackgroundInverted } from '../../../lib/set_is_reversed'; import { replaceVars } from '../../lib/replace_vars'; import PropTypes from 'prop-types'; -import React from 'react'; +import React, { useState, useCallback } from 'react'; import { sortBy, first, get } from 'lodash'; import { DATA_FORMATTERS } from '../../../../../common/enums'; import { getOperator, shouldOperate } from '../../../../../common/operators_utils'; +import { ExternalUrlErrorModal } from '../../lib/external_url_error_modal'; function sortByDirection(data, direction, fn) { if (direction === 'desc') { @@ -41,6 +42,8 @@ function sortSeries(visData, model) { } function TopNVisualization(props) { + const [accessDeniedDrilldownUrl, setAccessDeniedDrilldownUrl] = useState(null); + const coreStart = getCoreStart(); const { backgroundColor, model, visData, fieldFormatMap, getConfig } = props; const series = sortSeries(visData, model).map((item) => { @@ -83,13 +86,27 @@ function TopNVisualization(props) { if (model.drilldown_url) { params.onClick = (item) => { const url = replaceVars(model.drilldown_url, {}, { key: item.label }); - getCoreStart().application.navigateToUrl(url); + const validatedUrl = coreStart.http.externalUrl.validateUrl(url); + if (validatedUrl) { + setAccessDeniedDrilldownUrl(null); + coreStart.application.navigateToUrl(url); + } else { + setAccessDeniedDrilldownUrl(url); + } }; } + const closeExternalUrlErrorModal = useCallback(() => setAccessDeniedDrilldownUrl(null), []); + return (
+ {accessDeniedDrilldownUrl && ( + + )}
); } From 340271fba271dab844d0c535d2dd685233535705 Mon Sep 17 00:00:00 2001 From: Kevin Qualters <56408403+kqualters-elastic@users.noreply.github.com> Date: Tue, 19 Oct 2021 08:34:15 -0400 Subject: [PATCH 049/204] [Security Solution] Analyze event moved outside of overflow popover (#115478) --- .../timeline_actions/alert_context_menu.tsx | 16 +----- .../timeline/body/actions/index.test.tsx | 29 ++++++++++ .../timeline/body/actions/index.tsx | 54 ++++++++++++++++++- .../__snapshots__/index.test.tsx.snap | 2 +- .../timeline/body/control_columns/index.tsx | 2 +- .../components/timeline/body/helpers.tsx | 6 ++- 6 files changed, 90 insertions(+), 19 deletions(-) diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.tsx index 06d61b3f0b284..a9b6eabecff86 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/alert_context_menu.tsx @@ -34,7 +34,6 @@ import { useExceptionActions } from './use_add_exception_actions'; import { useEventFilterModal } from './use_event_filter_modal'; import { Status } from '../../../../../common/detection_engine/schemas/common/schemas'; import { useKibana } from '../../../../common/lib/kibana'; -import { useInvestigateInResolverContextItem } from './investigate_in_resolver'; import { ATTACH_ALERT_TO_CASE_FOR_ROW } from '../../../../timelines/components/timeline/body/translations'; import { useEventFilterAction } from './use_event_filter_action'; import { useAddToCaseActions } from './use_add_to_case_actions'; @@ -163,30 +162,19 @@ const AlertContextMenuComponent: React.FC !isEvent && ruleId - ? [ - ...investigateInResolverActionItems, - ...addToCaseActionItems, - ...statusActionItems, - ...exceptionActionItems, - ] - : [...investigateInResolverActionItems, ...addToCaseActionItems, ...eventFilterActionItems], + ? [...addToCaseActionItems, ...statusActionItems, ...exceptionActionItems] + : [...addToCaseActionItems, ...eventFilterActionItems], [ statusActionItems, addToCaseActionItems, eventFilterActionItems, exceptionActionItems, - investigateInResolverActionItems, isEvent, ruleId, ] diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/actions/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/actions/index.test.tsx index 5ed9398a621e8..1da09bcf4e25f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/actions/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/actions/index.test.tsx @@ -185,5 +185,34 @@ describe('Actions', () => { wrapper.find('[data-test-subj="timeline-context-menu-button"]').first().prop('isDisabled') ).toBe(false); }); + test('it shows the analyze event button when the event is from an endpoint', () => { + const ecsData = { + ...mockTimelineData[0].ecs, + event: { kind: ['alert'] }, + agent: { type: ['endpoint'] }, + process: { entity_id: ['1'] }, + }; + const wrapper = mount( + + + + ); + + expect(wrapper.find('[data-test-subj="view-in-analyzer"]').exists()).toBe(true); + }); + test('it does not render the analyze event button when the event is from an unsupported source', () => { + const ecsData = { + ...mockTimelineData[0].ecs, + event: { kind: ['alert'] }, + agent: { type: ['notendpoint'] }, + }; + const wrapper = mount( + + + + ); + + expect(wrapper.find('[data-test-subj="view-in-analyzer"]').exists()).toBe(false); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/actions/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/actions/index.tsx index 73650bd320f32..c4dae739cb251 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/actions/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/actions/index.tsx @@ -21,9 +21,23 @@ import { EventsTdContent } from '../../styles'; import * as i18n from '../translations'; import { DEFAULT_ICON_BUTTON_WIDTH } from '../../helpers'; import { useShallowEqualSelector } from '../../../../../common/hooks/use_selector'; -import { TimelineId, ActionProps, OnPinEvent } from '../../../../../../common/types/timeline'; +import { + setActiveTabTimeline, + updateTimelineGraphEventId, +} from '../../../../store/timeline/actions'; +import { + useGlobalFullScreen, + useTimelineFullScreen, +} from '../../../../../common/containers/use_full_screen'; +import { + TimelineId, + ActionProps, + OnPinEvent, + TimelineTabs, +} from '../../../../../../common/types/timeline'; import { timelineActions, timelineSelectors } from '../../../../store/timeline'; import { timelineDefaults } from '../../../../store/timeline/defaults'; +import { isInvestigateInResolverActionEnabled } from '../../../../../detections/components/alerts_table/timeline_actions/investigate_in_resolver'; const ActionsContainer = styled.div` align-items: center; @@ -100,6 +114,24 @@ const ActionsComponent: React.FC = ({ [eventType, ecsData.event?.kind, ecsData.agent?.type] ); + const isDisabled = useMemo(() => !isInvestigateInResolverActionEnabled(ecsData), [ecsData]); + const { setGlobalFullScreen } = useGlobalFullScreen(); + const { setTimelineFullScreen } = useTimelineFullScreen(); + const handleClick = useCallback(() => { + const dataGridIsFullScreen = document.querySelector('.euiDataGrid--fullScreen'); + dispatch(updateTimelineGraphEventId({ id: timelineId, graphEventId: ecsData._id })); + if (timelineId === TimelineId.active) { + if (dataGridIsFullScreen) { + setTimelineFullScreen(true); + } + dispatch(setActiveTabTimeline({ id: timelineId, activeTab: TimelineTabs.graph })); + } else { + if (dataGridIsFullScreen) { + setGlobalFullScreen(true); + } + } + }, [dispatch, ecsData._id, timelineId, setGlobalFullScreen, setTimelineFullScreen]); + return ( {showCheckboxes && !tGridEnabled && ( @@ -171,6 +203,26 @@ const ActionsComponent: React.FC = ({ refetch={refetch ?? noop} onRuleChange={onRuleChange} /> + {isDisabled === false ? ( +
+ + + + + +
+ ) : null}
); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/__snapshots__/index.test.tsx.snap index 6bc2dc089494d..25d5104a98d95 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/__snapshots__/index.test.tsx.snap @@ -521,7 +521,7 @@ exports[`ColumnHeaders rendering renders correctly against snapshot 1`] = ` "compare": null, "type": [Function], }, - "width": 108, + "width": 140, }, ] } diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/control_columns/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/control_columns/index.tsx index d38bf2136513e..2cdc8d5f4e284 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/control_columns/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/control_columns/index.tsx @@ -9,7 +9,7 @@ import { ControlColumnProps } from '../../../../../../common/types/timeline'; import { Actions } from '../actions'; import { HeaderActions } from '../actions/header_actions'; -const DEFAULT_CONTROL_COLUMN_WIDTH = 108; +const DEFAULT_CONTROL_COLUMN_WIDTH = 140; export const defaultControlColumn: ControlColumnProps = { id: 'default-timeline-control-column', diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/helpers.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/helpers.tsx index 5b993110d38b5..7032319b59333 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/helpers.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/helpers.tsx @@ -17,8 +17,10 @@ import { import { OnPinEvent, OnUnPinEvent } from '../events'; import * as i18n from './translations'; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export const omitTypenameAndEmpty = (k: string, v: any): any | undefined => +export const omitTypenameAndEmpty = ( + k: string, + v: string | object | Array +): string | object | Array | undefined => k !== '__typename' && v != null ? v : undefined; export const stringifyEvent = (ecs: Ecs): string => JSON.stringify(ecs, omitTypenameAndEmpty, 2); From b402bea0658a82d028a3e5c55df4a907a18ab326 Mon Sep 17 00:00:00 2001 From: Robert Austin Date: Tue, 19 Oct 2021 09:33:46 -0400 Subject: [PATCH 050/204] Disable the experimental `metrics_entities` plugin by default. (#115460) This was default disabled in 7.15, but we needed a code change to maintain that (consistent) behavior. --- x-pack/plugins/metrics_entities/server/index.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/x-pack/plugins/metrics_entities/server/index.ts b/x-pack/plugins/metrics_entities/server/index.ts index e61dc8b7dc642..bb80ac8e8be73 100644 --- a/x-pack/plugins/metrics_entities/server/index.ts +++ b/x-pack/plugins/metrics_entities/server/index.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { schema } from '@kbn/config-schema'; + import { PluginInitializerContext } from '../../../../src/core/server'; import { MetricsEntitiesPlugin } from './plugin'; @@ -17,3 +19,10 @@ export const plugin = (initializerContext: PluginInitializerContext): MetricsEnt }; export { MetricsEntitiesPluginSetup, MetricsEntitiesPluginStart } from './types'; + +export const config = { + schema: schema.object({ + // This plugin is experimental and should be disabled by default. + enabled: schema.boolean({ defaultValue: false }), + }), +}; From e5a918dc7dad47e32bd9abf6b056e99ffaf0db18 Mon Sep 17 00:00:00 2001 From: Garrett Spong Date: Tue, 19 Oct 2021 07:43:43 -0600 Subject: [PATCH 051/204] [SecuritySolution][Detections] Enables Index Action and Connector for Detection Actions (#111813) ## Summary This PR enables the [Index Connector and Action](https://www.elastic.co/guide/en/kibana/master/index-action-type.html) for the detection engine, addressing https://github.com/elastic/kibana/issues/110550.
Action type available in list:

No Connector UI:

Create Connector UI:

Connector Template:

``` json { "rule_id": "{{context.rule.id}}", "rule_name": "{{context.rule.name}}", "alert_id": "{{alert.id}}", "context_message": "Threshold Results: {{#context.alerts}}{{#signal.threshold_result.terms}}{{value}}, {{/signal.threshold_result.terms}}{{/context.alerts}}" } ```

Documents successfully written:

--- If wanting to store the alert index timestamp, create index first with `timestamp` field and use `Define timefield for each document` option: ``` PUT .homemade-alerts-index { "mappings" : { "dynamic": "true", "properties" : { "@timestamp": { "type": "date" } } } } ```

### Checklist Delete any items that are not applicable to this PR. - [ ] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials (will need to update documentation if we proceed with this PR) --- x-pack/plugins/security_solution/common/constants.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts index 5a7e19e2cdd05..6e8d574c15860 100644 --- a/x-pack/plugins/security_solution/common/constants.ts +++ b/x-pack/plugins/security_solution/common/constants.ts @@ -296,15 +296,16 @@ export const ML_GROUP_IDS = [ML_GROUP_ID, LEGACY_ML_GROUP_ID]; */ export const NOTIFICATION_SUPPORTED_ACTION_TYPES_IDS = [ '.email', - '.slack', + '.index', + '.jira', '.pagerduty', - '.swimlane', - '.webhook', + '.resilient', '.servicenow', '.servicenow-sir', - '.jira', - '.resilient', + '.slack', + '.swimlane', '.teams', + '.webhook', ]; if (ENABLE_CASE_CONNECTOR) { From 5a0002fae834965e7924b448b1036ff91cbc698d Mon Sep 17 00:00:00 2001 From: Jason Stoltzfus Date: Tue, 19 Oct 2021 09:51:55 -0400 Subject: [PATCH 052/204] [App Search] Update "overrides" badge (#115437) --- .../components/curations/components/suggestions_table.test.tsx | 2 +- .../components/curations/components/suggestions_table.tsx | 2 +- .../applications/app_search/components/curations/types.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/suggestions_table.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/suggestions_table.test.tsx index 28c368d942c1f..439f9dabadee6 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/suggestions_table.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/suggestions_table.test.tsx @@ -88,7 +88,7 @@ describe('SuggestionsTable', () => { wrapper = renderColumn(0)('test', {}); expect(wrapper.find(EuiBadge)).toHaveLength(0); - wrapper = renderColumn(0)('test', { override_curation_id: '1-2-3' }); + wrapper = renderColumn(0)('test', { override_manual_curation: true }); expect(wrapper.find(EuiBadge).prop('children')).toEqual('Overrides'); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/suggestions_table.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/suggestions_table.tsx index b7a731c1654ca..c91468e67529e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/suggestions_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/components/suggestions_table.tsx @@ -42,7 +42,7 @@ const columns: Array> = [ render: (query: string, curation: CurationSuggestion) => ( {query} - {curation.override_curation_id && ( + {curation.override_manual_curation && ( <> {i18n.translate( diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/types.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/types.ts index f00da5deec7e3..7479505ea86da 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/types.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/types.ts @@ -15,7 +15,7 @@ export interface CurationSuggestion { status: 'pending' | 'applied' | 'automated' | 'rejected' | 'disabled'; curation_id?: string; operation: 'create' | 'update' | 'delete'; - override_curation_id?: string; + override_manual_curation?: boolean; } export interface Curation { From 44d0150ae19ed3f01dca913285f834eccede3dd1 Mon Sep 17 00:00:00 2001 From: Marco Liberati Date: Tue, 19 Oct 2021 15:58:59 +0200 Subject: [PATCH 053/204] :bug: Fix single percentile case when ES is returning no buckets (#115214) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../common/search/aggs/metrics/single_percentile.test.ts | 5 +++++ .../data/common/search/aggs/metrics/single_percentile.ts | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/plugins/data/common/search/aggs/metrics/single_percentile.test.ts b/src/plugins/data/common/search/aggs/metrics/single_percentile.test.ts index c2ba6ee1a403a..967e1b1f624aa 100644 --- a/src/plugins/data/common/search/aggs/metrics/single_percentile.test.ts +++ b/src/plugins/data/common/search/aggs/metrics/single_percentile.test.ts @@ -73,6 +73,11 @@ describe('AggTypeMetricSinglePercentileProvider class', () => { ).toEqual(123); }); + it('should not throw error for empty buckets', () => { + const agg = aggConfigs.getResponseAggs()[0]; + expect(agg.getValue({})).toEqual(NaN); + }); + it('produces the expected expression ast', () => { const agg = aggConfigs.getResponseAggs()[0]; expect(agg.toExpressionAst()).toMatchInlineSnapshot(` diff --git a/src/plugins/data/common/search/aggs/metrics/single_percentile.ts b/src/plugins/data/common/search/aggs/metrics/single_percentile.ts index 4bdafcae327cd..954576e2bbe1f 100644 --- a/src/plugins/data/common/search/aggs/metrics/single_percentile.ts +++ b/src/plugins/data/common/search/aggs/metrics/single_percentile.ts @@ -57,7 +57,9 @@ export const getSinglePercentileMetricAgg = () => { if (Number.isInteger(agg.params.percentile)) { valueKey += '.0'; } - return bucket[agg.id].values[valueKey]; + const { values } = bucket[agg.id] ?? {}; + + return values ? values[valueKey] : NaN; }, }); }; From 7420cc228c3462b9b5b3ba34fd18166e54718d8f Mon Sep 17 00:00:00 2001 From: Thomas Neirynck Date: Tue, 19 Oct 2021 09:59:29 -0400 Subject: [PATCH 054/204] [Fleet] Add telemetry for integration cards (#115413) --- x-pack/plugins/fleet/kibana.json | 2 +- .../applications/integrations/index.tsx | 23 +++++---- .../sections/epm/components/package_card.tsx | 47 +++++++++++-------- .../epm/screens/home/available_packages.tsx | 2 +- .../sections/epm/screens/home/index.tsx | 2 +- x-pack/plugins/fleet/public/plugin.ts | 16 ++++++- 6 files changed, 58 insertions(+), 34 deletions(-) diff --git a/x-pack/plugins/fleet/kibana.json b/x-pack/plugins/fleet/kibana.json index 9de538ee91b8c..1ca88cac1cc11 100644 --- a/x-pack/plugins/fleet/kibana.json +++ b/x-pack/plugins/fleet/kibana.json @@ -11,5 +11,5 @@ "requiredPlugins": ["licensing", "data", "encryptedSavedObjects", "navigation", "customIntegrations", "share"], "optionalPlugins": ["security", "features", "cloud", "usageCollection", "home", "globalSearch"], "extraPublicDirs": ["common"], - "requiredBundles": ["kibanaReact", "esUiShared", "home", "infra", "kibanaUtils"] + "requiredBundles": ["kibanaReact", "esUiShared", "home", "infra", "kibanaUtils", "usageCollection"] } diff --git a/x-pack/plugins/fleet/public/applications/integrations/index.tsx b/x-pack/plugins/fleet/public/applications/integrations/index.tsx index 0abb78f850076..4099879538afa 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/index.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/index.tsx @@ -70,18 +70,21 @@ export function renderApp( { element, appBasePath, history, setHeaderActionMenu }: AppMountParameters, config: FleetConfigType, kibanaVersion: string, - extensions: UIExtensionsStorage + extensions: UIExtensionsStorage, + UsageTracker: React.FC ) { ReactDOM.render( - , + + + , element ); diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_card.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_card.tsx index 091eb4c97183d..7181241776dda 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_card.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_card.tsx @@ -9,6 +9,8 @@ import React from 'react'; import styled from 'styled-components'; import { EuiCard, EuiFlexItem, EuiBadge, EuiToolTip, EuiSpacer } from '@elastic/eui'; +import { TrackApplicationView } from '../../../../../../../../../src/plugins/usage_collection/public'; + import { CardIcon } from '../../../../../components/package_icon'; import type { IntegrationCardItem } from '../../../../../../common/types/models/epm'; @@ -31,6 +33,7 @@ export function PackageCard({ integration, url, release, + id, }: PackageCardProps) { let releaseBadge: React.ReactNode | null = null; @@ -47,26 +50,30 @@ export function PackageCard({ ); } + const testid = `integration-card:${id}`; return ( - - } - href={url} - target={url.startsWith('http') || url.startsWith('https') ? '_blank' : undefined} - > - {releaseBadge} - + + + } + href={url} + target={url.startsWith('http') || url.startsWith('https') ? '_blank' : undefined} + > + {releaseBadge} + + ); } diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/available_packages.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/available_packages.tsx index f5c521ebacf16..73de0e51bea65 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/available_packages.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/available_packages.tsx @@ -79,7 +79,7 @@ const packageListToIntegrationsList = (packages: PackageList): PackageList => { const allCategories = [...topCategories, ...categories]; return { ...restOfPackage, - id: `${restOfPackage}-${name}`, + id: `${restOfPackage.id}-${name}`, integration: name, title, description, diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/index.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/index.tsx index 4270d360b9294..e3fc5e15488e6 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/index.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/index.tsx @@ -74,7 +74,7 @@ export const mapToCard = ( } return { - id: `${item.type === 'ui_link' ? 'ui_link' : 'epr'}-${item.id}`, + id: `${item.type === 'ui_link' ? 'ui_link' : 'epr'}:${item.id}`, description: item.description, icons: !item.icons || !item.icons.length ? [] : item.icons, title: item.title, diff --git a/x-pack/plugins/fleet/public/plugin.ts b/x-pack/plugins/fleet/public/plugin.ts index 4a2a6900cc78c..b0e4e56aa344a 100644 --- a/x-pack/plugins/fleet/public/plugin.ts +++ b/x-pack/plugins/fleet/public/plugin.ts @@ -5,6 +5,7 @@ * 2.0. */ +import React from 'react'; import type { AppMountParameters, CoreSetup, @@ -23,6 +24,8 @@ import type { import type { SharePluginStart } from 'src/plugins/share/public'; +import type { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/public'; + import { DEFAULT_APP_CATEGORIES, AppNavLinkStatus } from '../../../../src/core/public'; import type { @@ -73,6 +76,7 @@ export interface FleetSetupDeps { cloud?: CloudSetup; globalSearch?: GlobalSearchPluginSetup; customIntegrations: CustomIntegrationsSetup; + usageCollection?: UsageCollectionSetup; } export interface FleetStartDeps { @@ -137,7 +141,17 @@ export class FleetPlugin implements Plugin { unmount(); From 1a917674a4569c2f61b567d2096827c385000bca Mon Sep 17 00:00:00 2001 From: "Devin W. Hurley" Date: Tue, 19 Oct 2021 10:07:45 -0400 Subject: [PATCH 055/204] [Security Solution] [Platform] Migrate legacy actions whenever user interacts with the rule (#115101) Migrate legacy actions whenever user interacts with the rule (#115101) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../rules/add_prepackaged_rules_route.ts | 1 + .../routes/rules/import_rules_route.ts | 9 ++- .../routes/rules/patch_rules_bulk_route.ts | 10 ++- .../routes/rules/patch_rules_route.ts | 10 ++- .../rules/update_rules_bulk_route.test.ts | 2 + .../routes/rules/update_rules_bulk_route.ts | 16 +++++ .../routes/rules/update_rules_route.test.ts | 2 +- .../routes/rules/update_rules_route.ts | 16 +++++ .../rules/patch_rules.mock.ts | 3 + .../lib/detection_engine/rules/patch_rules.ts | 7 ++- .../lib/detection_engine/rules/types.ts | 17 ++++- .../rules/update_prepacked_rules.test.ts | 4 ++ .../rules/update_prepacked_rules.ts | 14 ++++- .../rules/update_rules.mock.ts | 3 + .../detection_engine/rules/update_rules.ts | 20 +++++- .../lib/detection_engine/rules/utils.ts | 62 +++++++++++++++++++ 16 files changed, 183 insertions(+), 13 deletions(-) diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts index fed34743e220a..ddf4e956beac4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts @@ -171,6 +171,7 @@ export const createPrepackagedRules = async ( ); await updatePrepackagedRules( rulesClient, + savedObjectsClient, context.securitySolution.getSpaceId(), ruleStatusClient, rulesToUpdate, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.ts index 8269fe8b36132..b09ef1a215747 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.ts @@ -40,6 +40,7 @@ import { } from '../utils'; import { patchRules } from '../../rules/patch_rules'; +import { legacyMigrate } from '../../rules/utils'; import { getTupleDuplicateErrorsAndUniqueRules } from './utils'; import { createRulesStreamFromNdJson } from '../../rules/create_rules_stream_from_ndjson'; import { buildRouteValidation } from '../../../../utils/build_validation/route_validation'; @@ -271,8 +272,14 @@ export const importRulesRoute = ( status_code: 200, }); } else if (rule != null && request.query.overwrite) { + const migratedRule = await legacyMigrate({ + rulesClient, + savedObjectsClient, + rule, + }); await patchRules({ rulesClient, + savedObjectsClient, author, buildingBlockType, spaceId: context.securitySolution.getSpaceId(), @@ -291,7 +298,7 @@ export const importRulesRoute = ( timelineTitle, meta, filters, - rule, + rule: migratedRule, index, interval, maxSignals, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts index 67d68221d846f..2b514ba911091 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts @@ -24,6 +24,7 @@ import { transformValidateBulkError } from './validate'; import { patchRules } from '../../rules/patch_rules'; import { readRules } from '../../rules/read_rules'; import { PartialFilter } from '../../types'; +import { legacyMigrate } from '../../rules/utils'; export const patchRulesBulkRoute = ( router: SecuritySolutionPluginRouter, @@ -133,9 +134,16 @@ export const patchRulesBulkRoute = ( throwHttpError(await mlAuthz.validateRuleType(existingRule?.params.type)); } - const rule = await patchRules({ + const migratedRule = await legacyMigrate({ + rulesClient, + savedObjectsClient, rule: existingRule, + }); + + const rule = await patchRules({ + rule: migratedRule, rulesClient, + savedObjectsClient, author, buildingBlockType, description, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.ts index cf140f22289de..0096cd2e38180 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.ts @@ -24,6 +24,7 @@ import { buildSiemResponse } from '../utils'; import { getIdError } from './utils'; import { transformValidate } from './validate'; import { readRules } from '../../rules/read_rules'; +import { legacyMigrate } from '../../rules/utils'; import { PartialFilter } from '../../types'; export const patchRulesRoute = ( @@ -134,8 +135,15 @@ export const patchRulesRoute = ( throwHttpError(await mlAuthz.validateRuleType(existingRule?.params.type)); } + const migratedRule = await legacyMigrate({ + rulesClient, + savedObjectsClient, + rule: existingRule, + }); + const rule = await patchRules({ rulesClient, + savedObjectsClient, author, buildingBlockType, description, @@ -154,7 +162,7 @@ export const patchRulesRoute = ( timelineTitle, meta, filters, - rule: existingRule, + rule: migratedRule, index, interval, maxSignals, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.test.ts index f7bef76944a97..22e8f6543eb7c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.test.ts @@ -41,6 +41,8 @@ describe.each([ getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()) ); + clients.appClient.getSignalsIndex.mockReturnValue('.siem-signals-test-index'); + updateRulesBulkRoute(server.router, ml, isRuleRegistryEnabled); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts index 6138690070b62..d8b7e8cb2b724 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts @@ -19,6 +19,8 @@ import { getIdBulkError } from './utils'; import { transformValidateBulkError } from './validate'; import { transformBulkError, buildSiemResponse, createBulkErrorObject } from '../utils'; import { updateRules } from '../../rules/update_rules'; +import { legacyMigrate } from '../../rules/utils'; +import { readRules } from '../../rules/read_rules'; export const updateRulesBulkRoute = ( router: SecuritySolutionPluginRouter, @@ -69,10 +71,24 @@ export const updateRulesBulkRoute = ( throwHttpError(await mlAuthz.validateRuleType(payloadRule.type)); + const existingRule = await readRules({ + isRuleRegistryEnabled, + rulesClient, + ruleId: payloadRule.rule_id, + id: payloadRule.id, + }); + + await legacyMigrate({ + rulesClient, + savedObjectsClient, + rule: existingRule, + }); + const rule = await updateRules({ spaceId: context.securitySolution.getSpaceId(), rulesClient, ruleStatusClient, + savedObjectsClient, defaultOutputIndex: siemClient.getSignalsIndex(), ruleUpdate: payloadRule, isRuleRegistryEnabled, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.test.ts index 7d611f3cccbf2..37df792b421b0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.test.ts @@ -44,7 +44,7 @@ describe.each([ getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()) ); // successful update clients.ruleExecutionLogClient.find.mockResolvedValue([]); // successful transform: ; - + clients.appClient.getSignalsIndex.mockReturnValue('.siem-signals-test-index'); updateRulesRoute(server.router, ml, isRuleRegistryEnabled); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.ts index 7cfe83093a549..cf443e3293510 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_route.ts @@ -19,6 +19,8 @@ import { getIdError } from './utils'; import { transformValidate } from './validate'; import { updateRules } from '../../rules/update_rules'; import { buildRouteValidation } from '../../../../utils/build_validation/route_validation'; +import { legacyMigrate } from '../../rules/utils'; +import { readRules } from '../../rules/read_rules'; export const updateRulesRoute = ( router: SecuritySolutionPluginRouter, @@ -59,11 +61,25 @@ export const updateRulesRoute = ( throwHttpError(await mlAuthz.validateRuleType(request.body.type)); const ruleStatusClient = context.securitySolution.getExecutionLogClient(); + + const existingRule = await readRules({ + isRuleRegistryEnabled, + rulesClient, + ruleId: request.body.rule_id, + id: request.body.id, + }); + + await legacyMigrate({ + rulesClient, + savedObjectsClient, + rule: existingRule, + }); const rule = await updateRules({ defaultOutputIndex: siemClient.getSignalsIndex(), isRuleRegistryEnabled, rulesClient, ruleStatusClient, + savedObjectsClient, ruleUpdate: request.body, spaceId: context.securitySolution.getSpaceId(), }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.mock.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.mock.ts index 1d09e4ca5c508..3626bcd5f127e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.mock.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.mock.ts @@ -7,6 +7,7 @@ import { PatchRulesOptions } from './types'; import { rulesClientMock } from '../../../../../alerting/server/mocks'; +import { savedObjectsClientMock } from '../../../../../../../src/core/server/mocks'; import { getAlertMock } from '../routes/__mocks__/request_responses'; import { getMlRuleParams, getQueryRuleParams } from '../schemas/rule_schemas.mock'; import { ruleExecutionLogClientMock } from '../rule_execution_log/__mocks__/rule_execution_log_client'; @@ -15,6 +16,7 @@ export const getPatchRulesOptionsMock = (isRuleRegistryEnabled: boolean): PatchR author: ['Elastic'], buildingBlockType: undefined, rulesClient: rulesClientMock.create(), + savedObjectsClient: savedObjectsClientMock.create(), spaceId: 'default', ruleStatusClient: ruleExecutionLogClientMock.create(), anomalyThreshold: undefined, @@ -68,6 +70,7 @@ export const getPatchMlRulesOptionsMock = (isRuleRegistryEnabled: boolean): Patc author: ['Elastic'], buildingBlockType: undefined, rulesClient: rulesClientMock.create(), + savedObjectsClient: savedObjectsClientMock.create(), spaceId: 'default', ruleStatusClient: ruleExecutionLogClientMock.create(), anomalyThreshold: 55, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.ts index c3b7e7288dc57..fd48cd4eebc2c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.ts @@ -37,6 +37,7 @@ class PatchError extends Error { export const patchRules = async ({ rulesClient, + savedObjectsClient, author, buildingBlockType, ruleStatusClient, @@ -191,14 +192,14 @@ export const patchRules = async ({ const newRule = { tags: addTags(tags ?? rule.tags, rule.params.ruleId, rule.params.immutable), - throttle: throttle !== undefined ? transformToAlertThrottle(throttle) : rule.throttle, - notifyWhen: throttle !== undefined ? transformToNotifyWhen(throttle) : rule.notifyWhen, name: calculateName({ updatedName: name, originalName: rule.name }), schedule: { interval: calculateInterval(interval, rule.schedule.interval), }, - actions: actions?.map(transformRuleToAlertAction) ?? rule.actions, params: removeUndefined(nextParams), + actions: actions?.map(transformRuleToAlertAction) ?? rule.actions, + throttle: throttle !== undefined ? transformToAlertThrottle(throttle) : rule.throttle, + notifyWhen: throttle !== undefined ? transformToNotifyWhen(throttle) : rule.notifyWhen, }; const [validated, errors] = validate(newRule, internalRuleUpdate); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/types.ts index 53a83d61da78d..a4ef081154010 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/types.ts @@ -8,7 +8,12 @@ import { get } from 'lodash/fp'; import { Readable } from 'stream'; -import { SavedObject, SavedObjectAttributes, SavedObjectsFindResult } from 'kibana/server'; +import { + SavedObject, + SavedObjectAttributes, + SavedObjectsClientContract, + SavedObjectsFindResult, +} from 'kibana/server'; import type { MachineLearningJobIdOrUndefined, From, @@ -271,12 +276,14 @@ export interface UpdateRulesOptions { rulesClient: RulesClient; defaultOutputIndex: string; ruleUpdate: UpdateRulesSchema; + savedObjectsClient: SavedObjectsClientContract; } export interface PatchRulesOptions { spaceId: string; ruleStatusClient: IRuleExecutionLogClient; rulesClient: RulesClient; + savedObjectsClient: SavedObjectsClientContract; anomalyThreshold: AnomalyThresholdOrUndefined; author: AuthorOrUndefined; buildingBlockType: BuildingBlockTypeOrUndefined; @@ -323,7 +330,7 @@ export interface PatchRulesOptions { version: VersionOrUndefined; exceptionsList: ListArrayOrUndefined; actions: RuleAlertAction[] | undefined; - rule: SanitizedAlert | null; + rule: SanitizedAlert | null | undefined; namespace?: NamespaceOrUndefined; } @@ -351,3 +358,9 @@ export interface FindRuleOptions { fields: FieldsOrUndefined; sortOrder: SortOrderOrUndefined; } + +export interface LegacyMigrateParams { + rulesClient: RulesClient; + savedObjectsClient: SavedObjectsClientContract; + rule: SanitizedAlert | null | undefined; +} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_prepacked_rules.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_prepacked_rules.test.ts index 7c9f0c9ec67a3..9bd0fe3cef59a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_prepacked_rules.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_prepacked_rules.test.ts @@ -6,6 +6,7 @@ */ import { rulesClientMock } from '../../../../../alerting/server/mocks'; +import { savedObjectsClientMock } from '../../../../../../../src/core/server/mocks'; import { getFindResultWithSingleHit } from '../routes/__mocks__/request_responses'; import { updatePrepackagedRules } from './update_prepacked_rules'; import { patchRules } from './patch_rules'; @@ -19,10 +20,12 @@ describe.each([ ])('updatePrepackagedRules - %s', (_, isRuleRegistryEnabled) => { let rulesClient: ReturnType; let ruleStatusClient: ReturnType; + let savedObjectsClient: ReturnType; beforeEach(() => { rulesClient = rulesClientMock.create(); ruleStatusClient = ruleExecutionLogClientMock.create(); + savedObjectsClient = savedObjectsClientMock.create(); }); it('should omit actions and enabled when calling patchRules', async () => { @@ -40,6 +43,7 @@ describe.each([ await updatePrepackagedRules( rulesClient, + savedObjectsClient, 'default', ruleStatusClient, [{ ...prepackagedRule, actions }], diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_prepacked_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_prepacked_rules.ts index d9c2ecd1b5732..dcf43d41e8d78 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_prepacked_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_prepacked_rules.ts @@ -6,6 +6,7 @@ */ import { chunk } from 'lodash/fp'; +import { SavedObjectsClientContract } from 'kibana/server'; import { AddPrepackagedRulesSchemaDecoded } from '../../../../common/detection_engine/schemas/request/add_prepackaged_rules_schema'; import { RulesClient, PartialAlert } from '../../../../../alerting/server'; import { patchRules } from './patch_rules'; @@ -13,6 +14,7 @@ import { readRules } from './read_rules'; import { PartialFilter } from '../types'; import { RuleParams } from '../schemas/rule_schemas'; import { IRuleExecutionLogClient } from '../rule_execution_log/types'; +import { legacyMigrate } from './utils'; /** * How many rules to update at a time is set to 50 from errors coming from @@ -51,6 +53,7 @@ export const UPDATE_CHUNK_SIZE = 50; */ export const updatePrepackagedRules = async ( rulesClient: RulesClient, + savedObjectsClient: SavedObjectsClientContract, spaceId: string, ruleStatusClient: IRuleExecutionLogClient, rules: AddPrepackagedRulesSchemaDecoded[], @@ -61,6 +64,7 @@ export const updatePrepackagedRules = async ( for (const ruleChunk of ruleChunks) { const rulePromises = createPromises( rulesClient, + savedObjectsClient, spaceId, ruleStatusClient, ruleChunk, @@ -82,6 +86,7 @@ export const updatePrepackagedRules = async ( */ export const createPromises = ( rulesClient: RulesClient, + savedObjectsClient: SavedObjectsClientContract, spaceId: string, ruleStatusClient: IRuleExecutionLogClient, rules: AddPrepackagedRulesSchemaDecoded[], @@ -146,10 +151,17 @@ export const createPromises = ( // TODO: Fix these either with an is conversion or by better typing them within io-ts const filters: PartialFilter[] | undefined = filtersObject as PartialFilter[]; + const migratedRule = await legacyMigrate({ + rulesClient, + savedObjectsClient, + rule: existingRule, + }); + // Note: we do not pass down enabled as we do not want to suddenly disable // or enable rules on the user when they were not expecting it if a rule updates return patchRules({ rulesClient, + savedObjectsClient, author, buildingBlockType, description, @@ -160,7 +172,7 @@ export const createPromises = ( language, license, outputIndex, - rule: existingRule, + rule: migratedRule, savedId, spaceId, ruleStatusClient, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.mock.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.mock.ts index 58d6cf1fd5e6b..9a7711fcc8987 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.mock.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.mock.ts @@ -6,6 +6,7 @@ */ import { rulesClientMock } from '../../../../../alerting/server/mocks'; +import { savedObjectsClientMock } from '../../../../../../../src/core/server/mocks'; import { getUpdateMachineLearningSchemaMock, getUpdateRulesSchemaMock, @@ -16,6 +17,7 @@ export const getUpdateRulesOptionsMock = (isRuleRegistryEnabled: boolean) => ({ spaceId: 'default', rulesClient: rulesClientMock.create(), ruleStatusClient: ruleExecutionLogClientMock.create(), + savedObjectsClient: savedObjectsClientMock.create(), defaultOutputIndex: '.siem-signals-default', ruleUpdate: getUpdateRulesSchemaMock(), isRuleRegistryEnabled, @@ -25,6 +27,7 @@ export const getUpdateMlRulesOptionsMock = (isRuleRegistryEnabled: boolean) => ( spaceId: 'default', rulesClient: rulesClientMock.create(), ruleStatusClient: ruleExecutionLogClientMock.create(), + savedObjectsClient: savedObjectsClientMock.create(), defaultOutputIndex: '.siem-signals-default', ruleUpdate: getUpdateMachineLearningSchemaMock(), isRuleRegistryEnabled, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.ts index f4060f7f831a9..4268ed9014066 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.ts @@ -6,7 +6,7 @@ */ /* eslint-disable complexity */ - +import { validate } from '@kbn/securitysolution-io-ts-utils'; import { DEFAULT_MAX_SIGNALS } from '../../../../common/constants'; import { transformRuleToAlertAction } from '../../../../common/detection_engine/transform_actions'; import { PartialAlert } from '../../../../../alerting/server'; @@ -14,10 +14,18 @@ import { readRules } from './read_rules'; import { UpdateRulesOptions } from './types'; import { addTags } from './add_tags'; import { typeSpecificSnakeToCamel } from '../schemas/rule_converters'; -import { RuleParams } from '../schemas/rule_schemas'; +import { internalRuleUpdate, RuleParams } from '../schemas/rule_schemas'; import { enableRule } from './enable_rule'; import { maybeMute, transformToAlertThrottle, transformToNotifyWhen } from './utils'; +class UpdateError extends Error { + public readonly statusCode: number; + constructor(message: string, statusCode: number) { + super(message); + this.statusCode = statusCode; + } +} + export const updateRules = async ({ isRuleRegistryEnabled, spaceId, @@ -25,6 +33,7 @@ export const updateRules = async ({ ruleStatusClient, defaultOutputIndex, ruleUpdate, + savedObjectsClient, }: UpdateRulesOptions): Promise | null> => { const existingRule = await readRules({ isRuleRegistryEnabled, @@ -82,9 +91,14 @@ export const updateRules = async ({ notifyWhen: transformToNotifyWhen(ruleUpdate.throttle), }; + const [validated, errors] = validate(newInternalRule, internalRuleUpdate); + if (errors != null || validated === null) { + throw new UpdateError(`Applying update would create invalid rule: ${errors}`, 400); + } + const update = await rulesClient.update({ id: existingRule.id, - data: newInternalRule, + data: validated, }); await maybeMute({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/utils.ts index 4647a4a9951df..a558024a73e34 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/utils.ts @@ -65,6 +65,9 @@ import { RulesClient } from '../../../../../alerting/server'; import { LegacyRuleActions } from '../rule_actions/legacy_types'; import { FullResponseSchema } from '../../../../common/detection_engine/schemas/request'; import { transformAlertToRuleAction } from '../../../../common/detection_engine/transform_actions'; +// eslint-disable-next-line no-restricted-imports +import { legacyRuleActionsSavedObjectType } from '../rule_actions/legacy_saved_object_mappings'; +import { LegacyMigrateParams } from './types'; export const calculateInterval = ( interval: string | undefined, @@ -296,3 +299,62 @@ export const maybeMute = async ({ // Do nothing, no-operation } }; + +/** + * Determines if rule needs to be migrated from legacy actions + * and returns necessary pieces for the updated rule + */ +export const legacyMigrate = async ({ + rulesClient, + savedObjectsClient, + rule, +}: LegacyMigrateParams): Promise | null | undefined> => { + if (rule == null || rule.id == null) { + return rule; + } + /** + * On update / patch I'm going to take the actions as they are, better off taking rules client.find (siem.notification) result + * and putting that into the actions array of the rule, then set the rules onThrottle property, notifyWhen and throttle from null -> actualy value (1hr etc..) + * Then use the rules client to delete the siem.notification + * Then with the legacy Rule Actions saved object type, just delete it. + */ + + // find it using the references array, not params.ruleAlertId + const [siemNotification, legacyRuleActionsSO] = await Promise.all([ + rulesClient.find({ + options: { + hasReference: { + type: 'alert', + id: rule.id, + }, + }, + }), + savedObjectsClient.find({ + type: legacyRuleActionsSavedObjectType, + }), + ]); + + if (siemNotification != null && siemNotification.data.length > 0) { + await Promise.all([ + rulesClient.delete({ id: siemNotification.data[0].id }), + legacyRuleActionsSO != null && legacyRuleActionsSO.saved_objects.length > 0 + ? savedObjectsClient.delete( + legacyRuleActionsSavedObjectType, + legacyRuleActionsSO.saved_objects[0].id + ) + : null, + ]); + const migratedRule = { + ...rule, + actions: siemNotification.data[0].actions, + throttle: siemNotification.data[0].schedule.interval, + notifyWhen: transformToNotifyWhen(siemNotification.data[0].throttle), + }; + await rulesClient.update({ + id: rule.id, + data: migratedRule, + }); + return migratedRule; + } + return rule; +}; From c2c08be709429270139cae7b9674d349a4b61e86 Mon Sep 17 00:00:00 2001 From: Frank Hassanabad Date: Tue, 19 Oct 2021 08:24:42 -0600 Subject: [PATCH 056/204] [Security Solutions] Adds security detection rule actions as importable and exportable (#115243) ## Summary Adds the security detection rule actions as being exportable and importable. * Adds exportable actions for legacy notification system * Adds exportable actions for the new throttle notification system * Adds importable but only imports into the new throttle notification system. * Updates unit tests In your `ndjson` file when you have actions exported you will see them like so: ```json "actions": [ { "group": "default", "id": "b55117e0-2df9-11ec-b789-7f03e3cdd668", "params": { "message": "Rule {{context.rule.name}} generated {{state.signals_count}} alerts" }, "action_type_id": ".slack" } ] ``` where before it was `actions: []` and was not provided. **Caveats** If you delete your connector and have an invalid connector then the rule(s) that were referring to that invalid connector will not import and you will get an error like this: Screen Shot 2021-10-15 at 2 47 10 PM This does _not_ export your connectors at this point in time. You have to export your connector through the Saved Object Management separate like so: Screen Shot 2021-10-15 at 2 58 03 PM However, if remove everything and import your connector without changing its saved object ID and then go to import the rules everything should import ok and you will get your actions working. **Manual Testing**: * You can create normal actions on an alert and then do exports and you should see the actions in your ndjson file * You can create legacy notifications from 7.14.0 and then upgrade and export and you should see the actions in your ndjson file * You can manually create legacy notifications by: By getting an alert id first and ensuring that your `legacy_notifications/one_action.json` contains a valid action then running this command: ```ts ./post_legacy_notification.sh 3403c0d0-2d44-11ec-b147-3b0c6d563a60 ``` * You can export your connector and remove everything and then do an import and you will have everything imported and working with your actions and connector wired up correctly. ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added --- .../routes/rules/export_rules_route.ts | 13 ++++- .../routes/rules/import_rules_route.ts | 5 +- .../rules/perform_bulk_action_route.test.ts | 5 +- .../routes/rules/perform_bulk_action_route.ts | 5 ++ .../routes/rules/utils.test.ts | 6 +-- .../detection_engine/routes/rules/utils.ts | 7 ++- .../rules/get_export_all.test.ts | 26 +++++++++- .../detection_engine/rules/get_export_all.ts | 17 ++++++- .../rules/get_export_by_object_ids.test.ts | 49 +++++++++++++++++-- .../rules/get_export_by_object_ids.ts | 36 +++++++++++--- .../security_solution/server/routes/index.ts | 4 +- 11 files changed, 145 insertions(+), 28 deletions(-) diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/export_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/export_rules_route.ts index e4b99e63cb6c6..c84dd8147ebcc 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/export_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/export_rules_route.ts @@ -6,6 +6,7 @@ */ import { transformError } from '@kbn/securitysolution-es-utils'; +import { Logger } from 'src/core/server'; import { exportRulesQuerySchema, ExportRulesQuerySchemaDecoded, @@ -24,6 +25,7 @@ import { buildSiemResponse } from '../utils'; export const exportRulesRoute = ( router: SecuritySolutionPluginRouter, config: ConfigType, + logger: Logger, isRuleRegistryEnabled: boolean ) => { router.post( @@ -44,6 +46,7 @@ export const exportRulesRoute = ( async (context, request, response) => { const siemResponse = buildSiemResponse(response); const rulesClient = context.alerting?.getRulesClient(); + const savedObjectsClient = context.core.savedObjects.client; if (!rulesClient) { return siemResponse.error({ statusCode: 404 }); @@ -71,8 +74,14 @@ export const exportRulesRoute = ( const exported = request.body?.objects != null - ? await getExportByObjectIds(rulesClient, request.body.objects, isRuleRegistryEnabled) - : await getExportAll(rulesClient, isRuleRegistryEnabled); + ? await getExportByObjectIds( + rulesClient, + savedObjectsClient, + request.body.objects, + logger, + isRuleRegistryEnabled + ) + : await getExportAll(rulesClient, savedObjectsClient, logger, isRuleRegistryEnabled); const responseBody = request.query.exclude_export_details ? exported.rulesNdjson diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.ts index b09ef1a215747..3752128d3daa3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.ts @@ -194,6 +194,7 @@ export const importRulesRoute = ( throttle, version, exceptions_list: exceptionsList, + actions, } = parsedRule; try { @@ -265,7 +266,7 @@ export const importRulesRoute = ( note, version, exceptionsList, - actions: [], // Actions are not imported nor exported at this time + actions, }); resolve({ rule_id: ruleId, @@ -328,7 +329,7 @@ export const importRulesRoute = ( exceptionsList, anomalyThreshold, machineLearningJobId, - actions: undefined, + actions, }); resolve({ rule_id: ruleId, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.test.ts index 41b909bd718c0..3e85b4898d01c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.test.ts @@ -17,6 +17,7 @@ import { import { requestContextMock, serverMock, requestMock } from '../__mocks__'; import { performBulkActionRoute } from './perform_bulk_action_route'; import { getPerformBulkActionSchemaMock } from '../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema.mock'; +import { loggingSystemMock } from 'src/core/server/mocks'; jest.mock('../../../machine_learning/authz', () => mockMlAuthzFactory.create()); @@ -27,15 +28,17 @@ describe.each([ let server: ReturnType; let { clients, context } = requestContextMock.createTools(); let ml: ReturnType; + let logger: ReturnType; beforeEach(() => { server = serverMock.create(); + logger = loggingSystemMock.createLogger(); ({ clients, context } = requestContextMock.createTools()); ml = mlServicesMock.createSetupContract(); clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled)); - performBulkActionRoute(server.router, ml, isRuleRegistryEnabled); + performBulkActionRoute(server.router, ml, logger, isRuleRegistryEnabled); }); describe('status codes', () => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.ts index 0eba5af4e063a..fb5a2315479da 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.ts @@ -6,6 +6,8 @@ */ import { transformError } from '@kbn/securitysolution-es-utils'; +import { Logger } from 'src/core/server'; + import { DETECTION_ENGINE_RULES_BULK_ACTION } from '../../../../../common/constants'; import { BulkAction } from '../../../../../common/detection_engine/schemas/common/schemas'; import { performBulkActionSchema } from '../../../../../common/detection_engine/schemas/request/perform_bulk_action_schema'; @@ -26,6 +28,7 @@ const BULK_ACTION_RULES_LIMIT = 10000; export const performBulkActionRoute = ( router: SecuritySolutionPluginRouter, ml: SetupPlugins['ml'], + logger: Logger, isRuleRegistryEnabled: boolean ) => { router.post( @@ -133,7 +136,9 @@ export const performBulkActionRoute = ( case BulkAction.export: const exported = await getExportByObjectIds( rulesClient, + savedObjectsClient, rules.data.map(({ params }) => ({ rule_id: params.ruleId })), + logger, isRuleRegistryEnabled ); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils.test.ts index c5a30c349d497..366ae607f0ba8 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils.test.ts @@ -469,12 +469,12 @@ describe.each([ describe('transformAlertsToRules', () => { test('given an empty array returns an empty array', () => { - expect(transformAlertsToRules([])).toEqual([]); + expect(transformAlertsToRules([], {})).toEqual([]); }); test('given single alert will return the alert transformed', () => { const result1 = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); - const transformed = transformAlertsToRules([result1]); + const transformed = transformAlertsToRules([result1], {}); const expected = getOutputRuleAlertForRest(); expect(transformed).toEqual([expected]); }); @@ -485,7 +485,7 @@ describe.each([ result2.id = 'some other id'; result2.params.ruleId = 'some other id'; - const transformed = transformAlertsToRules([result1, result2]); + const transformed = transformAlertsToRules([result1, result2], {}); const expected1 = getOutputRuleAlertForRest(); const expected2 = getOutputRuleAlertForRest(); expected2.id = 'some other id'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils.ts index afc48386a2986..bb2e35d189ca1 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils.ts @@ -103,8 +103,11 @@ export const transformAlertToRule = ( return internalRuleToAPIResponse(alert, ruleStatus?.attributes, legacyRuleActions); }; -export const transformAlertsToRules = (alerts: RuleAlertType[]): Array> => { - return alerts.map((alert) => transformAlertToRule(alert)); +export const transformAlertsToRules = ( + alerts: RuleAlertType[], + legacyRuleActions: Record +): Array> => { + return alerts.map((alert) => transformAlertToRule(alert, undefined, legacyRuleActions[alert.id])); }; export const transformFindAlerts = ( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_all.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_all.test.ts index 3ca5960d7d4e1..92e4f0bbb4a5e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_all.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_all.test.ts @@ -9,21 +9,33 @@ import { getAlertMock, getFindResultWithSingleHit, FindHit, + getEmptySavedObjectsResponse, } from '../routes/__mocks__/request_responses'; import { rulesClientMock } from '../../../../../alerting/server/mocks'; import { getExportAll } from './get_export_all'; import { getListArrayMock } from '../../../../common/detection_engine/schemas/types/lists.mock'; import { getThreatMock } from '../../../../common/detection_engine/schemas/types/threat.mock'; + import { getQueryRuleParams } from '../schemas/rule_schemas.mock'; +import { loggingSystemMock } from 'src/core/server/mocks'; +import { requestContextMock } from '../routes/__mocks__/request_context'; describe.each([ ['Legacy', false], ['RAC', true], ])('getExportAll - %s', (_, isRuleRegistryEnabled) => { + let logger: ReturnType; + const { clients } = requestContextMock.createTools(); + + beforeEach(async () => { + clients.savedObjectsClient.find.mockResolvedValue(getEmptySavedObjectsResponse()); + }); + test('it exports everything from the alerts client', async () => { const rulesClient = rulesClientMock.create(); const result = getFindResultWithSingleHit(isRuleRegistryEnabled); const alert = getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()); + alert.params = { ...alert.params, filters: [{ query: { match_phrase: { 'host.name': 'some-host' } } }], @@ -35,7 +47,12 @@ describe.each([ result.data = [alert]; rulesClient.find.mockResolvedValue(result); - const exports = await getExportAll(rulesClient, isRuleRegistryEnabled); + const exports = await getExportAll( + rulesClient, + clients.savedObjectsClient, + logger, + isRuleRegistryEnabled + ); const rulesJson = JSON.parse(exports.rulesNdjson); const detailsJson = JSON.parse(exports.exportDetails); expect(rulesJson).toEqual({ @@ -97,7 +114,12 @@ describe.each([ rulesClient.find.mockResolvedValue(findResult); - const exports = await getExportAll(rulesClient, isRuleRegistryEnabled); + const exports = await getExportAll( + rulesClient, + clients.savedObjectsClient, + logger, + isRuleRegistryEnabled + ); expect(exports).toEqual({ rulesNdjson: '', exportDetails: '{"exported_count":0,"missing_rules":[],"missing_rules_count":0}\n', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_all.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_all.ts index 71079ccefc97a..cbbda5df7e2bf 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_all.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_all.ts @@ -7,20 +7,33 @@ import { transformDataToNdjson } from '@kbn/securitysolution-utils'; -import { RulesClient } from '../../../../../alerting/server'; +import { Logger } from 'src/core/server'; +import { RulesClient, AlertServices } from '../../../../../alerting/server'; import { getNonPackagedRules } from './get_existing_prepackaged_rules'; import { getExportDetailsNdjson } from './get_export_details_ndjson'; import { transformAlertsToRules } from '../routes/rules/utils'; +// eslint-disable-next-line no-restricted-imports +import { legacyGetBulkRuleActionsSavedObject } from '../rule_actions/legacy_get_bulk_rule_actions_saved_object'; + export const getExportAll = async ( rulesClient: RulesClient, + savedObjectsClient: AlertServices['savedObjectsClient'], + logger: Logger, isRuleRegistryEnabled: boolean ): Promise<{ rulesNdjson: string; exportDetails: string; }> => { const ruleAlertTypes = await getNonPackagedRules({ rulesClient, isRuleRegistryEnabled }); - const rules = transformAlertsToRules(ruleAlertTypes); + const alertIds = ruleAlertTypes.map((rule) => rule.id); + const legacyActions = await legacyGetBulkRuleActionsSavedObject({ + alertIds, + savedObjectsClient, + logger, + }); + + const rules = transformAlertsToRules(ruleAlertTypes, legacyActions); // We do not support importing/exporting actions. When we do, delete this line of code const rulesWithoutActions = rules.map((rule) => ({ ...rule, actions: [] })); const rulesNdjson = transformDataToNdjson(rulesWithoutActions); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts index 740427e44b560..961f2c6a41866 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts @@ -10,28 +10,43 @@ import { getAlertMock, getFindResultWithSingleHit, FindHit, + getEmptySavedObjectsResponse, } from '../routes/__mocks__/request_responses'; import { rulesClientMock } from '../../../../../alerting/server/mocks'; import { getListArrayMock } from '../../../../common/detection_engine/schemas/types/lists.mock'; import { getThreatMock } from '../../../../common/detection_engine/schemas/types/threat.mock'; import { getQueryRuleParams } from '../schemas/rule_schemas.mock'; +import { loggingSystemMock } from 'src/core/server/mocks'; +import { requestContextMock } from '../routes/__mocks__/request_context'; describe.each([ ['Legacy', false], ['RAC', true], ])('get_export_by_object_ids - %s', (_, isRuleRegistryEnabled) => { + let logger: ReturnType; + const { clients } = requestContextMock.createTools(); + beforeEach(() => { jest.resetAllMocks(); jest.restoreAllMocks(); jest.clearAllMocks(); + + clients.savedObjectsClient.find.mockResolvedValue(getEmptySavedObjectsResponse()); }); + describe('getExportByObjectIds', () => { test('it exports object ids into an expected string with new line characters', async () => { const rulesClient = rulesClientMock.create(); rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled)); const objects = [{ rule_id: 'rule-1' }]; - const exports = await getExportByObjectIds(rulesClient, objects, isRuleRegistryEnabled); + const exports = await getExportByObjectIds( + rulesClient, + clients.savedObjectsClient, + objects, + logger, + isRuleRegistryEnabled + ); const exportsObj = { rulesNdjson: JSON.parse(exports.rulesNdjson), exportDetails: JSON.parse(exports.exportDetails), @@ -102,7 +117,13 @@ describe.each([ rulesClient.find.mockResolvedValue(findResult); const objects = [{ rule_id: 'rule-1' }]; - const exports = await getExportByObjectIds(rulesClient, objects, isRuleRegistryEnabled); + const exports = await getExportByObjectIds( + rulesClient, + clients.savedObjectsClient, + objects, + logger, + isRuleRegistryEnabled + ); expect(exports).toEqual({ rulesNdjson: '', exportDetails: @@ -117,7 +138,13 @@ describe.each([ rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled)); const objects = [{ rule_id: 'rule-1' }]; - const exports = await getRulesFromObjects(rulesClient, objects, isRuleRegistryEnabled); + const exports = await getRulesFromObjects( + rulesClient, + clients.savedObjectsClient, + objects, + logger, + isRuleRegistryEnabled + ); const expected: RulesErrors = { exportedCount: 1, missingRules: [], @@ -192,7 +219,13 @@ describe.each([ rulesClient.find.mockResolvedValue(findResult); const objects = [{ rule_id: 'rule-1' }]; - const exports = await getRulesFromObjects(rulesClient, objects, isRuleRegistryEnabled); + const exports = await getRulesFromObjects( + rulesClient, + clients.savedObjectsClient, + objects, + logger, + isRuleRegistryEnabled + ); const expected: RulesErrors = { exportedCount: 0, missingRules: [{ rule_id: 'rule-1' }], @@ -215,7 +248,13 @@ describe.each([ rulesClient.find.mockResolvedValue(findResult); const objects = [{ rule_id: 'rule-1' }]; - const exports = await getRulesFromObjects(rulesClient, objects, isRuleRegistryEnabled); + const exports = await getRulesFromObjects( + rulesClient, + clients.savedObjectsClient, + objects, + logger, + isRuleRegistryEnabled + ); const expected: RulesErrors = { exportedCount: 0, missingRules: [{ rule_id: 'rule-1' }], diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_by_object_ids.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_by_object_ids.ts index 4cf3ad9133a71..8233fe6d4948c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_by_object_ids.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_by_object_ids.ts @@ -8,14 +8,20 @@ import { chunk } from 'lodash'; import { transformDataToNdjson } from '@kbn/securitysolution-utils'; +import { Logger } from 'src/core/server'; import { RulesSchema } from '../../../../common/detection_engine/schemas/response/rules_schema'; -import { RulesClient } from '../../../../../alerting/server'; +import { RulesClient, AlertServices } from '../../../../../alerting/server'; + import { getExportDetailsNdjson } from './get_export_details_ndjson'; + import { isAlertType } from '../rules/types'; import { transformAlertToRule } from '../routes/rules/utils'; import { INTERNAL_RULE_ID_KEY } from '../../../../common/constants'; import { findRules } from './find_rules'; +// eslint-disable-next-line no-restricted-imports +import { legacyGetBulkRuleActionsSavedObject } from '../rule_actions/legacy_get_bulk_rule_actions_saved_object'; + interface ExportSuccessRule { statusCode: 200; rule: Partial; @@ -34,23 +40,32 @@ export interface RulesErrors { export const getExportByObjectIds = async ( rulesClient: RulesClient, + savedObjectsClient: AlertServices['savedObjectsClient'], objects: Array<{ rule_id: string }>, + logger: Logger, isRuleRegistryEnabled: boolean ): Promise<{ rulesNdjson: string; exportDetails: string; }> => { - const rulesAndErrors = await getRulesFromObjects(rulesClient, objects, isRuleRegistryEnabled); - // We do not support importing/exporting actions. When we do, delete this line of code - const rulesWithoutActions = rulesAndErrors.rules.map((rule) => ({ ...rule, actions: [] })); - const rulesNdjson = transformDataToNdjson(rulesWithoutActions); - const exportDetails = getExportDetailsNdjson(rulesWithoutActions, rulesAndErrors.missingRules); + const rulesAndErrors = await getRulesFromObjects( + rulesClient, + savedObjectsClient, + objects, + logger, + isRuleRegistryEnabled + ); + + const rulesNdjson = transformDataToNdjson(rulesAndErrors.rules); + const exportDetails = getExportDetailsNdjson(rulesAndErrors.rules, rulesAndErrors.missingRules); return { rulesNdjson, exportDetails }; }; export const getRulesFromObjects = async ( rulesClient: RulesClient, + savedObjectsClient: AlertServices['savedObjectsClient'], objects: Array<{ rule_id: string }>, + logger: Logger, isRuleRegistryEnabled: boolean ): Promise => { // If we put more than 1024 ids in one block like "alert.attributes.tags: (id1 OR id2 OR ... OR id1100)" @@ -78,6 +93,13 @@ export const getRulesFromObjects = async ( sortField: undefined, sortOrder: undefined, }); + const alertIds = rules.data.map((rule) => rule.id); + const legacyActions = await legacyGetBulkRuleActionsSavedObject({ + alertIds, + savedObjectsClient, + logger, + }); + const alertsAndErrors = objects.map(({ rule_id: ruleId }) => { const matchingRule = rules.data.find((rule) => rule.params.ruleId === ruleId); if ( @@ -87,7 +109,7 @@ export const getRulesFromObjects = async ( ) { return { statusCode: 200, - rule: transformAlertToRule(matchingRule), + rule: transformAlertToRule(matchingRule, undefined, legacyActions[matchingRule.id]), }; } else { return { diff --git a/x-pack/plugins/security_solution/server/routes/index.ts b/x-pack/plugins/security_solution/server/routes/index.ts index d045c6b129e43..148580d5c4477 100644 --- a/x-pack/plugins/security_solution/server/routes/index.ts +++ b/x-pack/plugins/security_solution/server/routes/index.ts @@ -91,12 +91,12 @@ export const initRoutes = ( updateRulesBulkRoute(router, ml, isRuleRegistryEnabled); patchRulesBulkRoute(router, ml, isRuleRegistryEnabled); deleteRulesBulkRoute(router, isRuleRegistryEnabled); - performBulkActionRoute(router, ml, isRuleRegistryEnabled); + performBulkActionRoute(router, ml, logger, isRuleRegistryEnabled); createTimelinesRoute(router, config, security); patchTimelinesRoute(router, config, security); importRulesRoute(router, config, ml, isRuleRegistryEnabled); - exportRulesRoute(router, config, isRuleRegistryEnabled); + exportRulesRoute(router, config, logger, isRuleRegistryEnabled); importTimelinesRoute(router, config, security); exportTimelinesRoute(router, config, security); From 6a1af300f5a08de7b1287e52d04a6b304cc3186b Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Tue, 19 Oct 2021 16:35:40 +0200 Subject: [PATCH 057/204] [Discover] Improve doc viewer code in Discover (#114759) Co-authored-by: Dmitry Tomashevich <39378793+Dmitriynj@users.noreply.github.com> --- .../public/__mocks__/index_patterns.ts | 8 +++-- .../apps/context/context_app_route.tsx | 26 +++++++++++++- .../apps/doc/components/doc.test.tsx | 21 ++--------- .../application/apps/doc/components/doc.tsx | 16 ++++----- .../application/apps/doc/single_doc_route.tsx | 35 ++++++++++++++----- .../doc_viewer/doc_viewer_tab.test.tsx | 3 ++ .../__snapshots__/source_viewer.test.tsx.snap | 18 ++++++++-- .../source_viewer/source_viewer.test.tsx | 14 ++++---- .../source_viewer/source_viewer.tsx | 17 ++++----- .../application/components/table/table.tsx | 2 +- .../application/doc_views/doc_views_types.ts | 2 +- .../helpers/use_index_pattern.test.tsx | 20 ++++++++--- .../application/helpers/use_index_pattern.tsx | 13 ++++--- .../services/use_es_doc_search.test.tsx | 24 ++++--------- .../application/services/use_es_doc_search.ts | 15 +++----- src/plugins/discover/public/plugin.tsx | 2 +- 16 files changed, 135 insertions(+), 101 deletions(-) diff --git a/src/plugins/discover/public/__mocks__/index_patterns.ts b/src/plugins/discover/public/__mocks__/index_patterns.ts index 88447eacc884d..b90338e895623 100644 --- a/src/plugins/discover/public/__mocks__/index_patterns.ts +++ b/src/plugins/discover/public/__mocks__/index_patterns.ts @@ -10,12 +10,14 @@ import { IndexPatternsService } from '../../../data/common'; import { indexPatternMock } from './index_pattern'; export const indexPatternsMock = { - getCache: () => { + getCache: async () => { return [indexPatternMock]; }, - get: (id: string) => { + get: async (id: string) => { if (id === 'the-index-pattern-id') { - return indexPatternMock; + return Promise.resolve(indexPatternMock); + } else if (id === 'invalid-index-pattern-id') { + return Promise.reject('Invald'); } }, updateSavedObject: jest.fn(), diff --git a/src/plugins/discover/public/application/apps/context/context_app_route.tsx b/src/plugins/discover/public/application/apps/context/context_app_route.tsx index d124fd6cfa395..6c4722418be14 100644 --- a/src/plugins/discover/public/application/apps/context/context_app_route.tsx +++ b/src/plugins/discover/public/application/apps/context/context_app_route.tsx @@ -8,6 +8,8 @@ import React, { useEffect } from 'react'; import { useParams } from 'react-router-dom'; import { i18n } from '@kbn/i18n'; +import { EuiEmptyPrompt } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; import { DiscoverServices } from '../../../build_services'; import { ContextApp } from './context_app'; import { getRootBreadcrumbs } from '../../helpers/breadcrumbs'; @@ -43,7 +45,29 @@ export function ContextAppRoute(props: ContextAppProps) { ]); }, [chrome]); - const indexPattern = useIndexPattern(services.indexPatterns, indexPatternId); + const { indexPattern, error } = useIndexPattern(services.indexPatterns, indexPatternId); + + if (error) { + return ( + + } + body={ + + } + /> + ); + } if (!indexPattern) { return ; diff --git a/src/plugins/discover/public/application/apps/doc/components/doc.test.tsx b/src/plugins/discover/public/application/apps/doc/components/doc.test.tsx index 31ff39ea6b577..68c012ddd92e9 100644 --- a/src/plugins/discover/public/application/apps/doc/components/doc.test.tsx +++ b/src/plugins/discover/public/application/apps/doc/components/doc.test.tsx @@ -14,6 +14,7 @@ import { ReactWrapper } from 'enzyme'; import { findTestSubject } from '@elastic/eui/lib/test'; import { Doc, DocProps } from './doc'; import { SEARCH_FIELDS_FROM_SOURCE as mockSearchFieldsFromSource } from '../../../../../common'; +import { indexPatternMock } from '../../../../__mocks__/index_pattern'; const mockSearchApi = jest.fn(); @@ -74,21 +75,11 @@ const waitForPromises = async () => * this works but logs ugly error messages until we're using React 16.9 * should be adapted when we upgrade */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -async function mountDoc(update = false, indexPatternGetter: any = null) { - const indexPattern = { - getComputedFields: () => [], - }; - const indexPatternService = { - get: indexPatternGetter ? indexPatternGetter : jest.fn(() => Promise.resolve(indexPattern)), - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } as any; - +async function mountDoc(update = false) { const props = { id: '1', index: 'index1', - indexPatternId: 'xyz', - indexPatternService, + indexPattern: indexPatternMock, } as DocProps; let comp!: ReactWrapper; await act(async () => { @@ -108,12 +99,6 @@ describe('Test of of Discover', () => { expect(findTestSubject(comp, 'doc-msg-loading').length).toBe(1); }); - test('renders IndexPattern notFound msg', async () => { - const indexPatternGetter = jest.fn(() => Promise.reject({ savedObjectId: '007' })); - const comp = await mountDoc(true, indexPatternGetter); - expect(findTestSubject(comp, 'doc-msg-notFoundIndexPattern').length).toBe(1); - }); - test('renders notFound msg', async () => { mockSearchApi.mockImplementation(() => throwError({ status: 404 })); const comp = await mountDoc(true); diff --git a/src/plugins/discover/public/application/apps/doc/components/doc.tsx b/src/plugins/discover/public/application/apps/doc/components/doc.tsx index f33ffe561e490..c6cfad3953e95 100644 --- a/src/plugins/discover/public/application/apps/doc/components/doc.tsx +++ b/src/plugins/discover/public/application/apps/doc/components/doc.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiCallOut, EuiLink, EuiLoadingSpinner, EuiPageContent, EuiPage } from '@elastic/eui'; -import { IndexPatternsContract } from 'src/plugins/data/public'; +import { IndexPattern } from 'src/plugins/data/public'; import { getServices } from '../../../../kibana_services'; import { DocViewer } from '../../../components/doc_viewer/doc_viewer'; import { ElasticRequestState } from '../types'; @@ -25,14 +25,9 @@ export interface DocProps { */ index: string; /** - * IndexPattern ID used to get IndexPattern entity - * that's used for adding additional fields (stored_fields, script_fields, docvalue_fields) + * IndexPattern entity */ - indexPatternId: string; - /** - * IndexPatternService to get a given index pattern by ID - */ - indexPatternService: IndexPatternsContract; + indexPattern: IndexPattern; /** * If set, will always request source, regardless of the global `fieldsFromSource` setting */ @@ -40,7 +35,8 @@ export interface DocProps { } export function Doc(props: DocProps) { - const [reqState, hit, indexPattern] = useEsDocSearch(props); + const { indexPattern } = props; + const [reqState, hit] = useEsDocSearch(props); const indexExistsLink = getServices().docLinks.links.apis.indexExists; return ( @@ -54,7 +50,7 @@ export function Doc(props: DocProps) { } /> diff --git a/src/plugins/discover/public/application/apps/doc/single_doc_route.tsx b/src/plugins/discover/public/application/apps/doc/single_doc_route.tsx index 8398f6255e0f9..aef928d523515 100644 --- a/src/plugins/discover/public/application/apps/doc/single_doc_route.tsx +++ b/src/plugins/discover/public/application/apps/doc/single_doc_route.tsx @@ -7,6 +7,8 @@ */ import React, { useEffect } from 'react'; import { useLocation, useParams } from 'react-router-dom'; +import { EuiEmptyPrompt } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; import { DiscoverServices } from '../../../build_services'; import { getRootBreadcrumbs } from '../../helpers/breadcrumbs'; import { Doc } from './components/doc'; @@ -31,7 +33,7 @@ function useQuery() { export function SingleDocRoute(props: SingleDocRouteProps) { const { services } = props; - const { chrome, timefilter, indexPatterns } = services; + const { chrome, timefilter } = services; const { indexPatternId, index } = useParams(); @@ -52,7 +54,29 @@ export function SingleDocRoute(props: SingleDocRouteProps) { timefilter.disableTimeRangeSelector(); }); - const indexPattern = useIndexPattern(services.indexPatterns, indexPatternId); + const { indexPattern, error } = useIndexPattern(services.indexPatterns, indexPatternId); + + if (error) { + return ( + + } + body={ + + } + /> + ); + } if (!indexPattern) { return ; @@ -60,12 +84,7 @@ export function SingleDocRoute(props: SingleDocRouteProps) { return (
- +
); } diff --git a/src/plugins/discover/public/application/components/doc_viewer/doc_viewer_tab.test.tsx b/src/plugins/discover/public/application/components/doc_viewer/doc_viewer_tab.test.tsx index a2434170acdd7..188deba755445 100644 --- a/src/plugins/discover/public/application/components/doc_viewer/doc_viewer_tab.test.tsx +++ b/src/plugins/discover/public/application/components/doc_viewer/doc_viewer_tab.test.tsx @@ -10,6 +10,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import { DocViewerTab } from './doc_viewer_tab'; import { ElasticSearchHit } from '../../doc_views/doc_views_types'; +import { indexPatternMock } from '../../../__mocks__/index_pattern'; describe('DocViewerTab', () => { test('changing columns triggers an update', () => { @@ -21,6 +22,7 @@ describe('DocViewerTab', () => { renderProps: { hit: {} as ElasticSearchHit, columns: ['test'], + indexPattern: indexPatternMock, }, }; @@ -31,6 +33,7 @@ describe('DocViewerTab', () => { renderProps: { hit: {} as ElasticSearchHit, columns: ['test2'], + indexPattern: indexPatternMock, }, }; diff --git a/src/plugins/discover/public/application/components/source_viewer/__snapshots__/source_viewer.test.tsx.snap b/src/plugins/discover/public/application/components/source_viewer/__snapshots__/source_viewer.test.tsx.snap index 82d9183f3d394..761263ee861b9 100644 --- a/src/plugins/discover/public/application/components/source_viewer/__snapshots__/source_viewer.test.tsx.snap +++ b/src/plugins/discover/public/application/components/source_viewer/__snapshots__/source_viewer.test.tsx.snap @@ -5,7 +5,11 @@ exports[`Source Viewer component renders error state 1`] = ` hasLineNumbers={true} id="1" index="index1" - indexPatternId="xyz" + indexPattern={ + Object { + "getComputedFields": [Function], + } + } intl={ Object { "defaultFormats": Object {}, @@ -264,7 +268,11 @@ exports[`Source Viewer component renders json code editor 1`] = ` hasLineNumbers={true} id="1" index="index1" - indexPatternId="xyz" + indexPattern={ + Object { + "getComputedFields": [Function], + } + } intl={ Object { "defaultFormats": Object {}, @@ -619,7 +627,11 @@ exports[`Source Viewer component renders loading state 1`] = ` hasLineNumbers={true} id="1" index="index1" - indexPatternId="xyz" + indexPattern={ + Object { + "getComputedFields": [Function], + } + } intl={ Object { "defaultFormats": Object {}, diff --git a/src/plugins/discover/public/application/components/source_viewer/source_viewer.test.tsx b/src/plugins/discover/public/application/components/source_viewer/source_viewer.test.tsx index 7895c1025dda9..a98c2de6197d8 100644 --- a/src/plugins/discover/public/application/components/source_viewer/source_viewer.test.tsx +++ b/src/plugins/discover/public/application/components/source_viewer/source_viewer.test.tsx @@ -43,13 +43,13 @@ const mockIndexPatternService = { })); describe('Source Viewer component', () => { test('renders loading state', () => { - jest.spyOn(hooks, 'useEsDocSearch').mockImplementation(() => [0, null, null, () => {}]); + jest.spyOn(hooks, 'useEsDocSearch').mockImplementation(() => [0, null, () => {}]); const comp = mountWithIntl( @@ -60,13 +60,13 @@ describe('Source Viewer component', () => { }); test('renders error state', () => { - jest.spyOn(hooks, 'useEsDocSearch').mockImplementation(() => [3, null, null, () => {}]); + jest.spyOn(hooks, 'useEsDocSearch').mockImplementation(() => [3, null, () => {}]); const comp = mountWithIntl( @@ -97,9 +97,7 @@ describe('Source Viewer component', () => { _underscore: 123, }, } as never; - jest - .spyOn(hooks, 'useEsDocSearch') - .mockImplementation(() => [2, mockHit, mockIndexPattern, () => {}]); + jest.spyOn(hooks, 'useEsDocSearch').mockImplementation(() => [2, mockHit, () => {}]); jest.spyOn(useUiSettingHook, 'useUiSetting').mockImplementation(() => { return false; }); @@ -107,7 +105,7 @@ describe('Source Viewer component', () => { diff --git a/src/plugins/discover/public/application/components/source_viewer/source_viewer.tsx b/src/plugins/discover/public/application/components/source_viewer/source_viewer.tsx index 9e37ae8f8bf93..31d4d866df21e 100644 --- a/src/plugins/discover/public/application/components/source_viewer/source_viewer.tsx +++ b/src/plugins/discover/public/application/components/source_viewer/source_viewer.tsx @@ -17,11 +17,12 @@ import { getServices } from '../../../kibana_services'; import { SEARCH_FIELDS_FROM_SOURCE } from '../../../../common'; import { ElasticRequestState } from '../../apps/doc/types'; import { useEsDocSearch } from '../../services/use_es_doc_search'; +import { IndexPattern } from '../../../../../data_views/common'; interface SourceViewerProps { id: string; index: string; - indexPatternId: string; + indexPattern: IndexPattern; hasLineNumbers: boolean; width?: number; } @@ -29,19 +30,17 @@ interface SourceViewerProps { export const SourceViewer = ({ id, index, - indexPatternId, + indexPattern, width, hasLineNumbers, }: SourceViewerProps) => { const [editor, setEditor] = useState(); const [jsonValue, setJsonValue] = useState(''); - const indexPatternService = getServices().data.indexPatterns; const useNewFieldsApi = !getServices().uiSettings.get(SEARCH_FIELDS_FROM_SOURCE); - const [reqState, hit, , requestData] = useEsDocSearch({ + const [reqState, hit, requestData] = useEsDocSearch({ id, index, - indexPatternId, - indexPatternService, + indexPattern, requestSource: useNewFieldsApi, }); @@ -106,11 +105,7 @@ export const SourceViewer = ({ ); - if ( - reqState === ElasticRequestState.Error || - reqState === ElasticRequestState.NotFound || - reqState === ElasticRequestState.NotFoundIndexPattern - ) { + if (reqState === ElasticRequestState.Error || reqState === ElasticRequestState.NotFound) { return errorState; } diff --git a/src/plugins/discover/public/application/components/table/table.tsx b/src/plugins/discover/public/application/components/table/table.tsx index 7f597d846f88f..e64dbd10f7855 100644 --- a/src/plugins/discover/public/application/components/table/table.tsx +++ b/src/plugins/discover/public/application/components/table/table.tsx @@ -25,7 +25,7 @@ export interface DocViewerTableProps { columns?: string[]; filter?: DocViewFilterFn; hit: ElasticSearchHit; - indexPattern?: IndexPattern; + indexPattern: IndexPattern; onAddColumn?: (columnName: string) => void; onRemoveColumn?: (columnName: string) => void; } diff --git a/src/plugins/discover/public/application/doc_views/doc_views_types.ts b/src/plugins/discover/public/application/doc_views/doc_views_types.ts index 48bebec22b9b5..d3e482c0f2e1d 100644 --- a/src/plugins/discover/public/application/doc_views/doc_views_types.ts +++ b/src/plugins/discover/public/application/doc_views/doc_views_types.ts @@ -32,7 +32,7 @@ export interface DocViewRenderProps { columns?: string[]; filter?: DocViewFilterFn; hit: ElasticSearchHit; - indexPattern?: IndexPattern; + indexPattern: IndexPattern; onAddColumn?: (columnName: string) => void; onRemoveColumn?: (columnName: string) => void; } diff --git a/src/plugins/discover/public/application/helpers/use_index_pattern.test.tsx b/src/plugins/discover/public/application/helpers/use_index_pattern.test.tsx index 85282afb6fc37..dfc54d8630742 100644 --- a/src/plugins/discover/public/application/helpers/use_index_pattern.test.tsx +++ b/src/plugins/discover/public/application/helpers/use_index_pattern.test.tsx @@ -8,12 +8,24 @@ import { useIndexPattern } from './use_index_pattern'; import { indexPatternMock } from '../../__mocks__/index_pattern'; import { indexPatternsMock } from '../../__mocks__/index_patterns'; -import { renderHook, act } from '@testing-library/react-hooks'; +import { renderHook } from '@testing-library/react-hooks'; describe('Use Index Pattern', () => { test('returning a valid index pattern', async () => { - const { result } = renderHook(() => useIndexPattern(indexPatternsMock, 'the-index-pattern-id')); - await act(() => Promise.resolve()); - expect(result.current).toBe(indexPatternMock); + const { result, waitForNextUpdate } = renderHook(() => + useIndexPattern(indexPatternsMock, 'the-index-pattern-id') + ); + await waitForNextUpdate(); + expect(result.current.indexPattern).toBe(indexPatternMock); + expect(result.current.error).toBe(undefined); + }); + + test('returning an error', async () => { + const { result, waitForNextUpdate } = renderHook(() => + useIndexPattern(indexPatternsMock, 'invalid-index-pattern-id') + ); + await waitForNextUpdate(); + expect(result.current.indexPattern).toBe(undefined); + expect(result.current.error).toBeTruthy(); }); }); diff --git a/src/plugins/discover/public/application/helpers/use_index_pattern.tsx b/src/plugins/discover/public/application/helpers/use_index_pattern.tsx index f53d131920c5c..374f83cbbfe72 100644 --- a/src/plugins/discover/public/application/helpers/use_index_pattern.tsx +++ b/src/plugins/discover/public/application/helpers/use_index_pattern.tsx @@ -10,13 +10,18 @@ import { IndexPattern, IndexPatternsContract } from '../../../../data/common'; export const useIndexPattern = (indexPatterns: IndexPatternsContract, indexPatternId: string) => { const [indexPattern, setIndexPattern] = useState(undefined); + const [error, setError] = useState(); useEffect(() => { async function loadIndexPattern() { - const ip = await indexPatterns.get(indexPatternId); - setIndexPattern(ip); + try { + const item = await indexPatterns.get(indexPatternId); + setIndexPattern(item); + } catch (e) { + setError(e); + } } loadIndexPattern(); - }); - return indexPattern; + }, [indexPatternId, indexPatterns]); + return { indexPattern, error }; }; diff --git a/src/plugins/discover/public/application/services/use_es_doc_search.test.tsx b/src/plugins/discover/public/application/services/use_es_doc_search.test.tsx index af7d189e62882..ca57b470b471a 100644 --- a/src/plugins/discover/public/application/services/use_es_doc_search.test.tsx +++ b/src/plugins/discover/public/application/services/use_es_doc_search.test.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { renderHook, act } from '@testing-library/react-hooks'; +import { renderHook } from '@testing-library/react-hooks'; import { buildSearchBody, useEsDocSearch } from './use_es_doc_search'; import { Observable } from 'rxjs'; import { IndexPattern } from 'src/plugins/data/common'; @@ -175,26 +175,14 @@ describe('Test of helper / hook', () => { const indexPattern = { getComputedFields: () => [], }; - const getMock = jest.fn(() => Promise.resolve(indexPattern)); - const indexPatternService = { - get: getMock, - } as unknown as IndexPattern; const props = { id: '1', index: 'index1', - indexPatternId: 'xyz', - indexPatternService, - } as unknown as DocProps; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let hook: any; - await act(async () => { - hook = renderHook((p: DocProps) => useEsDocSearch(p), { initialProps: props }); - }); - expect(hook.result.current.slice(0, 3)).toEqual([ - ElasticRequestState.Loading, - null, indexPattern, - ]); - expect(getMock).toHaveBeenCalled(); + } as unknown as DocProps; + + const { result } = renderHook((p: DocProps) => useEsDocSearch(p), { initialProps: props }); + + expect(result.current.slice(0, 2)).toEqual([ElasticRequestState.Loading, null]); }); }); diff --git a/src/plugins/discover/public/application/services/use_es_doc_search.ts b/src/plugins/discover/public/application/services/use_es_doc_search.ts index a2f0cd6f8442b..16a24ff27292b 100644 --- a/src/plugins/discover/public/application/services/use_es_doc_search.ts +++ b/src/plugins/discover/public/application/services/use_es_doc_search.ts @@ -64,11 +64,9 @@ export function buildSearchBody( export function useEsDocSearch({ id, index, - indexPatternId, - indexPatternService, + indexPattern, requestSource, -}: DocProps): [ElasticRequestState, ElasticSearchHit | null, IndexPattern | null, () => void] { - const [indexPattern, setIndexPattern] = useState(null); +}: DocProps): [ElasticRequestState, ElasticSearchHit | null | null, () => void] { const [status, setStatus] = useState(ElasticRequestState.Loading); const [hit, setHit] = useState(null); const { data, uiSettings } = useMemo(() => getServices(), []); @@ -76,14 +74,11 @@ export function useEsDocSearch({ const requestData = useCallback(async () => { try { - const indexPatternEntity = await indexPatternService.get(indexPatternId); - setIndexPattern(indexPatternEntity); - const { rawResponse } = await data.search .search({ params: { index, - body: buildSearchBody(id, indexPatternEntity, useNewFieldsApi, requestSource)?.body, + body: buildSearchBody(id, indexPattern, useNewFieldsApi, requestSource)?.body, }, }) .toPromise(); @@ -105,11 +100,11 @@ export function useEsDocSearch({ setStatus(ElasticRequestState.Error); } } - }, [id, index, indexPatternId, indexPatternService, data.search, useNewFieldsApi, requestSource]); + }, [id, index, indexPattern, data.search, useNewFieldsApi, requestSource]); useEffect(() => { requestData(); }, [requestData]); - return [status, hit, indexPattern, requestData]; + return [status, hit, requestData]; } diff --git a/src/plugins/discover/public/plugin.tsx b/src/plugins/discover/public/plugin.tsx index d86e5f363630c..6d30e6fd9e8a9 100644 --- a/src/plugins/discover/public/plugin.tsx +++ b/src/plugins/discover/public/plugin.tsx @@ -267,7 +267,7 @@ export class DiscoverPlugin From 9dcf5bf1b70632dc511e48c2da2ef53d899b3df8 Mon Sep 17 00:00:00 2001 From: David Roberts Date: Tue, 19 Oct 2021 15:42:49 +0100 Subject: [PATCH 058/204] [ML] Stop reading the ml.max_open_jobs node attribute (#115524) The ml.max_open_jobs node attribute is going away in version 8, as the maximum number of open jobs has been defined by a dynamic cluster-wide setting during the 7 series and there is no chance of version 8 needing to run in a mixed version cluster with version 6. The ml.machine_memory attribute will still be available, so this can be checked instead as a way of detecting ML nodes. --- x-pack/plugins/ml/server/lib/node_utils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/ml/server/lib/node_utils.ts b/x-pack/plugins/ml/server/lib/node_utils.ts index 82e5d7f469849..a13e44b307a7b 100644 --- a/x-pack/plugins/ml/server/lib/node_utils.ts +++ b/x-pack/plugins/ml/server/lib/node_utils.ts @@ -17,8 +17,8 @@ export async function getMlNodeCount(client: IScopedClusterClient): Promise { if (body.nodes[k].attributes !== undefined) { - const maxOpenJobs = +body.nodes[k].attributes['ml.max_open_jobs']; - if (maxOpenJobs !== null && maxOpenJobs > 0) { + const machineMemory = +body.nodes[k].attributes['ml.machine_memory']; + if (machineMemory !== null && machineMemory > 0) { count++; } } From e8663d4ea420cf8b01793e2a61d8963ff45c2b6a Mon Sep 17 00:00:00 2001 From: Tim Roes Date: Tue, 19 Oct 2021 16:43:23 +0200 Subject: [PATCH 059/204] [Discover] Show ignored field values (#115040) * WIP replacing indexPattern.flattenHit by tabify * Fix jest tests * Read metaFields from index pattern * Remove old test code * remove unnecessary changes * Remove flattenHitWrapper APIs * Fix imports * Fix missing metaFields * Add all meta fields to allowlist * Improve inline comments * Move flattenHit test to new implementation * Add deprecation comment to implementation * WIP - Show ignored field values * Disable filters in doc_table * remove redundant comments * No, it wasn't * start warning message * Enable ignored values in CSV reports * Add help tooltip * Better styling with warning plus collapsible button * Disable filtering within table for ignored values * Fix jest tests * Fix types in tests * Add more tests and documentation * Remove comment * Move dangerouslySetInnerHTML into helper method * Extract document formatting into common utility * Remove HTML source field formatter * Move formatHit to Discover * Change wording of ignored warning * Add cache for formatted hits * Remove dead type * Fix row_formatter for objects * Improve mobile layout * Fix jest tests * Fix typo * Remove additional span again * Change mock to revert test * Improve tests * More jest tests * Fix typo * Change wording * Remove dead comment Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../common/search/tabify/tabify_docs.test.ts | 49 ++++++++ .../data/common/search/tabify/tabify_docs.ts | 38 ++++++- .../data_views/common/data_views/data_view.ts | 13 --- .../common/data_views/format_hit.ts | 74 ------------ .../data_views/common/data_views/index.ts | 1 - src/plugins/data_views/public/index.ts | 2 +- src/plugins/discover/kibana.json | 3 +- .../public/__mocks__/index_pattern.ts | 18 ++- .../__mocks__/index_pattern_with_timefield.ts | 12 +- .../discover/public/__mocks__/services.ts | 11 +- .../apps/context/context_app.test.tsx | 4 + .../doc_table/components/table_row.tsx | 42 +++++-- .../doc_table/lib/row_formatter.test.ts | 86 ++++++++------ .../doc_table/lib/row_formatter.tsx | 36 ++---- .../layout/discover_documents.test.tsx | 5 + .../layout/discover_layout.test.tsx | 13 +++ ...ver_index_pattern_management.test.tsx.snap | 39 +------ .../sidebar/lib/field_calculator.js | 2 +- .../apps/main/utils/calc_field_counts.ts | 2 +- .../discover_grid/discover_grid.test.tsx | 5 + .../discover_grid/discover_grid.tsx | 6 +- .../get_render_cell_value.test.tsx | 98 ++++++++++++++-- .../discover_grid/get_render_cell_value.tsx | 55 ++++----- .../components/table/table.test.tsx | 34 +----- .../application/components/table/table.tsx | 12 +- .../components/table/table_cell_actions.tsx | 6 +- .../components/table/table_cell_value.tsx | 107 ++++++++++++++++-- .../components/table/table_columns.tsx | 17 ++- .../table/table_row_btn_filter_add.tsx | 2 +- .../table/table_row_btn_filter_remove.tsx | 2 +- .../application/helpers/format_hit.test.ts | 96 ++++++++++++++++ .../public/application/helpers/format_hit.ts | 67 +++++++++++ .../application/helpers/format_value.test.ts | 69 +++++++++++ .../application/helpers/format_value.ts | 39 +++++++ .../helpers/get_ignored_reason.test.ts | 54 +++++++++ .../application/helpers/get_ignored_reason.ts | 52 +++++++++ src/plugins/discover/public/build_services.ts | 3 + src/plugins/discover/public/plugin.tsx | 2 + .../common/converters/source.test.ts | 21 +--- .../common/converters/source.tsx | 55 +-------- src/plugins/field_formats/common/types.ts | 4 - .../generate_csv/generate_csv.ts | 2 +- .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - 44 files changed, 858 insertions(+), 402 deletions(-) delete mode 100644 src/plugins/data_views/common/data_views/format_hit.ts create mode 100644 src/plugins/discover/public/application/helpers/format_hit.test.ts create mode 100644 src/plugins/discover/public/application/helpers/format_hit.ts create mode 100644 src/plugins/discover/public/application/helpers/format_value.test.ts create mode 100644 src/plugins/discover/public/application/helpers/format_value.ts create mode 100644 src/plugins/discover/public/application/helpers/get_ignored_reason.test.ts create mode 100644 src/plugins/discover/public/application/helpers/get_ignored_reason.ts diff --git a/src/plugins/data/common/search/tabify/tabify_docs.test.ts b/src/plugins/data/common/search/tabify/tabify_docs.test.ts index a2910a1be4a9a..1964247b09585 100644 --- a/src/plugins/data/common/search/tabify/tabify_docs.test.ts +++ b/src/plugins/data/common/search/tabify/tabify_docs.test.ts @@ -41,6 +41,11 @@ function create(id: string) { }); } +const meta = { + _index: 'index-name', + _id: '1', +}; + describe('tabify_docs', () => { describe('flattenHit', () => { let indexPattern: DataView; @@ -70,6 +75,50 @@ describe('tabify_docs', () => { expect(Object.keys(response)).toEqual(expectedOrder); expect(Object.entries(response).map(([key]) => key)).toEqual(expectedOrder); }); + + it('does merge values from ignored_field_values and fields correctly', () => { + const flatten = flattenHit( + { + ...meta, + fields: { 'extension.keyword': ['foo'], extension: ['foo', 'ignored'] }, + ignored_field_values: { + 'extension.keyword': ['ignored'], + fully_ignored: ['some', 'value'], + }, + }, + indexPattern, + { includeIgnoredValues: true } + ); + expect(flatten).toHaveProperty(['extension.keyword'], ['foo', 'ignored']); + expect(flatten).toHaveProperty('extension', ['foo', 'ignored']); + expect(flatten).toHaveProperty('fully_ignored', ['some', 'value']); + }); + + it('does not merge values from ignored_field_values into _source', () => { + const flatten = flattenHit( + { + ...meta, + _source: { 'extension.keyword': ['foo', 'ignored'] }, + ignored_field_values: { 'extension.keyword': ['ignored'] }, + }, + indexPattern, + { includeIgnoredValues: true, source: true } + ); + expect(flatten).toHaveProperty(['extension.keyword'], ['foo', 'ignored']); + }); + + it('does merge ignored_field_values when no _source was present, even when parameter was on', () => { + const flatten = flattenHit( + { + ...meta, + fields: { 'extension.keyword': ['foo'] }, + ignored_field_values: { 'extension.keyword': ['ignored'] }, + }, + indexPattern, + { includeIgnoredValues: true, source: true } + ); + expect(flatten).toHaveProperty(['extension.keyword'], ['foo', 'ignored']); + }); }); describe('tabifyDocs', () => { diff --git a/src/plugins/data/common/search/tabify/tabify_docs.ts b/src/plugins/data/common/search/tabify/tabify_docs.ts index 4259488771761..353a0c10ba12a 100644 --- a/src/plugins/data/common/search/tabify/tabify_docs.ts +++ b/src/plugins/data/common/search/tabify/tabify_docs.ts @@ -55,8 +55,18 @@ export interface TabifyDocsOptions { * merged into the flattened document. */ source?: boolean; + /** + * If set to `true` values that have been ignored in ES (ignored_field_values) + * will be merged into the flattened document. This will only have an effect if + * the `hit` has been retrieved using the `fields` option. + */ + includeIgnoredValues?: boolean; } +// This is an overwrite of the SearchHit type to add the ignored_field_values. +// Can be removed once the estypes.SearchHit knows about ignored_field_values +type Hit = estypes.SearchHit & { ignored_field_values?: Record }; + /** * Flattens an individual hit (from an ES response) into an object. This will * create flattened field names, like `user.name`. @@ -65,11 +75,7 @@ export interface TabifyDocsOptions { * @param indexPattern The index pattern for the requested index if available. * @param params Parameters how to flatten the hit */ -export function flattenHit( - hit: estypes.SearchHit, - indexPattern?: IndexPattern, - params?: TabifyDocsOptions -) { +export function flattenHit(hit: Hit, indexPattern?: IndexPattern, params?: TabifyDocsOptions) { const flat = {} as Record; function flatten(obj: Record, keyPrefix: string = '') { @@ -109,6 +115,28 @@ export function flattenHit( flatten(hit.fields || {}); if (params?.source !== false && hit._source) { flatten(hit._source as Record); + } else if (params?.includeIgnoredValues && hit.ignored_field_values) { + // If enabled merge the ignored_field_values into the flattened hit. This will + // merge values that are not actually indexed by ES (i.e. ignored), e.g. because + // they were above the `ignore_above` limit or malformed for specific types. + // This API will only contain the values that were actually ignored, i.e. for the same + // field there might exist another value in the `fields` response, why this logic + // merged them both together. We do not merge this (even if enabled) in case source has been + // merged, since we would otherwise duplicate values, since ignore_field_values and _source + // contain the same values. + Object.entries(hit.ignored_field_values).forEach(([fieldName, fieldValue]) => { + if (flat[fieldName]) { + // If there was already a value from the fields API, make sure we're merging both together + if (Array.isArray(flat[fieldName])) { + flat[fieldName] = [...flat[fieldName], ...fieldValue]; + } else { + flat[fieldName] = [flat[fieldName], ...fieldValue]; + } + } else { + // If no previous value was assigned we can simply use the value from `ignored_field_values` as it is + flat[fieldName] = fieldValue; + } + }); } // Merge all valid meta fields into the flattened object diff --git a/src/plugins/data_views/common/data_views/data_view.ts b/src/plugins/data_views/common/data_views/data_view.ts index 57db127208dc3..b7823677b70f9 100644 --- a/src/plugins/data_views/common/data_views/data_view.ts +++ b/src/plugins/data_views/common/data_views/data_view.ts @@ -17,7 +17,6 @@ import { DuplicateField } from '../../../kibana_utils/common'; import { IIndexPattern, IFieldType } from '../../common'; import { DataViewField, IIndexPatternFieldList, fieldList } from '../fields'; -import { formatHitProvider } from './format_hit'; import { flattenHitWrapper } from './flatten_hit'; import { FieldFormatsStartCommon, @@ -45,8 +44,6 @@ interface SavedObjectBody { type?: string; } -type FormatFieldFn = (hit: Record, fieldName: string) => any; - export class DataView implements IIndexPattern { public id?: string; public title: string = ''; @@ -67,11 +64,6 @@ export class DataView implements IIndexPattern { * Type is used to identify rollup index patterns */ public type: string | undefined; - public formatHit: { - (hit: Record, type?: string): any; - formatField: FormatFieldFn; - }; - public formatField: FormatFieldFn; /** * @deprecated Use `flattenHit` utility method exported from data plugin instead. */ @@ -103,11 +95,6 @@ export class DataView implements IIndexPattern { this.fields = fieldList([], this.shortDotsEnable); this.flattenHit = flattenHitWrapper(this, metaFields); - this.formatHit = formatHitProvider( - this, - fieldFormats.getDefaultInstance(KBN_FIELD_TYPES.STRING) - ); - this.formatField = this.formatHit.formatField; // set values this.id = spec.id; diff --git a/src/plugins/data_views/common/data_views/format_hit.ts b/src/plugins/data_views/common/data_views/format_hit.ts deleted file mode 100644 index c8e6e8e337155..0000000000000 --- a/src/plugins/data_views/common/data_views/format_hit.ts +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import _ from 'lodash'; -import { DataView } from './data_view'; -import { FieldFormatsContentType } from '../../../field_formats/common'; - -const formattedCache = new WeakMap(); -const partialFormattedCache = new WeakMap(); - -// Takes a hit, merges it with any stored/scripted fields, and with the metaFields -// returns a formatted version -export function formatHitProvider(dataView: DataView, defaultFormat: any) { - function convert( - hit: Record, - val: any, - fieldName: string, - type: FieldFormatsContentType = 'html' - ) { - const field = dataView.fields.getByName(fieldName); - const format = field ? dataView.getFormatterForField(field) : defaultFormat; - - return format.convert(val, type, { field, hit, indexPattern: dataView }); - } - - function formatHit(hit: Record, type: string = 'html') { - const cached = formattedCache.get(hit); - if (cached) { - return cached; - } - - // use and update the partial cache, but don't rewrite it. - // _source is stored in partialFormattedCache but not formattedCache - const partials = partialFormattedCache.get(hit) || {}; - partialFormattedCache.set(hit, partials); - - const cache: Record = {}; - formattedCache.set(hit, cache); - - _.forOwn(dataView.flattenHit(hit), function (val: any, fieldName?: string) { - // sync the formatted and partial cache - if (!fieldName) { - return; - } - const formatted = - partials[fieldName] == null ? convert(hit, val, fieldName) : partials[fieldName]; - cache[fieldName] = partials[fieldName] = formatted; - }); - - return cache; - } - - formatHit.formatField = function (hit: Record, fieldName: string) { - let partials = partialFormattedCache.get(hit); - if (partials && partials[fieldName] != null) { - return partials[fieldName]; - } - - if (!partials) { - partials = {}; - partialFormattedCache.set(hit, partials); - } - - const val = fieldName === '_source' ? hit._source : dataView.flattenHit(hit)[fieldName]; - return convert(hit, val, fieldName); - }; - - return formatHit; -} diff --git a/src/plugins/data_views/common/data_views/index.ts b/src/plugins/data_views/common/data_views/index.ts index 7c94dff961c9c..d925d42fbea0d 100644 --- a/src/plugins/data_views/common/data_views/index.ts +++ b/src/plugins/data_views/common/data_views/index.ts @@ -8,6 +8,5 @@ export * from './_pattern_cache'; export * from './flatten_hit'; -export * from './format_hit'; export * from './data_view'; export * from './data_views'; diff --git a/src/plugins/data_views/public/index.ts b/src/plugins/data_views/public/index.ts index 5c810ec1fd4c8..3a6b5ccb237f2 100644 --- a/src/plugins/data_views/public/index.ts +++ b/src/plugins/data_views/public/index.ts @@ -13,7 +13,7 @@ export { ILLEGAL_CHARACTERS, validateDataView, } from '../common/lib'; -export { formatHitProvider, onRedirectNoIndexPattern } from './data_views'; +export { onRedirectNoIndexPattern } from './data_views'; export { IndexPatternField, IIndexPatternFieldList, TypeMeta } from '../common'; diff --git a/src/plugins/discover/kibana.json b/src/plugins/discover/kibana.json index 3d5fdefd276d3..791ce54a0cb1b 100644 --- a/src/plugins/discover/kibana.json +++ b/src/plugins/discover/kibana.json @@ -8,6 +8,7 @@ "data", "embeddable", "inspector", + "fieldFormats", "kibanaLegacy", "urlForwarding", "navigation", @@ -16,7 +17,7 @@ "indexPatternFieldEditor" ], "optionalPlugins": ["home", "share", "usageCollection", "spaces"], - "requiredBundles": ["kibanaUtils", "home", "kibanaReact", "fieldFormats", "dataViews"], + "requiredBundles": ["kibanaUtils", "home", "kibanaReact", "dataViews"], "extraPublicDirs": ["common"], "owner": { "name": "Data Discovery", diff --git a/src/plugins/discover/public/__mocks__/index_pattern.ts b/src/plugins/discover/public/__mocks__/index_pattern.ts index 2acb512617a6b..d33445baa0a2b 100644 --- a/src/plugins/discover/public/__mocks__/index_pattern.ts +++ b/src/plugins/discover/public/__mocks__/index_pattern.ts @@ -6,8 +6,7 @@ * Side Public License, v 1. */ -import type { estypes } from '@elastic/elasticsearch'; -import { flattenHit, IIndexPatternFieldList } from '../../../data/common'; +import { IIndexPatternFieldList } from '../../../data/common'; import { IndexPattern } from '../../../data/common'; const fields = [ @@ -28,6 +27,7 @@ const fields = [ { name: 'message', type: 'string', + displayName: 'message', scripted: false, filterable: false, aggregatable: false, @@ -35,6 +35,7 @@ const fields = [ { name: 'extension', type: 'string', + displayName: 'extension', scripted: false, filterable: true, aggregatable: true, @@ -42,6 +43,7 @@ const fields = [ { name: 'bytes', type: 'number', + displayName: 'bytesDisplayName', scripted: false, filterable: true, aggregatable: true, @@ -49,12 +51,14 @@ const fields = [ { name: 'scripted', type: 'number', + displayName: 'scripted', scripted: true, filterable: false, }, { name: 'object.value', type: 'number', + displayName: 'object.value', scripted: false, filterable: true, aggregatable: true, @@ -73,23 +77,15 @@ const indexPattern = { id: 'the-index-pattern-id', title: 'the-index-pattern-title', metaFields: ['_index', '_score'], - formatField: jest.fn(), - flattenHit: undefined, - formatHit: jest.fn((hit) => (hit.fields ? hit.fields : hit._source)), fields, getComputedFields: () => ({ docvalueFields: [], scriptFields: {}, storedFields: ['*'] }), getSourceFiltering: () => ({}), getFieldByName: jest.fn(() => ({})), timeFieldName: '', docvalueFields: [], - getFormatterForField: () => ({ convert: () => 'formatted' }), + getFormatterForField: jest.fn(() => ({ convert: (value: unknown) => value })), } as unknown as IndexPattern; indexPattern.isTimeBased = () => !!indexPattern.timeFieldName; -indexPattern.formatField = (hit: Record, fieldName: string) => { - return fieldName === '_source' - ? hit._source - : flattenHit(hit as unknown as estypes.SearchHit, indexPattern)[fieldName]; -}; export const indexPatternMock = indexPattern; diff --git a/src/plugins/discover/public/__mocks__/index_pattern_with_timefield.ts b/src/plugins/discover/public/__mocks__/index_pattern_with_timefield.ts index 6cf8e8b3485ff..906ebdebdd06a 100644 --- a/src/plugins/discover/public/__mocks__/index_pattern_with_timefield.ts +++ b/src/plugins/discover/public/__mocks__/index_pattern_with_timefield.ts @@ -6,9 +6,8 @@ * Side Public License, v 1. */ -import { flattenHit, IIndexPatternFieldList } from '../../../data/common'; +import { IIndexPatternFieldList } from '../../../data/common'; import { IndexPattern } from '../../../data/common'; -import type { estypes } from '@elastic/elasticsearch'; const fields = [ { @@ -64,23 +63,16 @@ const indexPattern = { id: 'index-pattern-with-timefield-id', title: 'index-pattern-with-timefield', metaFields: ['_index', '_score'], - flattenHit: undefined, - formatHit: jest.fn((hit) => hit._source), fields, getComputedFields: () => ({}), getSourceFiltering: () => ({}), getFieldByName: (name: string) => fields.getByName(name), timeFieldName: 'timestamp', - getFormatterForField: () => ({ convert: () => 'formatted' }), + getFormatterForField: () => ({ convert: (value: unknown) => value }), isTimeNanosBased: () => false, popularizeField: () => {}, } as unknown as IndexPattern; indexPattern.isTimeBased = () => !!indexPattern.timeFieldName; -indexPattern.formatField = (hit: Record, fieldName: string) => { - return fieldName === '_source' - ? hit._source - : flattenHit(hit as unknown as estypes.SearchHit, indexPattern)[fieldName]; -}; export const indexPatternWithTimefieldMock = indexPattern; diff --git a/src/plugins/discover/public/__mocks__/services.ts b/src/plugins/discover/public/__mocks__/services.ts index 8cc5ccf5aa121..6a90ed42417e6 100644 --- a/src/plugins/discover/public/__mocks__/services.ts +++ b/src/plugins/discover/public/__mocks__/services.ts @@ -13,6 +13,7 @@ import { CONTEXT_STEP_SETTING, DEFAULT_COLUMNS_SETTING, DOC_HIDE_TIME_COLUMN_SETTING, + MAX_DOC_FIELDS_DISPLAYED, SAMPLE_SIZE_SETTING, SORT_DEFAULT_ORDER_SETTING, } from '../../common'; @@ -43,9 +44,13 @@ export const discoverServiceMock = { save: true, }, }, + fieldFormats: { + getDefaultInstance: jest.fn(() => ({ convert: (value: unknown) => value })), + getFormatterForField: jest.fn(() => ({ convert: (value: unknown) => value })), + }, filterManager: dataPlugin.query.filterManager, uiSettings: { - get: (key: string) => { + get: jest.fn((key: string) => { if (key === 'fields:popularLimit') { return 5; } else if (key === DEFAULT_COLUMNS_SETTING) { @@ -62,8 +67,10 @@ export const discoverServiceMock = { return false; } else if (key === SAMPLE_SIZE_SETTING) { return 250; + } else if (key === MAX_DOC_FIELDS_DISPLAYED) { + return 50; } - }, + }), isDefault: (key: string) => { return true; }, diff --git a/src/plugins/discover/public/application/apps/context/context_app.test.tsx b/src/plugins/discover/public/application/apps/context/context_app.test.tsx index 0e50f8f714a2c..d1c557f2839bc 100644 --- a/src/plugins/discover/public/application/apps/context/context_app.test.tsx +++ b/src/plugins/discover/public/application/apps/context/context_app.test.tsx @@ -62,6 +62,10 @@ describe('ContextApp test', () => { navigation: mockNavigationPlugin, core: { notifications: { toasts: [] } }, history: () => {}, + fieldFormats: { + getDefaultInstance: jest.fn(() => ({ convert: (value: unknown) => value })), + getFormatterForField: jest.fn(() => ({ convert: (value: unknown) => value })), + }, filterManager: mockFilterManager, uiSettings: uiSettingsMock, } as unknown as DiscoverServices); diff --git a/src/plugins/discover/public/application/apps/main/components/doc_table/components/table_row.tsx b/src/plugins/discover/public/application/apps/main/components/doc_table/components/table_row.tsx index d91735460af08..0bf4a36555d16 100644 --- a/src/plugins/discover/public/application/apps/main/components/doc_table/components/table_row.tsx +++ b/src/plugins/discover/public/application/apps/main/components/doc_table/components/table_row.tsx @@ -10,6 +10,7 @@ import React, { Fragment, useCallback, useMemo, useState } from 'react'; import classNames from 'classnames'; import { i18n } from '@kbn/i18n'; import { EuiButtonEmpty, EuiIcon } from '@elastic/eui'; +import { formatFieldValue } from '../../../../../helpers/format_value'; import { flattenHit } from '../../../../../../../../data/common'; import { DocViewer } from '../../../../../components/doc_viewer/doc_viewer'; import { FilterManager, IndexPattern } from '../../../../../../../../data/public'; @@ -58,7 +59,10 @@ export const TableRow = ({ }); const anchorDocTableRowSubj = row.isAnchor ? ' docTableAnchorRow' : ''; - const flattenedRow = useMemo(() => flattenHit(row, indexPattern), [indexPattern, row]); + const flattenedRow = useMemo( + () => flattenHit(row, indexPattern, { includeIgnoredValues: true }), + [indexPattern, row] + ); const mapping = useMemo(() => indexPattern.fields.getByName, [indexPattern]); // toggle display of the rows details, a full list of the fields from each row @@ -68,13 +72,24 @@ export const TableRow = ({ * Fill an element with the value of a field */ const displayField = (fieldName: string) => { - const formattedField = indexPattern.formatField(row, fieldName); - - // field formatters take care of escaping - // eslint-disable-next-line react/no-danger - const fieldElement = ; + // If we're formatting the _source column, don't use the regular field formatter, + // but our Discover mechanism to format a hit in a better human-readable way. + if (fieldName === '_source') { + return formatRow(row, indexPattern, fieldsToShow); + } + + const formattedField = formatFieldValue( + flattenedRow[fieldName], + row, + indexPattern, + mapping(fieldName) + ); - return
{fieldElement}
; + return ( + // formatFieldValue always returns sanitized HTML + // eslint-disable-next-line react/no-danger +
+ ); }; const inlineFilter = useCallback( (column: string, type: '+' | '-') => { @@ -141,10 +156,9 @@ export const TableRow = ({ ); } else { columns.forEach(function (column: string) { - // when useNewFieldsApi is true, addressing to the fields property is safe - if (useNewFieldsApi && !mapping(column) && !row.fields![column]) { + if (useNewFieldsApi && !mapping(column) && row.fields && !row.fields[column]) { const innerColumns = Object.fromEntries( - Object.entries(row.fields!).filter(([key]) => { + Object.entries(row.fields).filter(([key]) => { return key.indexOf(`${column}.`) === 0; }) ); @@ -161,7 +175,13 @@ export const TableRow = ({ /> ); } else { - const isFilterable = Boolean(mapping(column)?.filterable && filter); + // Check whether the field is defined as filterable in the mapping and does + // NOT have ignored values in it to determine whether we want to allow filtering. + // We should improve this and show a helpful tooltip why the filter buttons are not + // there/disabled when there are ignored values. + const isFilterable = Boolean( + mapping(column)?.filterable && filter && !row._ignored?.includes(column) + ); rowCells.push( { const hit = { _id: 'a', + _index: 'foo', _type: 'doc', _score: 1, _source: { @@ -39,7 +40,7 @@ describe('Row formatter', () => { spec: { id, type, version, timeFieldName, fields: JSON.parse(fields), title }, fieldFormats: fieldFormatsMock, shortDotsEnable: false, - metaFields: [], + metaFields: ['_id', '_type', '_score'], }); }; @@ -47,26 +48,15 @@ describe('Row formatter', () => { const fieldsToShow = indexPattern.fields.getAll().map((fld) => fld.name); - // Realistic response with alphabetical insertion order - const formatHitReturnValue = { - also: 'with \\"quotes\\" or 'single qoutes'', - foo: 'bar', - number: '42', - hello: '<h1>World</h1>', - _id: 'a', - _type: 'doc', - _score: 1, - }; - - const formatHitMock = jest.fn().mockReturnValue(formatHitReturnValue); - beforeEach(() => { - // @ts-expect-error - indexPattern.formatHit = formatHitMock; setServices({ uiSettings: { get: () => 100, }, + fieldFormats: { + getDefaultInstance: jest.fn(() => ({ convert: (value: unknown) => value })), + getFormatterForField: jest.fn(() => ({ convert: (value: unknown) => value })), + }, } as unknown as DiscoverServices); }); @@ -77,32 +67,32 @@ describe('Row formatter', () => { Array [ Array [ "also", - "with \\\\"quotes\\\\" or 'single qoutes'", + "with \\"quotes\\" or 'single quotes'", ], Array [ "foo", "bar", ], Array [ - "number", - "42", + "hello", + "

World

", ], Array [ - "hello", - "<h1>World</h1>", + "number", + 42, ], Array [ "_id", "a", ], - Array [ - "_type", - "doc", - ], Array [ "_score", 1, ], + Array [ + "_type", + "doc", + ], ] } /> @@ -114,6 +104,10 @@ describe('Row formatter', () => { uiSettings: { get: () => 1, }, + fieldFormats: { + getDefaultInstance: jest.fn(() => ({ convert: (value: unknown) => value })), + getFormatterForField: jest.fn(() => ({ convert: (value: unknown) => value })), + }, } as unknown as DiscoverServices); expect(formatRow(hit, indexPattern, [])).toMatchInlineSnapshot(` { Array [ Array [ "also", - "with \\\\"quotes\\\\" or 'single qoutes'", + "with \\"quotes\\" or 'single quotes'", + ], + Array [ + "foo", + "bar", + ], + Array [ + "hello", + "

World

", + ], + Array [ + "number", + 42, + ], + Array [ + "_id", + "a", + ], + Array [ + "_score", + 1, + ], + Array [ + "_type", + "doc", ], ] } @@ -130,18 +148,18 @@ describe('Row formatter', () => { }); it('formats document with highlighted fields first', () => { - expect(formatRow({ ...hit, highlight: { number: '42' } }, indexPattern, fieldsToShow)) + expect(formatRow({ ...hit, highlight: { number: ['42'] } }, indexPattern, fieldsToShow)) .toMatchInlineSnapshot(` { ], Array [ "hello", - "<h1>World</h1>", + "

World

", ], Array [ "_id", "a", ], - Array [ - "_type", - "doc", - ], Array [ "_score", 1, ], + Array [ + "_type", + "doc", + ], ] } /> diff --git a/src/plugins/discover/public/application/apps/main/components/doc_table/lib/row_formatter.tsx b/src/plugins/discover/public/application/apps/main/components/doc_table/lib/row_formatter.tsx index 14cf1839107e7..2702a232f21ef 100644 --- a/src/plugins/discover/public/application/apps/main/components/doc_table/lib/row_formatter.tsx +++ b/src/plugins/discover/public/application/apps/main/components/doc_table/lib/row_formatter.tsx @@ -6,15 +6,17 @@ * Side Public License, v 1. */ +import { estypes } from '@elastic/elasticsearch'; import React, { Fragment } from 'react'; import type { IndexPattern } from 'src/plugins/data/common'; import { MAX_DOC_FIELDS_DISPLAYED } from '../../../../../../../common'; import { getServices } from '../../../../../../kibana_services'; +import { formatHit } from '../../../../../helpers/format_hit'; import './row_formatter.scss'; interface Props { - defPairs: Array<[string, unknown]>; + defPairs: Array<[string, string]>; } const TemplateComponent = ({ defPairs }: Props) => { return ( @@ -24,8 +26,8 @@ const TemplateComponent = ({ defPairs }: Props) => {
{pair[0]}:
{' '} ))} @@ -34,30 +36,12 @@ const TemplateComponent = ({ defPairs }: Props) => { }; export const formatRow = ( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - hit: Record, + hit: estypes.SearchHit, indexPattern: IndexPattern, fieldsToShow: string[] ) => { - const highlights = hit?.highlight ?? {}; - // Keys are sorted in the hits object - const formatted = indexPattern.formatHit(hit); - const fields = indexPattern.fields; - const highlightPairs: Array<[string, unknown]> = []; - const sourcePairs: Array<[string, unknown]> = []; - Object.entries(formatted).forEach(([key, val]) => { - const displayKey = fields.getByName ? fields.getByName(key)?.displayName : undefined; - const pairs = highlights[key] ? highlightPairs : sourcePairs; - if (displayKey) { - if (fieldsToShow.includes(displayKey)) { - pairs.push([displayKey, val]); - } - } else { - pairs.push([key, val]); - } - }); - const maxEntries = getServices().uiSettings.get(MAX_DOC_FIELDS_DISPLAYED); - return ; + const pairs = formatHit(hit, indexPattern, fieldsToShow); + return ; }; export const formatTopLevelObject = ( @@ -68,8 +52,8 @@ export const formatTopLevelObject = ( indexPattern: IndexPattern ) => { const highlights = row.highlight ?? {}; - const highlightPairs: Array<[string, unknown]> = []; - const sourcePairs: Array<[string, unknown]> = []; + const highlightPairs: Array<[string, string]> = []; + const sourcePairs: Array<[string, string]> = []; const sorted = Object.entries(fields).sort(([keyA], [keyB]) => keyA.localeCompare(keyB)); sorted.forEach(([key, values]) => { const field = indexPattern.getFieldByName(key); diff --git a/src/plugins/discover/public/application/apps/main/components/layout/discover_documents.test.tsx b/src/plugins/discover/public/application/apps/main/components/layout/discover_documents.test.tsx index e5212e877e8ba..60540268dcd7f 100644 --- a/src/plugins/discover/public/application/apps/main/components/layout/discover_documents.test.tsx +++ b/src/plugins/discover/public/application/apps/main/components/layout/discover_documents.test.tsx @@ -20,6 +20,11 @@ import { DiscoverDocuments } from './discover_documents'; import { ElasticSearchHit } from '../../../../doc_views/doc_views_types'; import { indexPatternMock } from '../../../../../__mocks__/index_pattern'; +jest.mock('../../../../../kibana_services', () => ({ + ...jest.requireActual('../../../../../kibana_services'), + getServices: () => jest.requireActual('../../../../../__mocks__/services').discoverServiceMock, +})); + setHeaderActionMenuMounter(jest.fn()); function getProps(fetchStatus: FetchStatus, hits: ElasticSearchHit[]) { diff --git a/src/plugins/discover/public/application/apps/main/components/layout/discover_layout.test.tsx b/src/plugins/discover/public/application/apps/main/components/layout/discover_layout.test.tsx index 6ebed3185e2f1..7e3252dce1ef5 100644 --- a/src/plugins/discover/public/application/apps/main/components/layout/discover_layout.test.tsx +++ b/src/plugins/discover/public/application/apps/main/components/layout/discover_layout.test.tsx @@ -33,6 +33,19 @@ import { RequestAdapter } from '../../../../../../../inspector'; import { Chart } from '../chart/point_series'; import { DiscoverSidebar } from '../sidebar/discover_sidebar'; +jest.mock('../../../../../kibana_services', () => ({ + ...jest.requireActual('../../../../../kibana_services'), + getServices: () => ({ + fieldFormats: { + getDefaultInstance: jest.fn(() => ({ convert: (value: unknown) => value })), + getFormatterForField: jest.fn(() => ({ convert: (value: unknown) => value })), + }, + uiSettings: { + get: jest.fn((key: string) => key === 'discover:maxDocFieldsDisplayed' && 50), + }, + }), +})); + setHeaderActionMenuMounter(jest.fn()); function getProps(indexPattern: IndexPattern, wasSidebarClosed?: boolean): DiscoverLayoutProps { diff --git a/src/plugins/discover/public/application/apps/main/components/sidebar/__snapshots__/discover_index_pattern_management.test.tsx.snap b/src/plugins/discover/public/application/apps/main/components/sidebar/__snapshots__/discover_index_pattern_management.test.tsx.snap index ebb06e0b2ecd3..02e2879476a5e 100644 --- a/src/plugins/discover/public/application/apps/main/components/sidebar/__snapshots__/discover_index_pattern_management.test.tsx.snap +++ b/src/plugins/discover/public/application/apps/main/components/sidebar/__snapshots__/discover_index_pattern_management.test.tsx.snap @@ -115,42 +115,7 @@ exports[`Discover IndexPattern Management renders correctly 1`] = ` "deserialize": [MockFunction], "getByFieldType": [MockFunction], "getDefaultConfig": [MockFunction], - "getDefaultInstance": [MockFunction] { - "calls": Array [ - Array [ - "string", - ], - Array [ - "string", - ], - Array [ - "string", - ], - ], - "results": Array [ - Object { - "type": "return", - "value": Object { - "convert": [MockFunction], - "getConverterFor": [MockFunction], - }, - }, - Object { - "type": "return", - "value": Object { - "convert": [MockFunction], - "getConverterFor": [MockFunction], - }, - }, - Object { - "type": "return", - "value": Object { - "convert": [MockFunction], - "getConverterFor": [MockFunction], - }, - }, - ], - }, + "getDefaultInstance": [MockFunction], "getDefaultInstanceCacheResolver": [MockFunction], "getDefaultInstancePlain": [MockFunction], "getDefaultType": [MockFunction], @@ -651,8 +616,6 @@ exports[`Discover IndexPattern Management renders correctly 1`] = ` }, ], "flattenHit": [Function], - "formatField": [Function], - "formatHit": [Function], "getFieldAttrs": [Function], "getOriginalSavedObjectBody": [Function], "id": "logstash-*", diff --git a/src/plugins/discover/public/application/apps/main/components/sidebar/lib/field_calculator.js b/src/plugins/discover/public/application/apps/main/components/sidebar/lib/field_calculator.js index be7e9c616273d..c709f3311105d 100644 --- a/src/plugins/discover/public/application/apps/main/components/sidebar/lib/field_calculator.js +++ b/src/plugins/discover/public/application/apps/main/components/sidebar/lib/field_calculator.js @@ -13,7 +13,7 @@ import { flattenHit } from '../../../../../../../../data/common'; function getFieldValues(hits, field, indexPattern) { const name = field.name; return map(hits, function (hit) { - return flattenHit(hit, indexPattern)[name]; + return flattenHit(hit, indexPattern, { includeIgnoredValues: true })[name]; }); } diff --git a/src/plugins/discover/public/application/apps/main/utils/calc_field_counts.ts b/src/plugins/discover/public/application/apps/main/utils/calc_field_counts.ts index 211c4e5c8b069..2198d2f66b6b4 100644 --- a/src/plugins/discover/public/application/apps/main/utils/calc_field_counts.ts +++ b/src/plugins/discover/public/application/apps/main/utils/calc_field_counts.ts @@ -22,7 +22,7 @@ export function calcFieldCounts( return {}; } for (const hit of rows) { - const fields = Object.keys(flattenHit(hit, indexPattern)); + const fields = Object.keys(flattenHit(hit, indexPattern, { includeIgnoredValues: true })); for (const fieldName of fields) { counts[fieldName] = (counts[fieldName] || 0) + 1; } diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid.test.tsx b/src/plugins/discover/public/application/components/discover_grid/discover_grid.test.tsx index b2be40c008200..22284480afc05 100644 --- a/src/plugins/discover/public/application/components/discover_grid/discover_grid.test.tsx +++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid.test.tsx @@ -19,6 +19,11 @@ import { DiscoverServices } from '../../../build_services'; import { ElasticSearchHit } from '../../doc_views/doc_views_types'; import { getDocId } from './discover_grid_document_selection'; +jest.mock('../../../kibana_services', () => ({ + ...jest.requireActual('../../../kibana_services'), + getServices: () => jest.requireActual('../../../__mocks__/services').discoverServiceMock, +})); + function getProps() { const servicesMock = { uiSettings: uiSettingsMock, diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid.tsx b/src/plugins/discover/public/application/components/discover_grid/discover_grid.tsx index 11323080274a9..ca403c813010b 100644 --- a/src/plugins/discover/public/application/components/discover_grid/discover_grid.tsx +++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid.tsx @@ -271,7 +271,11 @@ export const DiscoverGrid = ({ getRenderCellValueFn( indexPattern, displayedRows, - displayedRows ? displayedRows.map((hit) => flattenHit(hit, indexPattern)) : [], + displayedRows + ? displayedRows.map((hit) => + flattenHit(hit, indexPattern, { includeIgnoredValues: true }) + ) + : [], useNewFieldsApi, fieldsToShow, services.uiSettings.get(MAX_DOC_FIELDS_DISPLAYED) diff --git a/src/plugins/discover/public/application/components/discover_grid/get_render_cell_value.test.tsx b/src/plugins/discover/public/application/components/discover_grid/get_render_cell_value.test.tsx index 6556876217953..3fb96ba9e9daa 100644 --- a/src/plugins/discover/public/application/components/discover_grid/get_render_cell_value.test.tsx +++ b/src/plugins/discover/public/application/components/discover_grid/get_render_cell_value.test.tsx @@ -25,6 +25,9 @@ jest.mock('../../../kibana_services', () => ({ uiSettings: { get: jest.fn(), }, + fieldFormats: { + getDefaultInstance: jest.fn(() => ({ convert: (value: unknown) => (value ? value : '-') })), + }, }), })); @@ -102,7 +105,7 @@ describe('Discover grid cell rendering', function () { rowsSource, rowsSource.map(flatten), false, - [], + ['extension', 'bytes'], 100 ); const component = shallow( @@ -133,7 +136,7 @@ describe('Discover grid cell rendering', function () { } /> - bytes + bytesDisplayName + + _index + + + + _score + + `); }); @@ -196,7 +221,7 @@ describe('Discover grid cell rendering', function () { rowsFields, rowsFields.map(flatten), true, - [], + ['extension', 'bytes'], 100 ); const component = shallow( @@ -229,7 +254,7 @@ describe('Discover grid cell rendering', function () { } /> - bytes + bytesDisplayName + + _index + + + + _score + + `); }); @@ -251,7 +298,7 @@ describe('Discover grid cell rendering', function () { rowsFields, rowsFields.map(flatten), true, - [], + ['extension', 'bytes'], // this is the number of rendered items 1 ); @@ -284,6 +331,41 @@ describe('Discover grid cell rendering', function () { } } /> + + bytesDisplayName + + + + _index + + + + _score + + `); }); @@ -342,7 +424,7 @@ describe('Discover grid cell rendering', function () { rowsFieldsWithTopLevelObject, rowsFieldsWithTopLevelObject.map(flatten), true, - [], + ['object.value', 'extension', 'bytes'], 100 ); const component = shallow( @@ -368,7 +450,7 @@ describe('Discover grid cell rendering', function () { className="dscDiscoverGrid__descriptionListDescription" dangerouslySetInnerHTML={ Object { - "__html": "formatted", + "__html": "100", } } /> @@ -383,7 +465,7 @@ describe('Discover grid cell rendering', function () { rowsFieldsWithTopLevelObject, rowsFieldsWithTopLevelObject.map(flatten), true, - [], + ['extension', 'bytes', 'object.value'], 100 ); const component = shallow( diff --git a/src/plugins/discover/public/application/components/discover_grid/get_render_cell_value.tsx b/src/plugins/discover/public/application/components/discover_grid/get_render_cell_value.tsx index a052971580666..4066c13f6391e 100644 --- a/src/plugins/discover/public/application/components/discover_grid/get_render_cell_value.tsx +++ b/src/plugins/discover/public/application/components/discover_grid/get_render_cell_value.tsx @@ -22,6 +22,8 @@ import { DiscoverGridContext } from './discover_grid_context'; import { JsonCodeEditor } from '../json_code_editor/json_code_editor'; import { defaultMonacoEditorWidth } from './constants'; import { EsHitRecord } from '../../types'; +import { formatFieldValue } from '../../helpers/format_value'; +import { formatHit } from '../../helpers/format_hit'; export const getRenderCellValueFn = ( @@ -145,39 +147,19 @@ export const getRenderCellValueFn = // eslint-disable-next-line @typescript-eslint/no-explicit-any return ; } - const formatted = indexPattern.formatHit(row); - - // Put the most important fields first - const highlights: Record = (row.highlight as Record) ?? {}; - const highlightPairs: Array<[string, string]> = []; - const sourcePairs: Array<[string, string]> = []; - Object.entries(formatted).forEach(([key, val]) => { - const pairs = highlights[key] ? highlightPairs : sourcePairs; - const displayKey = indexPattern.fields.getByName - ? indexPattern.fields.getByName(key)?.displayName - : undefined; - if (displayKey) { - if (fieldsToShow.includes(displayKey)) { - pairs.push([displayKey, val as string]); - } - } else { - pairs.push([key, val as string]); - } - }); + const pairs = formatHit(row, indexPattern, fieldsToShow); return ( - {[...highlightPairs, ...sourcePairs] - .slice(0, maxDocFieldsDisplayed) - .map(([key, value]) => ( - - {key} - - - ))} + {pairs.map(([key, value]) => ( + + {key} + + + ))} ); } @@ -191,12 +173,13 @@ export const getRenderCellValueFn = return {JSON.stringify(rowFlattened[columnId])}; } - const valueFormatted = indexPattern.formatField(row, columnId); - if (typeof valueFormatted === 'undefined') { - return -; - } return ( - // eslint-disable-next-line react/no-danger - + ); }; diff --git a/src/plugins/discover/public/application/components/table/table.test.tsx b/src/plugins/discover/public/application/components/table/table.test.tsx index ce914edcec703..e61333cce1166 100644 --- a/src/plugins/discover/public/application/components/table/table.test.tsx +++ b/src/plugins/discover/public/application/components/table/table.test.tsx @@ -27,6 +27,10 @@ import { getServices } from '../../../kibana_services'; } }, }, + fieldFormats: { + getDefaultInstance: jest.fn(() => ({ convert: (value: unknown) => value })), + getFormatterForField: jest.fn(() => ({ convert: (value: unknown) => value })), + }, })); const indexPattern = { @@ -65,8 +69,7 @@ const indexPattern = { ], }, metaFields: ['_index', '_score'], - flattenHit: jest.fn(), - formatHit: jest.fn((hit) => hit._source), + getFormatterForField: jest.fn(() => ({ convert: (value: unknown) => value })), } as unknown as IndexPattern; indexPattern.fields.getByName = (name: string) => { @@ -359,32 +362,7 @@ describe('DocViewTable at Discover Doc with Fields API', () => { ], }, metaFields: ['_index', '_type', '_score', '_id'], - flattenHit: jest.fn((hit) => { - const result = {} as Record; - Object.keys(hit).forEach((key) => { - if (key !== 'fields') { - result[key] = hit[key]; - } else { - Object.keys(hit.fields).forEach((field) => { - result[field] = hit.fields[field]; - }); - } - }); - return result; - }), - formatHit: jest.fn((hit) => { - const result = {} as Record; - Object.keys(hit).forEach((key) => { - if (key !== 'fields') { - result[key] = hit[key]; - } else { - Object.keys(hit.fields).forEach((field) => { - result[field] = hit.fields[field]; - }); - } - }); - return result; - }), + getFormatterForField: jest.fn(() => ({ convert: (value: unknown) => value })), } as unknown as IndexPattern; indexPatterneCommerce.fields.getByName = (name: string) => { diff --git a/src/plugins/discover/public/application/components/table/table.tsx b/src/plugins/discover/public/application/components/table/table.tsx index e64dbd10f7855..78a6d9ddd3237 100644 --- a/src/plugins/discover/public/application/components/table/table.tsx +++ b/src/plugins/discover/public/application/components/table/table.tsx @@ -20,6 +20,8 @@ import { } from '../../doc_views/doc_views_types'; import { ACTIONS_COLUMN, MAIN_COLUMNS } from './table_columns'; import { getFieldsToShow } from '../../helpers/get_fields_to_show'; +import { getIgnoredReason, IgnoredReason } from '../../helpers/get_ignored_reason'; +import { formatFieldValue } from '../../helpers/format_value'; export interface DocViewerTableProps { columns?: string[]; @@ -46,6 +48,7 @@ export interface FieldRecord { }; value: { formattedValue: string; + ignored?: IgnoredReason; }; } @@ -64,8 +67,6 @@ export const DocViewerTable = ({ [indexPattern?.fields] ); - const formattedHit = useMemo(() => indexPattern?.formatHit(hit, 'html'), [hit, indexPattern]); - const tableColumns = useMemo(() => { return filter ? [ACTIONS_COLUMN, ...MAIN_COLUMNS] : MAIN_COLUMNS; }, [filter]); @@ -96,7 +97,7 @@ export const DocViewerTable = ({ return null; } - const flattened = flattenHit(hit, indexPattern, { source: true }); + const flattened = flattenHit(hit, indexPattern, { source: true, includeIgnoredValues: true }); const fieldsToShow = getFieldsToShow(Object.keys(flattened), indexPattern, showMultiFields); const items: FieldRecord[] = Object.keys(flattened) @@ -115,6 +116,8 @@ export const DocViewerTable = ({ const displayName = fieldMapping?.displayName ?? field; const fieldType = isNestedFieldParent(field, indexPattern) ? 'nested' : fieldMapping?.type; + const ignored = getIgnoredReason(fieldMapping ?? field, hit._ignored); + return { action: { onToggleColumn, @@ -130,7 +133,8 @@ export const DocViewerTable = ({ scripted: Boolean(fieldMapping?.scripted), }, value: { - formattedValue: formattedHit[field], + formattedValue: formatFieldValue(flattened[field], hit, indexPattern, fieldMapping), + ignored, }, }; }); diff --git a/src/plugins/discover/public/application/components/table/table_cell_actions.tsx b/src/plugins/discover/public/application/components/table/table_cell_actions.tsx index 7f2f87e7c296c..e43a17448de2e 100644 --- a/src/plugins/discover/public/application/components/table/table_cell_actions.tsx +++ b/src/plugins/discover/public/application/components/table/table_cell_actions.tsx @@ -21,6 +21,7 @@ interface TableActionsProps { fieldMapping?: IndexPatternField; onFilter: DocViewFilterFn; onToggleColumn: (field: string) => void; + ignoredValue: boolean; } export const TableActions = ({ @@ -30,15 +31,16 @@ export const TableActions = ({ flattenedField, onToggleColumn, onFilter, + ignoredValue, }: TableActionsProps) => { return (
onFilter(fieldMapping, flattenedField, '+')} /> onFilter(fieldMapping, flattenedField, '-')} /> ; +interface IgnoreWarningProps { + reason: IgnoredReason; + rawValue: unknown; +} -export const TableFieldValue = ({ formattedValue, field }: TableFieldValueProps) => { +const IgnoreWarning: React.FC = React.memo(({ rawValue, reason }) => { + const multiValue = Array.isArray(rawValue) && rawValue.length > 1; + + const getToolTipContent = (): string => { + switch (reason) { + case IgnoredReason.IGNORE_ABOVE: + return multiValue + ? i18n.translate('discover.docView.table.ignored.multiAboveTooltip', { + defaultMessage: `One or more values in this field are too long and can't be searched or filtered.`, + }) + : i18n.translate('discover.docView.table.ignored.singleAboveTooltip', { + defaultMessage: `The value in this field is too long and can't be searched or filtered.`, + }); + case IgnoredReason.MALFORMED: + return multiValue + ? i18n.translate('discover.docView.table.ignored.multiMalformedTooltip', { + defaultMessage: `This field has one or more malformed values that can't be searched or filtered.`, + }) + : i18n.translate('discover.docView.table.ignored.singleMalformedTooltip', { + defaultMessage: `The value in this field is malformed and can't be searched or filtered.`, + }); + case IgnoredReason.UNKNOWN: + return multiValue + ? i18n.translate('discover.docView.table.ignored.multiUnknownTooltip', { + defaultMessage: `One or more values in this field were ignored by Elasticsearch and can't be searched or filtered.`, + }) + : i18n.translate('discover.docView.table.ignored.singleUnknownTooltip', { + defaultMessage: `The value in this field was ignored by Elasticsearch and can't be searched or filtered.`, + }); + } + }; + + return ( + + + + + + + + {multiValue + ? i18n.translate('discover.docViews.table.ignored.multiValueLabel', { + defaultMessage: 'Contains ignored values', + }) + : i18n.translate('discover.docViews.table.ignored.singleValueLabel', { + defaultMessage: 'Ignored value', + })} + + + + + ); +}); + +type TableFieldValueProps = Pick & { + formattedValue: FieldRecord['value']['formattedValue']; + rawValue: unknown; + ignoreReason?: IgnoredReason; +}; + +export const TableFieldValue = ({ + formattedValue, + field, + rawValue, + ignoreReason, +}: TableFieldValueProps) => { const [fieldOpen, setFieldOpen] = useState(false); - const value = String(formattedValue); + const value = String(rawValue); const isCollapsible = value.length > COLLAPSE_LINE_LENGTH; const isCollapsed = isCollapsible && !fieldOpen; @@ -32,18 +111,26 @@ export const TableFieldValue = ({ formattedValue, field }: TableFieldValueProps) return ( - {isCollapsible && ( - + {(isCollapsible || ignoreReason) && ( + + {isCollapsible && ( + + + + )} + {ignoreReason && ( + + + + )} + )}
); diff --git a/src/plugins/discover/public/application/components/table/table_columns.tsx b/src/plugins/discover/public/application/components/table/table_columns.tsx index 5bd92fe9166e9..5944f9bede646 100644 --- a/src/plugins/discover/public/application/components/table/table_columns.tsx +++ b/src/plugins/discover/public/application/components/table/table_columns.tsx @@ -31,7 +31,7 @@ export const ACTIONS_COLUMN: EuiBasicTableColumn = { ), render: ( { flattenedField, isActive, onFilter, onToggleColumn }: FieldRecord['action'], - { field: { field, fieldMapping } }: FieldRecord + { field: { field, fieldMapping }, value: { ignored } }: FieldRecord ) => { return ( = { flattenedField={flattenedField} onFilter={onFilter!} onToggleColumn={onToggleColumn} + ignoredValue={!!ignored} /> ); }, @@ -82,8 +83,18 @@ export const MAIN_COLUMNS: Array> = [ ), - render: ({ formattedValue }: FieldRecord['value'], { field: { field } }: FieldRecord) => { - return ; + render: ( + { formattedValue, ignored }: FieldRecord['value'], + { field: { field }, action: { flattenedField } }: FieldRecord + ) => { + return ( + + ); }, }, ]; diff --git a/src/plugins/discover/public/application/components/table/table_row_btn_filter_add.tsx b/src/plugins/discover/public/application/components/table/table_row_btn_filter_add.tsx index 5fe1b4dc33342..de56d733442d6 100644 --- a/src/plugins/discover/public/application/components/table/table_row_btn_filter_add.tsx +++ b/src/plugins/discover/public/application/components/table/table_row_btn_filter_add.tsx @@ -20,7 +20,7 @@ export function DocViewTableRowBtnFilterAdd({ onClick, disabled = false }: Props const tooltipContent = disabled ? ( ) : ( ) : ( ({ + getServices: () => jest.requireActual('../../__mocks__/services').discoverServiceMock, +})); + +describe('formatHit', () => { + let hit: estypes.SearchHit; + beforeEach(() => { + hit = { + _id: '1', + _index: 'logs', + fields: { + message: ['foobar'], + extension: ['png'], + 'object.value': [42, 13], + bytes: [123], + }, + }; + (dataViewMock.getFormatterForField as jest.Mock).mockReturnValue({ + convert: (value: unknown) => `formatted:${value}`, + }); + }); + + afterEach(() => { + (discoverServiceMock.uiSettings.get as jest.Mock).mockReset(); + }); + + it('formats a document as expected', () => { + const formatted = formatHit(hit, dataViewMock, ['message', 'extension', 'object.value']); + expect(formatted).toEqual([ + ['extension', 'formatted:png'], + ['message', 'formatted:foobar'], + ['object.value', 'formatted:42,13'], + ['_index', 'formatted:logs'], + ['_score', undefined], + ]); + }); + + it('orders highlighted fields first', () => { + const formatted = formatHit({ ...hit, highlight: { message: ['%%'] } }, dataViewMock, [ + 'message', + 'extension', + 'object.value', + ]); + expect(formatted.map(([fieldName]) => fieldName)).toEqual([ + 'message', + 'extension', + 'object.value', + '_index', + '_score', + ]); + }); + + it('only limits count of pairs based on advanced setting', () => { + (discoverServiceMock.uiSettings.get as jest.Mock).mockImplementation( + (key) => key === MAX_DOC_FIELDS_DISPLAYED && 2 + ); + const formatted = formatHit(hit, dataViewMock, ['message', 'extension', 'object.value']); + expect(formatted).toEqual([ + ['extension', 'formatted:png'], + ['message', 'formatted:foobar'], + ]); + }); + + it('should not include fields not mentioned in fieldsToShow', () => { + const formatted = formatHit(hit, dataViewMock, ['message', 'object.value']); + expect(formatted).toEqual([ + ['message', 'formatted:foobar'], + ['object.value', 'formatted:42,13'], + ['_index', 'formatted:logs'], + ['_score', undefined], + ]); + }); + + it('should filter fields based on their real name not displayName', () => { + const formatted = formatHit(hit, dataViewMock, ['bytes']); + expect(formatted).toEqual([ + ['bytesDisplayName', 'formatted:123'], + ['_index', 'formatted:logs'], + ['_score', undefined], + ]); + }); +}); diff --git a/src/plugins/discover/public/application/helpers/format_hit.ts b/src/plugins/discover/public/application/helpers/format_hit.ts new file mode 100644 index 0000000000000..3890973a3f3e4 --- /dev/null +++ b/src/plugins/discover/public/application/helpers/format_hit.ts @@ -0,0 +1,67 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { estypes } from '@elastic/elasticsearch'; +import { DataView, flattenHit } from '../../../../data/common'; +import { MAX_DOC_FIELDS_DISPLAYED } from '../../../common'; +import { getServices } from '../../kibana_services'; +import { formatFieldValue } from './format_value'; + +const formattedHitCache = new WeakMap(); + +type FormattedHit = Array<[fieldName: string, formattedValue: string]>; + +/** + * Returns a formatted document in form of key/value pairs of the fields name and a formatted value. + * The value returned in each pair is an HTML string which is safe to be applied to the DOM, since + * it's formatted using field formatters. + * @param hit The hit to format + * @param dataView The corresponding data view + * @param fieldsToShow A list of fields that should be included in the document summary. + */ +export function formatHit( + hit: estypes.SearchHit, + dataView: DataView, + fieldsToShow: string[] +): FormattedHit { + const cached = formattedHitCache.get(hit); + if (cached) { + return cached; + } + + const highlights = hit.highlight ?? {}; + // Flatten the object using the flattenHit implementation we use across Discover for flattening documents. + const flattened = flattenHit(hit, dataView, { includeIgnoredValues: true, source: true }); + + const highlightPairs: Array<[fieldName: string, formattedValue: string]> = []; + const sourcePairs: Array<[fieldName: string, formattedValue: string]> = []; + + // Add each flattened field into the corresponding array for highlighted or other fields, + // depending on whether the original hit had a highlight for it. That way we can later + // put highlighted fields first in the document summary. + Object.entries(flattened).forEach(([key, val]) => { + // Retrieve the (display) name of the fields, if it's a mapped field on the data view + const displayKey = dataView.fields.getByName(key)?.displayName; + const pairs = highlights[key] ? highlightPairs : sourcePairs; + // Format the raw value using the regular field formatters for that field + const formattedValue = formatFieldValue(val, hit, dataView, dataView.fields.getByName(key)); + // If the field was a mapped field, we validate it against the fieldsToShow list, if not + // we always include it into the result. + if (displayKey) { + if (fieldsToShow.includes(key)) { + pairs.push([displayKey, formattedValue]); + } + } else { + pairs.push([key, formattedValue]); + } + }); + const maxEntries = getServices().uiSettings.get(MAX_DOC_FIELDS_DISPLAYED); + const formatted = [...highlightPairs, ...sourcePairs].slice(0, maxEntries); + formattedHitCache.set(hit, formatted); + return formatted; +} diff --git a/src/plugins/discover/public/application/helpers/format_value.test.ts b/src/plugins/discover/public/application/helpers/format_value.test.ts new file mode 100644 index 0000000000000..76d95c08e4a19 --- /dev/null +++ b/src/plugins/discover/public/application/helpers/format_value.test.ts @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { FieldFormat } from '../../../../field_formats/common'; +import { indexPatternMock } from '../../__mocks__/index_pattern'; +import { formatFieldValue } from './format_value'; + +import { getServices } from '../../kibana_services'; + +jest.mock('../../kibana_services', () => { + const services = { + fieldFormats: { + getDefaultInstance: jest.fn( + () => ({ convert: (value: unknown) => value } as FieldFormat) + ), + }, + }; + return { getServices: () => services }; +}); + +const hit = { + _id: '1', + _index: 'index', + fields: { + message: 'foo', + }, +}; + +describe('formatFieldValue', () => { + afterEach(() => { + (indexPatternMock.getFormatterForField as jest.Mock).mockReset(); + (getServices().fieldFormats.getDefaultInstance as jest.Mock).mockReset(); + }); + + it('should call correct fieldFormatter for field', () => { + const formatterForFieldMock = indexPatternMock.getFormatterForField as jest.Mock; + const convertMock = jest.fn((value: unknown) => `formatted:${value}`); + formatterForFieldMock.mockReturnValue({ convert: convertMock }); + const field = indexPatternMock.fields.getByName('message'); + expect(formatFieldValue('foo', hit, indexPatternMock, field)).toBe('formatted:foo'); + expect(indexPatternMock.getFormatterForField).toHaveBeenCalledWith(field); + expect(convertMock).toHaveBeenCalledWith('foo', 'html', { field, hit }); + }); + + it('should call default string formatter if no field specified', () => { + const convertMock = jest.fn((value: unknown) => `formatted:${value}`); + (getServices().fieldFormats.getDefaultInstance as jest.Mock).mockReturnValue({ + convert: convertMock, + }); + expect(formatFieldValue('foo', hit, indexPatternMock)).toBe('formatted:foo'); + expect(getServices().fieldFormats.getDefaultInstance).toHaveBeenCalledWith('string'); + expect(convertMock).toHaveBeenCalledWith('foo', 'html', { field: undefined, hit }); + }); + + it('should call default string formatter if no indexPattern is specified', () => { + const convertMock = jest.fn((value: unknown) => `formatted:${value}`); + (getServices().fieldFormats.getDefaultInstance as jest.Mock).mockReturnValue({ + convert: convertMock, + }); + expect(formatFieldValue('foo', hit)).toBe('formatted:foo'); + expect(getServices().fieldFormats.getDefaultInstance).toHaveBeenCalledWith('string'); + expect(convertMock).toHaveBeenCalledWith('foo', 'html', { field: undefined, hit }); + }); +}); diff --git a/src/plugins/discover/public/application/helpers/format_value.ts b/src/plugins/discover/public/application/helpers/format_value.ts new file mode 100644 index 0000000000000..cc33276790372 --- /dev/null +++ b/src/plugins/discover/public/application/helpers/format_value.ts @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { estypes } from '@elastic/elasticsearch'; +import { DataView, DataViewField, KBN_FIELD_TYPES } from '../../../../data/common'; +import { getServices } from '../../kibana_services'; + +/** + * Formats the value of a specific field using the appropriate field formatter if available + * or the default string field formatter otherwise. + * + * @param value The value to format + * @param hit The actual search hit (required to get highlight information from) + * @param dataView The data view if available + * @param field The field that value was from if available + * @returns An sanitized HTML string, that is safe to be applied via dangerouslySetInnerHTML + */ +export function formatFieldValue( + value: unknown, + hit: estypes.SearchHit, + dataView?: DataView, + field?: DataViewField +): string { + if (!dataView || !field) { + // If either no field is available or no data view, we'll use the default + // string formatter to format that field. + return getServices() + .fieldFormats.getDefaultInstance(KBN_FIELD_TYPES.STRING) + .convert(value, 'html', { hit, field }); + } + + // If we have a data view and field we use that fields field formatter + return dataView.getFormatterForField(field).convert(value, 'html', { hit, field }); +} diff --git a/src/plugins/discover/public/application/helpers/get_ignored_reason.test.ts b/src/plugins/discover/public/application/helpers/get_ignored_reason.test.ts new file mode 100644 index 0000000000000..13632ca5ed901 --- /dev/null +++ b/src/plugins/discover/public/application/helpers/get_ignored_reason.test.ts @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { getIgnoredReason, IgnoredReason } from './get_ignored_reason'; +import { DataViewField, KBN_FIELD_TYPES } from '../../../../data/common'; + +function field(params: Partial): DataViewField { + return { + name: 'text', + type: 'keyword', + ...params, + } as unknown as DataViewField; +} + +describe('getIgnoredReason', () => { + it('will correctly return undefined when no value was ignored', () => { + expect(getIgnoredReason(field({ name: 'foo' }), undefined)).toBeUndefined(); + expect(getIgnoredReason(field({ name: 'foo' }), ['bar', 'baz'])).toBeUndefined(); + }); + + it('will return UNKNOWN if the field passed in was only a name, and thus no type information is present', () => { + expect(getIgnoredReason('foo', ['foo'])).toBe(IgnoredReason.UNKNOWN); + }); + + it('will return IGNORE_ABOVE for string types', () => { + expect(getIgnoredReason(field({ name: 'foo', type: KBN_FIELD_TYPES.STRING }), ['foo'])).toBe( + IgnoredReason.IGNORE_ABOVE + ); + }); + + // Each type that can have malformed values + [ + KBN_FIELD_TYPES.DATE, + KBN_FIELD_TYPES.IP, + KBN_FIELD_TYPES.GEO_POINT, + KBN_FIELD_TYPES.GEO_SHAPE, + KBN_FIELD_TYPES.NUMBER, + ].forEach((type) => { + it(`will return MALFORMED for ${type} fields`, () => { + expect(getIgnoredReason(field({ name: 'foo', type }), ['foo'])).toBe(IgnoredReason.MALFORMED); + }); + }); + + it('will return unknown reasons if it does not know what the reason was', () => { + expect(getIgnoredReason(field({ name: 'foo', type: 'range' }), ['foo'])).toBe( + IgnoredReason.UNKNOWN + ); + }); +}); diff --git a/src/plugins/discover/public/application/helpers/get_ignored_reason.ts b/src/plugins/discover/public/application/helpers/get_ignored_reason.ts new file mode 100644 index 0000000000000..4d2fb85bdb2c4 --- /dev/null +++ b/src/plugins/discover/public/application/helpers/get_ignored_reason.ts @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { estypes } from '@elastic/elasticsearch'; +import { DataViewField, KBN_FIELD_TYPES } from '../../../../data/common'; + +export enum IgnoredReason { + IGNORE_ABOVE = 'ignore_above', + MALFORMED = 'malformed', + UNKNOWN = 'unknown', +} + +/** + * Returns the reason why a specific field was ignored in the response. + * Will return undefined if the field had no ignored values in it. + * This implementation will make some assumptions based on specific types + * of ignored values can only happen with specific field types in Elasticsearch. + * + * @param field Either the data view field or the string name of it. + * @param ignoredFields The hit._ignored value of the hit to validate. + */ +export function getIgnoredReason( + field: DataViewField | string, + ignoredFields: estypes.SearchHit['_ignored'] +): IgnoredReason | undefined { + const fieldName = typeof field === 'string' ? field : field.name; + if (!ignoredFields?.includes(fieldName)) { + return undefined; + } + + if (typeof field === 'string') { + return IgnoredReason.UNKNOWN; + } + + switch (field.type) { + case KBN_FIELD_TYPES.STRING: + return IgnoredReason.IGNORE_ABOVE; + case KBN_FIELD_TYPES.NUMBER: + case KBN_FIELD_TYPES.DATE: + case KBN_FIELD_TYPES.GEO_POINT: + case KBN_FIELD_TYPES.GEO_SHAPE: + case KBN_FIELD_TYPES.IP: + return IgnoredReason.MALFORMED; + default: + return IgnoredReason.UNKNOWN; + } +} diff --git a/src/plugins/discover/public/build_services.ts b/src/plugins/discover/public/build_services.ts index ab2484abee892..ac16b6b3cc2ba 100644 --- a/src/plugins/discover/public/build_services.ts +++ b/src/plugins/discover/public/build_services.ts @@ -36,6 +36,7 @@ import { KibanaLegacyStart } from '../../kibana_legacy/public'; import { UrlForwardingStart } from '../../url_forwarding/public'; import { NavigationPublicPluginStart } from '../../navigation/public'; import { IndexPatternFieldEditorStart } from '../../index_pattern_field_editor/public'; +import { FieldFormatsStart } from '../../field_formats/public'; import type { SpacesApi } from '../../../../x-pack/plugins/spaces/public'; @@ -49,6 +50,7 @@ export interface DiscoverServices { history: () => History; theme: ChartsPluginStart['theme']; filterManager: FilterManager; + fieldFormats: FieldFormatsStart; indexPatterns: IndexPatternsContract; inspector: InspectorPublicPluginStart; metadata: { branch: string }; @@ -82,6 +84,7 @@ export function buildServices( data: plugins.data, docLinks: core.docLinks, theme: plugins.charts.theme, + fieldFormats: plugins.fieldFormats, filterManager: plugins.data.query.filterManager, history: getHistory, indexPatterns: plugins.data.indexPatterns, diff --git a/src/plugins/discover/public/plugin.tsx b/src/plugins/discover/public/plugin.tsx index 6d30e6fd9e8a9..e170e61f7ebc5 100644 --- a/src/plugins/discover/public/plugin.tsx +++ b/src/plugins/discover/public/plugin.tsx @@ -61,6 +61,7 @@ import { IndexPatternFieldEditorStart } from '../../../plugins/index_pattern_fie import { DeferredSpinner } from './shared'; import { ViewSavedSearchAction } from './application/embeddable/view_saved_search_action'; import type { SpacesPluginStart } from '../../../../x-pack/plugins/spaces/public'; +import { FieldFormatsStart } from '../../field_formats/public'; declare module '../../share/public' { export interface UrlGeneratorStateMapping { @@ -180,6 +181,7 @@ export interface DiscoverStartPlugins { navigation: NavigationStart; charts: ChartsPluginStart; data: DataPublicPluginStart; + fieldFormats: FieldFormatsStart; share?: SharePluginStart; kibanaLegacy: KibanaLegacyStart; urlForwarding: UrlForwardingStart; diff --git a/src/plugins/field_formats/common/converters/source.test.ts b/src/plugins/field_formats/common/converters/source.test.ts index 298c93dac8c4e..6f9e96a136d0b 100644 --- a/src/plugins/field_formats/common/converters/source.test.ts +++ b/src/plugins/field_formats/common/converters/source.test.ts @@ -19,7 +19,7 @@ describe('Source Format', () => { convertHtml = source.getConverterFor(HTML_CONTEXT_TYPE) as HtmlContextTypeConvert; }); - test('should use the text content type if a field is not passed', () => { + test('should render stringified object', () => { const hit = { foo: 'bar', number: 42, @@ -27,23 +27,8 @@ describe('Source Format', () => { also: 'with "quotes" or \'single quotes\'', }; - expect(convertHtml(hit)).toBe( - '{"foo":"bar","number":42,"hello":"<h1>World</h1>","also":"with \\"quotes\\" or 'single quotes'"}' - ); - }); - - test('should render a description list if a field is passed', () => { - const hit = { - foo: 'bar', - number: 42, - hello: '

World

', - also: 'with "quotes" or \'single quotes\'', - }; - - expect( - convertHtml(hit, { field: 'field', indexPattern: { formatHit: (h: string) => h }, hit }) - ).toMatchInlineSnapshot( - `"
foo:
bar
number:
42
hello:

World

also:
with \\"quotes\\" or 'single quotes'
"` + expect(convertHtml(hit, { field: 'field', hit })).toMatchInlineSnapshot( + `"{"foo":"bar","number":42,"hello":"<h1>World</h1>","also":"with \\\\"quotes\\\\" or 'single quotes'"}"` ); }); }); diff --git a/src/plugins/field_formats/common/converters/source.tsx b/src/plugins/field_formats/common/converters/source.tsx index 1caffb5bfb9a8..f92027ec07451 100644 --- a/src/plugins/field_formats/common/converters/source.tsx +++ b/src/plugins/field_formats/common/converters/source.tsx @@ -7,33 +7,8 @@ */ import { KBN_FIELD_TYPES } from '@kbn/field-types'; -import React, { Fragment } from 'react'; -import ReactDOM from 'react-dom/server'; -import { escape, keys } from 'lodash'; -import { shortenDottedString } from '../utils'; import { FieldFormat } from '../field_format'; -import { TextContextTypeConvert, HtmlContextTypeConvert, FIELD_FORMAT_IDS } from '../types'; -import { FORMATS_UI_SETTINGS } from '../constants/ui_settings'; - -interface Props { - defPairs: Array<[string, string]>; -} -const TemplateComponent = ({ defPairs }: Props) => { - return ( -
- {defPairs.map((pair, idx) => ( - -
-
{' '} - - ))} -
- ); -}; +import { TextContextTypeConvert, FIELD_FORMAT_IDS } from '../types'; /** @public */ export class SourceFormat extends FieldFormat { @@ -42,32 +17,4 @@ export class SourceFormat extends FieldFormat { static fieldType = KBN_FIELD_TYPES._SOURCE; textConvert: TextContextTypeConvert = (value: string) => JSON.stringify(value); - - htmlConvert: HtmlContextTypeConvert = (value: string, options = {}) => { - const { field, hit, indexPattern } = options; - - if (!field) { - const converter = this.getConverterFor('text') as Function; - - return escape(converter(value)); - } - - const highlights: Record = (hit && hit.highlight) || {}; - // TODO: remove index pattern dependency - const formatted = hit ? indexPattern!.formatHit(hit) : {}; - const highlightPairs: Array<[string, string]> = []; - const sourcePairs: Array<[string, string]> = []; - const isShortDots = this.getConfig!(FORMATS_UI_SETTINGS.SHORT_DOTS_ENABLE); - - keys(formatted).forEach((key) => { - const pairs = highlights[key] ? highlightPairs : sourcePairs; - const newField = isShortDots ? shortenDottedString(key) : key; - const val = formatted![key]; - pairs.push([newField as string, val]); - }, []); - - return ReactDOM.renderToStaticMarkup( - - ); - }; } diff --git a/src/plugins/field_formats/common/types.ts b/src/plugins/field_formats/common/types.ts index 00f9f5d707e89..6f0efebe389a1 100644 --- a/src/plugins/field_formats/common/types.ts +++ b/src/plugins/field_formats/common/types.ts @@ -17,10 +17,6 @@ export type FieldFormatsContentType = 'html' | 'text'; */ export interface HtmlContextTypeOptions { field?: { name: string }; - // TODO: get rid of indexPattern dep completely - indexPattern?: { - formatHit: (hit: { highlight: Record }) => Record; - }; hit?: { highlight: Record }; } diff --git a/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.ts b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.ts index 6c2989d54309d..77ad4fba1ab60 100644 --- a/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.ts +++ b/x-pack/plugins/reporting/server/export_types/csv_searchsource/generate_csv/generate_csv.ts @@ -356,7 +356,7 @@ export class CsvGenerator { let table: Datatable | undefined; try { - table = tabifyDocs(results, index, { shallow: true }); + table = tabifyDocs(results, index, { shallow: true, includeIgnoredValues: true }); } catch (err) { this.logger.error(err); } diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index aac0f651b8dee..64258d42a4cc1 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -2424,7 +2424,6 @@ "discover.docViews.table.toggleFieldDetails": "フィールド詳細を切り替える", "discover.docViews.table.unableToFilterForPresenceOfMetaFieldsTooltip": "メタフィールドの有無でフィルタリングできません", "discover.docViews.table.unableToFilterForPresenceOfScriptedFieldsTooltip": "スクリプトフィールドの有無でフィルタリングできません", - "discover.docViews.table.unindexedFieldsCanNotBeSearchedTooltip": "インデックスされていないフィールドは検索できません", "discover.embeddable.inspectorRequestDataTitle": "データ", "discover.embeddable.inspectorRequestDescription": "このリクエストはElasticsearchにクエリをかけ、検索データを取得します。", "discover.embeddable.search.displayName": "検索", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 50d90f5144585..ecc27bbe9dea8 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -2449,7 +2449,6 @@ "discover.docViews.table.toggleFieldDetails": "切换字段详细信息", "discover.docViews.table.unableToFilterForPresenceOfMetaFieldsTooltip": "无法筛选元数据字段是否存在", "discover.docViews.table.unableToFilterForPresenceOfScriptedFieldsTooltip": "无法筛选脚本字段是否存在", - "discover.docViews.table.unindexedFieldsCanNotBeSearchedTooltip": "无法搜索未索引字段", "discover.embeddable.inspectorRequestDataTitle": "数据", "discover.embeddable.inspectorRequestDescription": "此请求将查询 Elasticsearch 以获取搜索的数据。", "discover.embeddable.search.displayName": "搜索", From 96c89e0fcab42366078c35ecadbc31681a8bf834 Mon Sep 17 00:00:00 2001 From: Josh Dover <1813008+joshdover@users.noreply.github.com> Date: Tue, 19 Oct 2021 16:43:51 +0200 Subject: [PATCH 060/204] [Unified Integrations] Remove and cleanup add data views (#115424) Co-authored-by: cchaos Co-authored-by: Dave Snider Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Thomas Neirynck --- .../__snapshots__/home.test.tsx.snap | 422 +++++------------- .../application/components/home.test.tsx | 26 +- .../public/application/components/home.tsx | 8 +- .../public/application/components/home_app.js | 12 +- .../components/tutorial/tutorial.js | 13 +- .../components/tutorial_directory.js | 24 +- .../elastic_agent_card.test.tsx.snap | 246 ++++++---- .../no_data_card/elastic_agent_card.tsx | 39 +- .../apps/home/{_add_data.js => _add_data.ts} | 13 +- .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - 11 files changed, 356 insertions(+), 449 deletions(-) rename test/functional/apps/home/{_add_data.js => _add_data.ts} (59%) diff --git a/src/plugins/home/public/application/components/__snapshots__/home.test.tsx.snap b/src/plugins/home/public/application/components/__snapshots__/home.test.tsx.snap index b6679dd7ba493..f38bdb9ac53f0 100644 --- a/src/plugins/home/public/application/components/__snapshots__/home.test.tsx.snap +++ b/src/plugins/home/public/application/components/__snapshots__/home.test.tsx.snap @@ -21,10 +21,28 @@ exports[`home change home route should render a link to change the default route /> - - - -`; - -exports[`home welcome should show the normal home page if loading fails 1`] = ` -, - } - } - template="empty" -> - - - - - -`; - -exports[`home welcome should show the normal home page if welcome screen is disabled locally 1`] = ` -, + application={ + Object { + "capabilities": Object { + "navLinks": Object { + "integrations": true, + }, + }, + } } - } - template="empty" -> - - - - -`; - -exports[`home welcome should show the welcome screen if enabled, and there are no index patterns defined 1`] = ` - -`; - -exports[`home welcome stores skip welcome setting if skipped 1`] = ` -, - } - } - template="empty" -> - - - ({ getServices: () => ({ getBasePath: () => 'path', @@ -22,6 +24,13 @@ jest.mock('../kibana_services', () => ({ chrome: { setBreadcrumbs: () => {}, }, + application: { + capabilities: { + navLinks: { + integrations: mockHasIntegrationsPermission, + }, + }, + }, }), })); @@ -35,6 +44,7 @@ describe('home', () => { let defaultProps: HomeProps; beforeEach(() => { + mockHasIntegrationsPermission = true; defaultProps = { directories: [], solutions: [], @@ -182,7 +192,7 @@ describe('home', () => { expect(defaultProps.localStorage.getItem).toHaveBeenCalledTimes(1); - expect(component).toMatchSnapshot(); + expect(component.find(Welcome).exists()).toBe(true); }); test('stores skip welcome setting if skipped', async () => { @@ -196,7 +206,7 @@ describe('home', () => { expect(defaultProps.localStorage.setItem).toHaveBeenCalledWith('home:welcome:show', 'false'); - expect(component).toMatchSnapshot(); + expect(component.find(Welcome).exists()).toBe(false); }); test('should show the normal home page if loading fails', async () => { @@ -205,7 +215,7 @@ describe('home', () => { const hasUserIndexPattern = jest.fn(() => Promise.reject('Doh!')); const component = await renderHome({ hasUserIndexPattern }); - expect(component).toMatchSnapshot(); + expect(component.find(Welcome).exists()).toBe(false); }); test('should show the normal home page if welcome screen is disabled locally', async () => { @@ -213,7 +223,15 @@ describe('home', () => { const component = await renderHome(); - expect(component).toMatchSnapshot(); + expect(component.find(Welcome).exists()).toBe(false); + }); + + test("should show the normal home page if user doesn't have access to integrations", async () => { + mockHasIntegrationsPermission = false; + + const component = await renderHome(); + + expect(component.find(Welcome).exists()).toBe(false); }); }); diff --git a/src/plugins/home/public/application/components/home.tsx b/src/plugins/home/public/application/components/home.tsx index d398311d30255..2a08754889c28 100644 --- a/src/plugins/home/public/application/components/home.tsx +++ b/src/plugins/home/public/application/components/home.tsx @@ -45,10 +45,10 @@ export class Home extends Component { constructor(props: HomeProps) { super(props); - const isWelcomeEnabled = !( - getServices().homeConfig.disableWelcomeScreen || - props.localStorage.getItem(KEY_ENABLE_WELCOME) === 'false' - ); + const isWelcomeEnabled = + !getServices().homeConfig.disableWelcomeScreen && + getServices().application.capabilities.navLinks.integrations && + props.localStorage.getItem(KEY_ENABLE_WELCOME) !== 'false'; const body = document.querySelector('body')!; body.classList.add('isHomPage'); diff --git a/src/plugins/home/public/application/components/home_app.js b/src/plugins/home/public/application/components/home_app.js index b0ba4d46646d0..1dbcaa6f50fa1 100644 --- a/src/plugins/home/public/application/components/home_app.js +++ b/src/plugins/home/public/application/components/home_app.js @@ -17,8 +17,11 @@ import { getTutorial } from '../load_tutorials'; import { replaceTemplateStrings } from './tutorial/replace_template_strings'; import { getServices } from '../kibana_services'; +const REDIRECT_TO_INTEGRATIONS_TAB_IDS = ['all', 'logging', 'metrics', 'security']; + export function HomeApp({ directories, solutions }) { const { + application, savedObjectsClient, getBasePath, addBasePath, @@ -30,10 +33,17 @@ export function HomeApp({ directories, solutions }) { const isCloudEnabled = environment.cloud; const renderTutorialDirectory = (props) => { + // Redirect to integrations app unless a specific tab that is still supported was specified. + const tabId = props.match.params.tab; + if (!tabId || REDIRECT_TO_INTEGRATIONS_TAB_IDS.includes(tabId)) { + application.navigateToApp('integrations', { replace: true }); + return null; + } + return ( ); diff --git a/src/plugins/home/public/application/components/tutorial/tutorial.js b/src/plugins/home/public/application/components/tutorial/tutorial.js index 508a236bf45d4..4af5e362baca9 100644 --- a/src/plugins/home/public/application/components/tutorial/tutorial.js +++ b/src/plugins/home/public/application/components/tutorial/tutorial.js @@ -26,9 +26,8 @@ const INSTRUCTIONS_TYPE = { ON_PREM_ELASTIC_CLOUD: 'onPremElasticCloud', }; -const homeTitle = i18n.translate('home.breadcrumbs.homeTitle', { defaultMessage: 'Home' }); -const addDataTitle = i18n.translate('home.breadcrumbs.addDataTitle', { - defaultMessage: 'Add data', +const integrationsTitle = i18n.translate('home.breadcrumbs.integrationsAppTitle', { + defaultMessage: 'Integrations', }); class TutorialUi extends React.Component { @@ -80,12 +79,8 @@ class TutorialUi extends React.Component { getServices().chrome.setBreadcrumbs([ { - text: homeTitle, - href: '#/', - }, - { - text: addDataTitle, - href: '#/tutorial_directory', + text: integrationsTitle, + href: this.props.addBasePath('/app/integrations/browse'), }, { text: tutorial ? tutorial.name : this.props.tutorialId, diff --git a/src/plugins/home/public/application/components/tutorial_directory.js b/src/plugins/home/public/application/components/tutorial_directory.js index 83e629a7c891e..ac0d1524145a1 100644 --- a/src/plugins/home/public/application/components/tutorial_directory.js +++ b/src/plugins/home/public/application/components/tutorial_directory.js @@ -18,12 +18,10 @@ import { getServices } from '../kibana_services'; import { KibanaPageTemplate } from '../../../../kibana_react/public'; import { getTutorials } from '../load_tutorials'; -const ALL_TAB_ID = 'all'; const SAMPLE_DATA_TAB_ID = 'sampleData'; -const homeTitle = i18n.translate('home.breadcrumbs.homeTitle', { defaultMessage: 'Home' }); -const addDataTitle = i18n.translate('home.breadcrumbs.addDataTitle', { - defaultMessage: 'Add data', +const integrationsTitle = i18n.translate('home.breadcrumbs.integrationsAppTitle', { + defaultMessage: 'Integrations', }); class TutorialDirectoryUi extends React.Component { @@ -48,7 +46,7 @@ class TutorialDirectoryUi extends React.Component { })), ]; - let openTab = ALL_TAB_ID; + let openTab = SAMPLE_DATA_TAB_ID; if ( props.openTab && this.tabs.some((tab) => { @@ -72,10 +70,9 @@ class TutorialDirectoryUi extends React.Component { getServices().chrome.setBreadcrumbs([ { - text: homeTitle, - href: '#/', + text: integrationsTitle, + href: this.props.addBasePath(`/app/integrations/browse`), }, - { text: addDataTitle }, ]); const tutorialConfigs = await getTutorials(); @@ -155,6 +152,15 @@ class TutorialDirectoryUi extends React.Component { renderTabContent = () => { const tab = this.tabs.find(({ id }) => id === this.state.selectedTabId); if (tab?.content) { + getServices().chrome.setBreadcrumbs([ + { + text: integrationsTitle, + href: this.props.addBasePath(`/app/integrations/browse`), + }, + { + text: tab.name, + }, + ]); return tab.content; } @@ -163,7 +169,7 @@ class TutorialDirectoryUi extends React.Component { {this.state.tutorialCards .filter((tutorial) => { return ( - this.state.selectedTabId === ALL_TAB_ID || + this.state.selectedTabId === SAMPLE_DATA_TAB_ID || this.state.selectedTabId === tutorial.category ); }) diff --git a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/__snapshots__/elastic_agent_card.test.tsx.snap b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/__snapshots__/elastic_agent_card.test.tsx.snap index f66d05140b2e9..8e1d0cb92e006 100644 --- a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/__snapshots__/elastic_agent_card.test.tsx.snap +++ b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/__snapshots__/elastic_agent_card.test.tsx.snap @@ -1,117 +1,177 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`ElasticAgentCard props button 1`] = ` - - Button - + - - Add Elastic Agent - - - } -/> +> + + Button + + } + href="/app/integrations/browse" + image="/plugins/kibanaReact/assets/elastic_agent_card.svg" + paddingSize="l" + title={ + + + Add Elastic Agent + + + } + /> + `; exports[`ElasticAgentCard props category 1`] = ` - - Add Elastic Agent - + - +> + Add Elastic Agent - - - } -/> + + } + href="/app/integrations/browse/custom" + image="/plugins/kibanaReact/assets/elastic_agent_card.svg" + paddingSize="l" + title={ + + + Add Elastic Agent + + + } + /> + `; exports[`ElasticAgentCard props href 1`] = ` - - Button - + - - Add Elastic Agent - - - } -/> +> + + Button + + } + href="#" + image="/plugins/kibanaReact/assets/elastic_agent_card.svg" + paddingSize="l" + title={ + + + Add Elastic Agent + + + } + /> + `; exports[`ElasticAgentCard props recommended 1`] = ` - - Add Elastic Agent - + - +> + Add Elastic Agent - - - } -/> + + } + href="/app/integrations/browse" + image="/plugins/kibanaReact/assets/elastic_agent_card.svg" + paddingSize="l" + title={ + + + Add Elastic Agent + + + } + /> + `; exports[`ElasticAgentCard renders 1`] = ` - - Add Elastic Agent - + - +> + Add Elastic Agent - - - } -/> + + } + href="/app/integrations/browse" + image="/plugins/kibanaReact/assets/elastic_agent_card.svg" + paddingSize="l" + title={ + + + Add Elastic Agent + + + } + /> + `; diff --git a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_agent_card.tsx b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_agent_card.tsx index 5a91e568471d1..b9d412fe4df89 100644 --- a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_agent_card.tsx +++ b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_agent_card.tsx @@ -12,6 +12,7 @@ import { CoreStart } from 'kibana/public'; import { EuiButton, EuiCard, EuiTextColor, EuiScreenReaderOnly } from '@elastic/eui'; import { useKibana } from '../../../context'; import { NoDataPageActions, NO_DATA_RECOMMENDED } from '../no_data_page'; +import { RedirectAppLinks } from '../../../app_links'; export type ElasticAgentCardProps = NoDataPageActions & { solution: string; @@ -76,23 +77,25 @@ export const ElasticAgentCard: FunctionComponent = ({ ); return ( - - {defaultCTAtitle} - - } - description={i18n.translate('kibana-react.noDataPage.elasticAgentCard.description', { - defaultMessage: `Use Elastic Agent for a simple, unified way to collect data from your machines.`, - })} - betaBadgeLabel={recommended ? NO_DATA_RECOMMENDED : undefined} - footer={footer} - layout={layout as 'vertical' | undefined} - {...cardRest} - /> + + + {defaultCTAtitle} + + } + description={i18n.translate('kibana-react.noDataPage.elasticAgentCard.description', { + defaultMessage: `Use Elastic Agent for a simple, unified way to collect data from your machines.`, + })} + betaBadgeLabel={recommended ? NO_DATA_RECOMMENDED : undefined} + footer={footer} + layout={layout as 'vertical' | undefined} + {...cardRest} + /> + ); }; diff --git a/test/functional/apps/home/_add_data.js b/test/functional/apps/home/_add_data.ts similarity index 59% rename from test/functional/apps/home/_add_data.js rename to test/functional/apps/home/_add_data.ts index c69e0a02c26e4..3fd69c1a488f4 100644 --- a/test/functional/apps/home/_add_data.js +++ b/test/functional/apps/home/_add_data.ts @@ -6,20 +6,15 @@ * Side Public License, v 1. */ -import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; -export default function ({ getService, getPageObjects }) { - const retry = getService('retry'); +export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['common', 'header', 'home', 'dashboard']); describe('add data tutorials', function describeIndexTests() { - it('directory should display registered tutorials', async () => { + it('directory should redirect to integrations app', async () => { await PageObjects.common.navigateToUrl('home', 'tutorial_directory', { useActualUrl: true }); - await PageObjects.header.waitUntilLoadingHasFinished(); - await retry.try(async () => { - const tutorialExists = await PageObjects.home.doesSynopsisExist('netflowlogs'); - expect(tutorialExists).to.be(true); - }); + await PageObjects.common.waitUntilUrlIncludes('/app/integrations'); }); }); } diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 64258d42a4cc1..f831f3b91eaa5 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -2914,7 +2914,6 @@ "home.addData.sampleDataButtonLabel": "サンプルデータを試す", "home.addData.sectionTitle": "データを追加して開始する", "home.addData.text": "データの操作を開始するには、多数の取り込みオプションのいずれかを使用します。アプリまたはサービスからデータを収集するか、ファイルをアップロードします。独自のデータを使用する準備ができていない場合は、サンプルデータセットを追加してください。", - "home.breadcrumbs.addDataTitle": "データの追加", "home.breadcrumbs.homeTitle": "ホーム", "home.dataManagementDisableCollection": " 収集を停止するには、", "home.dataManagementDisableCollectionLink": "ここで使用状況データを無効にします。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index ecc27bbe9dea8..5fbcd26340be3 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -2943,7 +2943,6 @@ "home.addData.sampleDataButtonLabel": "试用样例数据", "home.addData.sectionTitle": "首先添加您的数据", "home.addData.text": "要开始使用您的数据,请使用我们众多采集选项中的一个选项。从应用或服务收集数据,或上传文件。如果未准备好使用自己的数据,请添加示例数据集。", - "home.breadcrumbs.addDataTitle": "添加数据", "home.breadcrumbs.homeTitle": "主页", "home.dataManagementDisableCollection": " 要停止收集,", "home.dataManagementDisableCollectionLink": "请在此禁用使用情况数据。", From e4fb118fee9302d65e696c40adc3c44ff44c5a27 Mon Sep 17 00:00:00 2001 From: Joe Portner <5295965+jportner@users.noreply.github.com> Date: Tue, 19 Oct 2021 10:44:15 -0400 Subject: [PATCH 061/204] Change deleteByNamespace to include legacy URL aliases (#115459) --- .../object_types/registration.ts | 1 + .../service/lib/repository.test.js | 7 ++- .../saved_objects/service/lib/repository.ts | 16 +++++- .../common/lib/space_test_utils.ts | 2 + .../common/suites/delete.ts | 53 +++++++++---------- 5 files changed, 47 insertions(+), 32 deletions(-) diff --git a/src/core/server/saved_objects/object_types/registration.ts b/src/core/server/saved_objects/object_types/registration.ts index 6ef4f79ef77c9..ce10896747178 100644 --- a/src/core/server/saved_objects/object_types/registration.ts +++ b/src/core/server/saved_objects/object_types/registration.ts @@ -17,6 +17,7 @@ const legacyUrlAliasType: SavedObjectsType = { properties: { sourceId: { type: 'keyword' }, targetType: { type: 'keyword' }, + targetNamespace: { type: 'keyword' }, resolveCounter: { type: 'long' }, disabled: { type: 'boolean' }, // other properties exist, but we aren't querying or aggregating on those, so we don't need to specify them (because we use `dynamic: false` above) diff --git a/src/core/server/saved_objects/service/lib/repository.test.js b/src/core/server/saved_objects/service/lib/repository.test.js index 82a0dd71700f6..84359147fccbc 100644 --- a/src/core/server/saved_objects/service/lib/repository.test.js +++ b/src/core/server/saved_objects/service/lib/repository.test.js @@ -26,6 +26,7 @@ import { encodeHitVersion } from '../../version'; import { SavedObjectTypeRegistry } from '../../saved_objects_type_registry'; import { DocumentMigrator } from '../../migrations/core/document_migrator'; import { mockKibanaMigrator } from '../../migrations/kibana/kibana_migrator.mock'; +import { LEGACY_URL_ALIAS_TYPE } from '../../object_types'; import { elasticsearchClientMock } from '../../../elasticsearch/client/mocks'; import * as esKuery from '@kbn/es-query'; import { errors as EsErrors } from '@elastic/elasticsearch'; @@ -2714,7 +2715,11 @@ describe('SavedObjectsRepository', () => { const allTypes = registry.getAllTypes().map((type) => type.name); expect(getSearchDslNS.getSearchDsl).toHaveBeenCalledWith(mappings, registry, { namespaces: [namespace], - type: allTypes.filter((type) => !registry.isNamespaceAgnostic(type)), + type: [ + ...allTypes.filter((type) => !registry.isNamespaceAgnostic(type)), + LEGACY_URL_ALIAS_TYPE, + ], + kueryNode: expect.anything(), }); }); }); diff --git a/src/core/server/saved_objects/service/lib/repository.ts b/src/core/server/saved_objects/service/lib/repository.ts index e522d770b3f58..c081c59911405 100644 --- a/src/core/server/saved_objects/service/lib/repository.ts +++ b/src/core/server/saved_objects/service/lib/repository.ts @@ -8,6 +8,7 @@ import { omit, isObject } from 'lodash'; import type { estypes } from '@elastic/elasticsearch'; +import * as esKuery from '@kbn/es-query'; import type { ElasticsearchClient } from '../../../elasticsearch/'; import { isSupportedEsServer, isNotFoundFromUnsupportedServer } from '../../../elasticsearch'; import type { Logger } from '../../../logging'; @@ -55,6 +56,7 @@ import { SavedObjectsBulkResolveObject, SavedObjectsBulkResolveResponse, } from '../saved_objects_client'; +import { LEGACY_URL_ALIAS_TYPE } from '../../object_types'; import { SavedObject, SavedObjectsBaseOptions, @@ -780,7 +782,16 @@ export class SavedObjectsRepository { } const allTypes = Object.keys(getRootPropertiesObjects(this._mappings)); - const typesToUpdate = allTypes.filter((type) => !this._registry.isNamespaceAgnostic(type)); + const typesToUpdate = [ + ...allTypes.filter((type) => !this._registry.isNamespaceAgnostic(type)), + LEGACY_URL_ALIAS_TYPE, + ]; + + // Construct kueryNode to filter legacy URL aliases (these space-agnostic objects do not use root-level "namespace/s" fields) + const { buildNode } = esKuery.nodeTypes.function; + const match1 = buildNode('is', `${LEGACY_URL_ALIAS_TYPE}.targetNamespace`, namespace); + const match2 = buildNode('not', buildNode('is', 'type', LEGACY_URL_ALIAS_TYPE)); + const kueryNode = buildNode('or', [match1, match2]); const { body, statusCode, headers } = await this.client.updateByQuery( { @@ -803,8 +814,9 @@ export class SavedObjectsRepository { }, conflicts: 'proceed', ...getSearchDsl(this._mappings, this._registry, { - namespaces: namespace ? [namespace] : undefined, + namespaces: [namespace], type: typesToUpdate, + kueryNode, }), }, }, diff --git a/x-pack/test/spaces_api_integration/common/lib/space_test_utils.ts b/x-pack/test/spaces_api_integration/common/lib/space_test_utils.ts index 28b19d5db20b6..c047a741e35da 100644 --- a/x-pack/test/spaces_api_integration/common/lib/space_test_utils.ts +++ b/x-pack/test/spaces_api_integration/common/lib/space_test_utils.ts @@ -50,6 +50,8 @@ export function getAggregatedSpaceData(es: KibanaClient, objectTypes: string[]) emit(doc["namespaces"].value); } else if (doc["namespace"].size() > 0) { emit(doc["namespace"].value); + } else if (doc["legacy-url-alias.targetNamespace"].size() > 0) { + emit(doc["legacy-url-alias.targetNamespace"].value); } `, }, diff --git a/x-pack/test/spaces_api_integration/common/suites/delete.ts b/x-pack/test/spaces_api_integration/common/suites/delete.ts index aaca4fa843d67..4bf44d88db8e0 100644 --- a/x-pack/test/spaces_api_integration/common/suites/delete.ts +++ b/x-pack/test/spaces_api_integration/common/suites/delete.ts @@ -48,6 +48,7 @@ export function deleteTestSuiteFactory( 'dashboard', 'space', 'index-pattern', + 'legacy-url-alias', // TODO: add assertions for config objects -- these assertions were removed because of flaky behavior in #92358, but we should // consider adding them again at some point, especially if we convert config objects to `namespaceType: 'multiple-isolated'` in // the future. @@ -56,6 +57,10 @@ export function deleteTestSuiteFactory( // @ts-expect-error @elastic/elasticsearch doesn't defined `count.buckets`. const buckets = response.aggregations?.count.buckets; + // The test fixture contains three legacy URL aliases: + // (1) one for "space_1", (2) one for "space_2", and (3) one for "other_space", which is a non-existent space. + // Each test deletes "space_2", so the agg buckets should reflect that aliases (1) and (3) still exist afterwards. + // Space 2 deleted, all others should exist const expectedBuckets = [ { @@ -65,47 +70,37 @@ export function deleteTestSuiteFactory( doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [ - { - key: 'visualization', - doc_count: 3, - }, - { - key: 'dashboard', - doc_count: 2, - }, - { - key: 'space', - doc_count: 2, - }, - { - key: 'index-pattern', - doc_count: 1, - }, + { key: 'visualization', doc_count: 3 }, + { key: 'dashboard', doc_count: 2 }, + { key: 'space', doc_count: 2 }, // since space objects are namespace-agnostic, they appear in the "default" agg bucket + { key: 'index-pattern', doc_count: 1 }, + // legacy-url-alias objects cannot exist for the default space ], }, }, { - doc_count: 6, + doc_count: 7, key: 'space_1', countByType: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [ - { - key: 'visualization', - doc_count: 3, - }, - { - key: 'dashboard', - doc_count: 2, - }, - { - key: 'index-pattern', - doc_count: 1, - }, + { key: 'visualization', doc_count: 3 }, + { key: 'dashboard', doc_count: 2 }, + { key: 'index-pattern', doc_count: 1 }, + { key: 'legacy-url-alias', doc_count: 1 }, // alias (1) ], }, }, + { + doc_count: 1, + key: 'other_space', + countByType: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [{ key: 'legacy-url-alias', doc_count: 1 }], // alias (3) + }, + }, ]; expect(buckets).to.eql(expectedBuckets); From 92e1cd25b7fd8145689cb68d3b21a70f80c6b504 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Tue, 19 Oct 2021 15:51:30 +0100 Subject: [PATCH 062/204] [ML] Adding ability to change data view in advanced job wizard (#115191) * [ML] Adding ability to change data view in advanced job wizard * updating translation ids * type and text changes * code clean up * route id change * text changes * text change * changing data view to index pattern * adding api tests * text updates * removing first step * renaming temp variable * adding permission checks Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../plugins/ml/common/types/job_validation.ts | 14 + .../plugins/ml/common/types/saved_objects.ts | 2 +- .../contexts/kibana/use_navigate_to_path.ts | 2 +- .../new_job/common/job_creator/job_creator.ts | 4 + .../common/job_creator/util/general.ts | 8 +- .../json_editor_flyout/json_editor_flyout.tsx | 2 +- .../components/data_view/change_data_view.tsx | 326 ++++++++++++++++++ .../data_view/change_data_view_button.tsx | 36 ++ .../components/data_view/description.tsx | 32 ++ .../components/data_view/index.ts | 8 + .../components/datafeed_step/datafeed.tsx | 2 + .../services/ml_api_service/index.ts | 12 +- .../ml/server/models/job_validation/index.ts | 4 + .../models/job_validation/job_validation.ts | 6 +- .../validate_datafeed_preview.ts | 29 +- x-pack/plugins/ml/server/routes/apidoc.json | 2 + .../ml/server/routes/job_validation.ts | 44 ++- .../routes/schemas/job_validation_schema.ts | 7 + .../apis/ml/data_frame_analytics/delete.ts | 4 +- .../datafeed_preview_validation.ts | 175 ++++++++++ .../apis/ml/job_validation/index.ts | 1 + x-pack/test/functional/services/ml/api.ts | 10 +- 22 files changed, 711 insertions(+), 19 deletions(-) create mode 100644 x-pack/plugins/ml/common/types/job_validation.ts create mode 100644 x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/components/data_view/change_data_view.tsx create mode 100644 x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/components/data_view/change_data_view_button.tsx create mode 100644 x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/components/data_view/description.tsx create mode 100644 x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/components/data_view/index.ts create mode 100644 x-pack/test/api_integration/apis/ml/job_validation/datafeed_preview_validation.ts diff --git a/x-pack/plugins/ml/common/types/job_validation.ts b/x-pack/plugins/ml/common/types/job_validation.ts new file mode 100644 index 0000000000000..0c1db63ff3762 --- /dev/null +++ b/x-pack/plugins/ml/common/types/job_validation.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { ErrorType } from '../util/errors'; + +export interface DatafeedValidationResponse { + valid: boolean; + documentsFound: boolean; + error?: ErrorType; +} diff --git a/x-pack/plugins/ml/common/types/saved_objects.ts b/x-pack/plugins/ml/common/types/saved_objects.ts index 0e48800dd845d..e376fddbe6272 100644 --- a/x-pack/plugins/ml/common/types/saved_objects.ts +++ b/x-pack/plugins/ml/common/types/saved_objects.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { ErrorType } from '../util/errors'; +import type { ErrorType } from '../util/errors'; export type JobType = 'anomaly-detector' | 'data-frame-analytics'; export const ML_SAVED_OBJECT_TYPE = 'ml-job'; export const ML_MODULE_SAVED_OBJECT_TYPE = 'ml-module'; diff --git a/x-pack/plugins/ml/public/application/contexts/kibana/use_navigate_to_path.ts b/x-pack/plugins/ml/public/application/contexts/kibana/use_navigate_to_path.ts index 951d9d6dfded9..00050803b97c6 100644 --- a/x-pack/plugins/ml/public/application/contexts/kibana/use_navigate_to_path.ts +++ b/x-pack/plugins/ml/public/application/contexts/kibana/use_navigate_to_path.ts @@ -23,7 +23,7 @@ export const useNavigateToPath = () => { const location = useLocation(); return useCallback( - async (path: string | undefined, preserveSearch = false) => { + async (path: string | undefined, preserveSearch: boolean = false) => { if (path === undefined) return; const modifiedPath = `${path}${preserveSearch === true ? location.search : ''}`; /** diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/job_creator.ts b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/job_creator.ts index a44b4bdef60c4..607a4fcf9a73c 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/job_creator.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/job_creator.ts @@ -502,6 +502,10 @@ export class JobCreator { return this._datafeed_config.indices; } + public set indices(indics: string[]) { + this._datafeed_config.indices = indics; + } + public get scriptFields(): Field[] { return this._scriptFields; } diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/general.ts b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/general.ts index 78903e64686f5..46315ac3b02d8 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/general.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/general.ts @@ -258,17 +258,21 @@ export function convertToMultiMetricJob( jobCreator.createdBy = CREATED_BY_LABEL.MULTI_METRIC; jobCreator.modelPlot = false; stashJobForCloning(jobCreator, true, true); - navigateToPath(`jobs/new_job/${JOB_TYPE.MULTI_METRIC}`, true); } export function convertToAdvancedJob(jobCreator: JobCreatorType, navigateToPath: NavigateToPath) { jobCreator.createdBy = null; stashJobForCloning(jobCreator, true, true); - navigateToPath(`jobs/new_job/${JOB_TYPE.ADVANCED}`, true); } +export function resetAdvancedJob(jobCreator: JobCreatorType, navigateToPath: NavigateToPath) { + jobCreator.createdBy = null; + stashJobForCloning(jobCreator, true, false); + navigateToPath('/jobs/new_job'); +} + export function resetJob(jobCreator: JobCreatorType, navigateToPath: NavigateToPath) { jobCreator.jobId = ''; stashJobForCloning(jobCreator, true, true); diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/common/json_editor_flyout/json_editor_flyout.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/common/json_editor_flyout/json_editor_flyout.tsx index ce71cd80e45c0..9e5d1ac5eef6f 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/common/json_editor_flyout/json_editor_flyout.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/common/json_editor_flyout/json_editor_flyout.tsx @@ -204,7 +204,7 @@ export const JsonEditorFlyout: FC = ({ isDisabled, jobEditorMode, datafee > diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/components/data_view/change_data_view.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/components/data_view/change_data_view.tsx new file mode 100644 index 0000000000000..c402ee4bf9799 --- /dev/null +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/components/data_view/change_data_view.tsx @@ -0,0 +1,326 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC, useState, useEffect, useCallback, useContext } from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; + +import { i18n } from '@kbn/i18n'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiButtonEmpty, + EuiModal, + EuiButton, + EuiCallOut, + EuiSpacer, + EuiModalHeader, + EuiLoadingSpinner, + EuiModalHeaderTitle, + EuiModalBody, +} from '@elastic/eui'; + +import { JobCreatorContext } from '../../../job_creator_context'; +import { AdvancedJobCreator } from '../../../../../common/job_creator'; +import { resetAdvancedJob } from '../../../../../common/job_creator/util/general'; +import { + CombinedJob, + Datafeed, +} from '../../../../../../../../../common/types/anomaly_detection_jobs'; +import { extractErrorMessage } from '../../../../../../../../../common/util/errors'; +import type { DatafeedValidationResponse } from '../../../../../../../../../common/types/job_validation'; + +import { SavedObjectFinderUi } from '../../../../../../../../../../../../src/plugins/saved_objects/public'; +import { + useMlKibana, + useMlApiContext, + useNavigateToPath, +} from '../../../../../../../contexts/kibana'; + +const fixedPageSize: number = 8; + +enum STEP { + PICK_DATA_VIEW, + VALIDATE, +} + +interface Props { + onClose: () => void; +} + +export const ChangeDataViewModal: FC = ({ onClose }) => { + const { + services: { + savedObjects, + uiSettings, + data: { dataViews }, + }, + } = useMlKibana(); + const navigateToPath = useNavigateToPath(); + const { validateDatafeedPreview } = useMlApiContext(); + + const { jobCreator: jc } = useContext(JobCreatorContext); + const jobCreator = jc as AdvancedJobCreator; + + const [validating, setValidating] = useState(false); + const [step, setStep] = useState(STEP.PICK_DATA_VIEW); + + const [currentDataViewTitle, setCurrentDataViewTitle] = useState(''); + const [newDataViewTitle, setNewDataViewTitle] = useState(''); + const [validationResponse, setValidationResponse] = useState( + null + ); + + useEffect(function initialPageLoad() { + setCurrentDataViewTitle(jobCreator.indexPatternTitle); + }, []); + + useEffect( + function stepChange() { + if (step === STEP.PICK_DATA_VIEW) { + setValidationResponse(null); + } + }, + [step] + ); + + function onDataViewSelected(dataViewId: string) { + if (validating === false) { + setStep(STEP.VALIDATE); + validate(dataViewId); + } + } + + const validate = useCallback( + async (dataViewId: string) => { + setValidating(true); + + const { title } = await dataViews.get(dataViewId); + setNewDataViewTitle(title); + + const indices = title.split(','); + if (jobCreator.detectors.length) { + const datafeed: Datafeed = { ...jobCreator.datafeedConfig, indices }; + const resp = await validateDatafeedPreview({ + job: { + ...jobCreator.jobConfig, + datafeed_config: datafeed, + } as CombinedJob, + }); + setValidationResponse(resp); + } + setValidating(false); + }, + [dataViews, validateDatafeedPreview, jobCreator] + ); + + const applyDataView = useCallback(() => { + const newIndices = newDataViewTitle.split(','); + jobCreator.indices = newIndices; + resetAdvancedJob(jobCreator, navigateToPath); + }, [jobCreator, newDataViewTitle, navigateToPath]); + + return ( + <> + + + + + + + + + {step === STEP.PICK_DATA_VIEW && ( + <> + + + + + 'indexPatternApp', + name: i18n.translate( + 'xpack.ml.newJob.wizard.datafeedStep.dataView.step1.dataView', + { + defaultMessage: 'Index pattern', + } + ), + }, + ]} + fixedPageSize={fixedPageSize} + uiSettings={uiSettings} + savedObjects={savedObjects} + /> + + )} + {step === STEP.VALIDATE && ( + <> + + + + + {validating === true ? ( + <> + + + + ) : ( + + )} + + + + + + + + + + + applyDataView()} + isDisabled={validating} + data-test-subj="mlJobsImportButton" + > + + + + + + )} + + + + ); +}; + +const ValidationMessage: FC<{ + validationResponse: DatafeedValidationResponse | null; + dataViewTitle: string; +}> = ({ validationResponse, dataViewTitle }) => { + if (validationResponse === null) { + return ( + + + + ); + } + if (validationResponse.valid === true) { + if (validationResponse.documentsFound === true) { + return ( + + + + ); + } else { + return ( + + + + ); + } + } else { + return ( + + + + + + + + + + {validationResponse.error ? extractErrorMessage(validationResponse.error) : null} + + ); + } +}; diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/components/data_view/change_data_view_button.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/components/data_view/change_data_view_button.tsx new file mode 100644 index 0000000000000..dc9af26236d8c --- /dev/null +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/components/data_view/change_data_view_button.tsx @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC, useState } from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; + +import { EuiButtonEmpty } from '@elastic/eui'; +import { Description } from './description'; +import { ChangeDataViewModal } from './change_data_view'; + +export const ChangeDataView: FC<{ isDisabled: boolean }> = ({ isDisabled }) => { + const [showFlyout, setShowFlyout] = useState(false); + + return ( + <> + {showFlyout && } + + + + + + + + ); +}; diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/components/data_view/description.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/components/data_view/description.tsx new file mode 100644 index 0000000000000..2632660738a58 --- /dev/null +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/components/data_view/description.tsx @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { memo, FC } from 'react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiDescribedFormGroup, EuiFormRow } from '@elastic/eui'; + +export const Description: FC = memo(({ children }) => { + const title = i18n.translate('xpack.ml.newJob.wizard.datafeedStep.dataView.title', { + defaultMessage: 'Index pattern', + }); + return ( + {title}} + description={ + + } + > + + <>{children} + + + ); +}); diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/components/data_view/index.ts b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/components/data_view/index.ts new file mode 100644 index 0000000000000..ef7c451b4889c --- /dev/null +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/components/data_view/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { ChangeDataView } from './change_data_view_button'; diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/datafeed.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/datafeed.tsx index 77db2eb2419cd..47e488ab201ec 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/datafeed.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/datafeed.tsx @@ -14,6 +14,7 @@ import { FrequencyInput } from './components/frequency'; import { ScrollSizeInput } from './components/scroll_size'; import { ResetQueryButton } from './components/reset_query'; import { TimeField } from './components/time_field'; +import { ChangeDataView } from './components/data_view'; import { WIZARD_STEPS, StepProps } from '../step_types'; import { JobCreatorContext } from '../job_creator_context'; import { JsonEditorFlyout, EDITOR_MODE } from '../common/json_editor_flyout'; @@ -46,6 +47,7 @@ export const DatafeedStep: FC = ({ setCurrentStep, isCurrentStep }) = + diff --git a/x-pack/plugins/ml/public/application/services/ml_api_service/index.ts b/x-pack/plugins/ml/public/application/services/ml_api_service/index.ts index 883e5d499c3d4..720e54e386cbc 100644 --- a/x-pack/plugins/ml/public/application/services/ml_api_service/index.ts +++ b/x-pack/plugins/ml/public/application/services/ml_api_service/index.ts @@ -43,6 +43,7 @@ import type { FieldHistogramRequestConfig } from '../../datavisualizer/index_bas import type { DataRecognizerConfigResponse, Module } from '../../../../common/types/modules'; import { getHttp } from '../../util/dependency_cache'; import type { RuntimeMappings } from '../../../../common/types/fields'; +import type { DatafeedValidationResponse } from '../../../../common/types/job_validation'; export interface MlInfoResponse { defaults: MlServerDefaults; @@ -194,7 +195,7 @@ export function mlApiServicesProvider(httpService: HttpService) { }, validateJob(payload: { - job: Job; + job: CombinedJob; duration: { start?: number; end?: number; @@ -209,6 +210,15 @@ export function mlApiServicesProvider(httpService: HttpService) { }); }, + validateDatafeedPreview(payload: { job: CombinedJob }) { + const body = JSON.stringify(payload); + return httpService.http({ + path: `${basePath()}/validate/datafeed_preview`, + method: 'POST', + body, + }); + }, + validateCardinality$(job: CombinedJob): Observable { const body = JSON.stringify(job); return httpService.http$({ diff --git a/x-pack/plugins/ml/server/models/job_validation/index.ts b/x-pack/plugins/ml/server/models/job_validation/index.ts index 92d3e7d613efc..a527b9dcf3d4b 100644 --- a/x-pack/plugins/ml/server/models/job_validation/index.ts +++ b/x-pack/plugins/ml/server/models/job_validation/index.ts @@ -7,3 +7,7 @@ export { validateJob } from './job_validation'; export { validateCardinality } from './validate_cardinality'; +export { + validateDatafeedPreviewWithMessages, + validateDatafeedPreview, +} from './validate_datafeed_preview'; diff --git a/x-pack/plugins/ml/server/models/job_validation/job_validation.ts b/x-pack/plugins/ml/server/models/job_validation/job_validation.ts index 838f188455d44..4cd2d8a95ee79 100644 --- a/x-pack/plugins/ml/server/models/job_validation/job_validation.ts +++ b/x-pack/plugins/ml/server/models/job_validation/job_validation.ts @@ -17,7 +17,7 @@ import { basicJobValidation, uniqWithIsEqual } from '../../../common/util/job_ut import { validateBucketSpan } from './validate_bucket_span'; import { validateCardinality } from './validate_cardinality'; import { validateInfluencers } from './validate_influencers'; -import { validateDatafeedPreview } from './validate_datafeed_preview'; +import { validateDatafeedPreviewWithMessages } from './validate_datafeed_preview'; import { validateModelMemoryLimit } from './validate_model_memory_limit'; import { validateTimeRange, isValidTimeField } from './validate_time_range'; import { validateJobSchema } from '../../routes/schemas/job_validation_schema'; @@ -111,7 +111,9 @@ export async function validateJob( validationMessages.push({ id: 'missing_summary_count_field_name' }); } - validationMessages.push(...(await validateDatafeedPreview(mlClient, authHeader, job))); + validationMessages.push( + ...(await validateDatafeedPreviewWithMessages(mlClient, authHeader, job)) + ); } else { validationMessages = basicValidation.messages; validationMessages.push({ id: 'skipped_extended_tests' }); diff --git a/x-pack/plugins/ml/server/models/job_validation/validate_datafeed_preview.ts b/x-pack/plugins/ml/server/models/job_validation/validate_datafeed_preview.ts index 4ae94229a930b..0775de7ae0e13 100644 --- a/x-pack/plugins/ml/server/models/job_validation/validate_datafeed_preview.ts +++ b/x-pack/plugins/ml/server/models/job_validation/validate_datafeed_preview.ts @@ -9,12 +9,25 @@ import type { MlClient } from '../../lib/ml_client'; import type { AuthorizationHeader } from '../../lib/request_authorization'; import type { CombinedJob } from '../../../common/types/anomaly_detection_jobs'; import type { JobValidationMessage } from '../../../common/constants/messages'; +import type { DatafeedValidationResponse } from '../../../common/types/job_validation'; -export async function validateDatafeedPreview( +export async function validateDatafeedPreviewWithMessages( mlClient: MlClient, authHeader: AuthorizationHeader, job: CombinedJob ): Promise { + const { valid, documentsFound } = await validateDatafeedPreview(mlClient, authHeader, job); + if (valid) { + return documentsFound ? [] : [{ id: 'datafeed_preview_no_documents' }]; + } + return [{ id: 'datafeed_preview_failed' }]; +} + +export async function validateDatafeedPreview( + mlClient: MlClient, + authHeader: AuthorizationHeader, + job: CombinedJob +): Promise { const { datafeed_config: datafeed, ...tempJob } = job; try { const { body } = (await mlClient.previewDatafeed( @@ -28,11 +41,15 @@ export async function validateDatafeedPreview( // previewDatafeed response type is incorrect )) as unknown as { body: unknown[] }; - if (Array.isArray(body) === false || body.length === 0) { - return [{ id: 'datafeed_preview_no_documents' }]; - } - return []; + return { + valid: true, + documentsFound: Array.isArray(body) && body.length > 0, + }; } catch (error) { - return [{ id: 'datafeed_preview_failed' }]; + return { + valid: false, + documentsFound: false, + error: error.body ?? error, + }; } } diff --git a/x-pack/plugins/ml/server/routes/apidoc.json b/x-pack/plugins/ml/server/routes/apidoc.json index 7f53ebb92b68a..226b69e06b48a 100644 --- a/x-pack/plugins/ml/server/routes/apidoc.json +++ b/x-pack/plugins/ml/server/routes/apidoc.json @@ -123,11 +123,13 @@ "GetJobAuditMessages", "GetAllJobAuditMessages", "ClearJobAuditMessages", + "JobValidation", "EstimateBucketSpan", "CalculateModelMemoryLimit", "ValidateCardinality", "ValidateJob", + "ValidateDataFeedPreview", "DatafeedService", "CreateDatafeed", diff --git a/x-pack/plugins/ml/server/routes/job_validation.ts b/x-pack/plugins/ml/server/routes/job_validation.ts index b75eab20e7bc0..bceb59fa33fc6 100644 --- a/x-pack/plugins/ml/server/routes/job_validation.ts +++ b/x-pack/plugins/ml/server/routes/job_validation.ts @@ -16,12 +16,18 @@ import { modelMemoryLimitSchema, validateCardinalitySchema, validateJobSchema, + validateDatafeedPreviewSchema, } from './schemas/job_validation_schema'; import { estimateBucketSpanFactory } from '../models/bucket_span_estimator'; import { calculateModelMemoryLimitProvider } from '../models/calculate_model_memory_limit'; -import { validateJob, validateCardinality } from '../models/job_validation'; +import { + validateJob, + validateCardinality, + validateDatafeedPreview, +} from '../models/job_validation'; import { getAuthorizationHeader } from '../lib/request_authorization'; import type { MlClient } from '../lib/ml_client'; +import { CombinedJob } from '../../common/types/anomaly_detection_jobs'; type CalculateModelMemoryLimitPayload = TypeOf; @@ -205,4 +211,40 @@ export function jobValidationRoutes({ router, mlLicense, routeGuard }: RouteInit } }) ); + + /** + * @apiGroup DataFeedPreviewValidation + * + * @api {post} /api/ml/validate/datafeed_preview Validates datafeed preview + * @apiName ValidateDataFeedPreview + * @apiDescription Validates that the datafeed preview runs successfully and produces results + * + * @apiSchema (body) validateDatafeedPreviewSchema + */ + router.post( + { + path: '/api/ml/validate/datafeed_preview', + validate: { + body: validateDatafeedPreviewSchema, + }, + options: { + tags: ['access:ml:canCreateJob'], + }, + }, + routeGuard.fullLicenseAPIGuard(async ({ client, mlClient, request, response }) => { + try { + const resp = await validateDatafeedPreview( + mlClient, + getAuthorizationHeader(request), + request.body.job as CombinedJob + ); + + return response.ok({ + body: resp, + }); + } catch (e) { + return response.customError(wrapError(e)); + } + }) + ); } diff --git a/x-pack/plugins/ml/server/routes/schemas/job_validation_schema.ts b/x-pack/plugins/ml/server/routes/schemas/job_validation_schema.ts index a83bbbff6cec9..a481713f67359 100644 --- a/x-pack/plugins/ml/server/routes/schemas/job_validation_schema.ts +++ b/x-pack/plugins/ml/server/routes/schemas/job_validation_schema.ts @@ -60,6 +60,13 @@ export const validateJobSchema = schema.object({ }), }); +export const validateDatafeedPreviewSchema = schema.object({ + job: schema.object({ + ...anomalyDetectionJobSchema, + datafeed_config: datafeedConfigSchema, + }), +}); + export const validateCardinalitySchema = schema.object({ ...anomalyDetectionJobSchema, datafeed_config: datafeedConfigSchema, diff --git a/x-pack/test/api_integration/apis/ml/data_frame_analytics/delete.ts b/x-pack/test/api_integration/apis/ml/data_frame_analytics/delete.ts index 055b4b69ab7a6..e7ea71863352e 100644 --- a/x-pack/test/api_integration/apis/ml/data_frame_analytics/delete.ts +++ b/x-pack/test/api_integration/apis/ml/data_frame_analytics/delete.ts @@ -130,7 +130,7 @@ export default ({ getService }: FtrProviderContext) => { const destinationIndex = generateDestinationIndex(analyticsId); before(async () => { - await ml.api.createIndices(destinationIndex); + await ml.api.createIndex(destinationIndex); await ml.api.assertIndicesExist(destinationIndex); }); @@ -189,7 +189,7 @@ export default ({ getService }: FtrProviderContext) => { before(async () => { // Mimic real job by creating target index & index pattern after DFA job is created - await ml.api.createIndices(destinationIndex); + await ml.api.createIndex(destinationIndex); await ml.api.assertIndicesExist(destinationIndex); await ml.testResources.createIndexPatternIfNeeded(destinationIndex); }); diff --git a/x-pack/test/api_integration/apis/ml/job_validation/datafeed_preview_validation.ts b/x-pack/test/api_integration/apis/ml/job_validation/datafeed_preview_validation.ts new file mode 100644 index 0000000000000..c16050e08c886 --- /dev/null +++ b/x-pack/test/api_integration/apis/ml/job_validation/datafeed_preview_validation.ts @@ -0,0 +1,175 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { estypes } from '@elastic/elasticsearch'; +import { FtrProviderContext } from '../../../ftr_provider_context'; +import { USER } from '../../../../functional/services/ml/security_common'; +import { COMMON_REQUEST_HEADERS } from '../../../../functional/services/ml/common_api'; + +const farequoteMappings: estypes.MappingTypeMapping = { + properties: { + '@timestamp': { + type: 'date', + }, + airline: { + type: 'keyword', + }, + responsetime: { + type: 'float', + }, + }, +}; + +function getBaseJobConfig() { + return { + job_id: 'test', + description: '', + analysis_config: { + bucket_span: '15m', + detectors: [ + { + function: 'mean', + field_name: 'responsetime', + }, + ], + influencers: [], + }, + analysis_limits: { + model_memory_limit: '11MB', + }, + data_description: { + time_field: '@timestamp', + time_format: 'epoch_ms', + }, + model_plot_config: { + enabled: false, + annotations_enabled: false, + }, + model_snapshot_retention_days: 10, + daily_model_snapshot_retention_after_days: 1, + allow_lazy_open: false, + datafeed_config: { + query: { + bool: { + must: [ + { + match_all: {}, + }, + ], + }, + }, + indices: ['ft_farequote'], + scroll_size: 1000, + delayed_data_check_config: { + enabled: true, + }, + job_id: 'test', + datafeed_id: 'datafeed-test', + }, + }; +} + +export default ({ getService }: FtrProviderContext) => { + const esArchiver = getService('esArchiver'); + const supertest = getService('supertestWithoutAuth'); + const ml = getService('ml'); + + describe('Validate datafeed preview', function () { + before(async () => { + await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); + await ml.testResources.setKibanaTimeZoneToUTC(); + await ml.api.createIndex('farequote_empty', farequoteMappings); + }); + + after(async () => { + await ml.api.cleanMlIndices(); + await ml.api.deleteIndices('farequote_empty'); + }); + + it(`should validate a job with documents`, async () => { + const job = getBaseJobConfig(); + + const { body } = await supertest + .post('/api/ml/validate/datafeed_preview') + .auth(USER.ML_POWERUSER, ml.securityCommon.getPasswordForUser(USER.ML_POWERUSER)) + .set(COMMON_REQUEST_HEADERS) + .send({ job }) + .expect(200); + + expect(body.valid).to.eql(true, `valid should be true, but got ${body.valid}`); + expect(body.documentsFound).to.eql( + true, + `documentsFound should be true, but got ${body.documentsFound}` + ); + }); + + it(`should fail to validate a job with documents and non-existent field`, async () => { + const job = getBaseJobConfig(); + job.analysis_config.detectors[0].field_name = 'no_such_field'; + + const { body } = await supertest + .post('/api/ml/validate/datafeed_preview') + .auth(USER.ML_POWERUSER, ml.securityCommon.getPasswordForUser(USER.ML_POWERUSER)) + .set(COMMON_REQUEST_HEADERS) + .send({ job }) + .expect(200); + + expect(body.valid).to.eql(false, `valid should be false, but got ${body.valid}`); + expect(body.documentsFound).to.eql( + false, + `documentsFound should be false, but got ${body.documentsFound}` + ); + }); + + it(`should validate a job with no documents`, async () => { + const job = getBaseJobConfig(); + job.datafeed_config.indices = ['farequote_empty']; + + const { body } = await supertest + .post('/api/ml/validate/datafeed_preview') + .auth(USER.ML_POWERUSER, ml.securityCommon.getPasswordForUser(USER.ML_POWERUSER)) + .set(COMMON_REQUEST_HEADERS) + .send({ job }) + .expect(200); + + expect(body.valid).to.eql(true, `valid should be true, but got ${body.valid}`); + expect(body.documentsFound).to.eql( + false, + `documentsFound should be false, but got ${body.documentsFound}` + ); + }); + + it(`should fail for viewer user`, async () => { + const job = getBaseJobConfig(); + + await supertest + .post('/api/ml/validate/datafeed_preview') + .auth(USER.ML_VIEWER, ml.securityCommon.getPasswordForUser(USER.ML_VIEWER)) + .set(COMMON_REQUEST_HEADERS) + .send({ job }) + .expect(403); + }); + + it(`should fail for unauthorized user`, async () => { + const job = getBaseJobConfig(); + + await supertest + .post('/api/ml/validate/datafeed_preview') + .auth(USER.ML_UNAUTHORIZED, ml.securityCommon.getPasswordForUser(USER.ML_UNAUTHORIZED)) + .set(COMMON_REQUEST_HEADERS) + .send({ job }) + .expect(403); + }); + }); +}; diff --git a/x-pack/test/api_integration/apis/ml/job_validation/index.ts b/x-pack/test/api_integration/apis/ml/job_validation/index.ts index 4b75102d7b0bf..be07ae3b1852a 100644 --- a/x-pack/test/api_integration/apis/ml/job_validation/index.ts +++ b/x-pack/test/api_integration/apis/ml/job_validation/index.ts @@ -13,5 +13,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./calculate_model_memory_limit')); loadTestFile(require.resolve('./cardinality')); loadTestFile(require.resolve('./validate')); + loadTestFile(require.resolve('./datafeed_preview_validation')); }); } diff --git a/x-pack/test/functional/services/ml/api.ts b/x-pack/test/functional/services/ml/api.ts index abde3bf365384..6ffd95f213c41 100644 --- a/x-pack/test/functional/services/ml/api.ts +++ b/x-pack/test/functional/services/ml/api.ts @@ -126,14 +126,20 @@ export function MachineLearningAPIProvider({ getService }: FtrProviderContext) { ); }, - async createIndices(indices: string) { + async createIndex( + indices: string, + mappings?: Record | estypes.MappingTypeMapping + ) { log.debug(`Creating indices: '${indices}'...`); if ((await es.indices.exists({ index: indices, allow_no_indices: false })).body === true) { log.debug(`Indices '${indices}' already exist. Nothing to create.`); return; } - const { body } = await es.indices.create({ index: indices }); + const { body } = await es.indices.create({ + index: indices, + ...(mappings ? { body: { mappings } } : {}), + }); expect(body) .to.have.property('acknowledged') .eql(true, 'Response for create request indices should be acknowledged.'); From b306f8e2c31c96623d310fdd0dc110e7935b8993 Mon Sep 17 00:00:00 2001 From: DeDe Morton Date: Tue, 19 Oct 2021 08:01:49 -0700 Subject: [PATCH 063/204] Update UI links to Fleet and Agent docs (#115295) * Update UI links to Fleet and Agent docs * Update link service * Fix merge problem * Update link service Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../public/kibana-plugin-core-public.doclinksstart.links.md | 1 + .../core/public/kibana-plugin-core-public.doclinksstart.md | 2 +- src/core/public/doc_links/doc_links_service.ts | 4 +++- src/core/public/public.api.md | 1 + .../components/enrollment_instructions/manual/index.tsx | 2 +- 5 files changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md index e79bc7a0db026..73efed79324fe 100644 --- a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md +++ b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md @@ -237,6 +237,7 @@ readonly links: { elasticAgent: string; datastreams: string; datastreamsNamingScheme: string; + installElasticAgent: string; upgradeElasticAgent: string; upgradeElasticAgent712lower: string; learnMoreBlog: string; diff --git a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md index d90972d327041..fdf469f443f28 100644 --- a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md +++ b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md @@ -17,5 +17,5 @@ export interface DocLinksStart | --- | --- | --- | | [DOC\_LINK\_VERSION](./kibana-plugin-core-public.doclinksstart.doc_link_version.md) | string | | | [ELASTIC\_WEBSITE\_URL](./kibana-plugin-core-public.doclinksstart.elastic_website_url.md) | string | | -| [links](./kibana-plugin-core-public.doclinksstart.links.md) | {
readonly settings: string;
readonly elasticStackGetStarted: string;
readonly apm: {
readonly kibanaSettings: string;
readonly supportedServiceMaps: string;
readonly customLinks: string;
readonly droppedTransactionSpans: string;
readonly upgrading: string;
readonly metaData: string;
};
readonly canvas: {
readonly guide: string;
};
readonly dashboard: {
readonly guide: string;
readonly drilldowns: string;
readonly drilldownsTriggerPicker: string;
readonly urlDrilldownTemplateSyntax: string;
readonly urlDrilldownVariables: string;
};
readonly discover: Record<string, string>;
readonly filebeat: {
readonly base: string;
readonly installation: string;
readonly configuration: string;
readonly elasticsearchOutput: string;
readonly elasticsearchModule: string;
readonly startup: string;
readonly exportedFields: string;
readonly suricataModule: string;
readonly zeekModule: string;
};
readonly auditbeat: {
readonly base: string;
readonly auditdModule: string;
readonly systemModule: 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 libbeat: {
readonly getStarted: 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: {
readonly overview: string;
readonly mapping: 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 search: {
readonly sessions: string;
readonly sessionLimits: string;
};
readonly indexPatterns: {
readonly introduction: string;
readonly fieldFormattersNumber: string;
readonly fieldFormattersString: string;
readonly runtimeFields: string;
};
readonly addData: string;
readonly kibana: string;
readonly upgradeAssistant: string;
readonly rollupJobs: string;
readonly elasticsearch: Record<string, string>;
readonly siem: {
readonly privileges: string;
readonly guide: string;
readonly gettingStarted: string;
readonly ml: string;
readonly ruleChangeLog: string;
readonly detectionsReq: string;
readonly networkMap: string;
readonly troubleshootGaps: string;
};
readonly securitySolution: {
readonly trustedApps: string;
};
readonly query: {
readonly eql: string;
readonly kueryQuerySyntax: string;
readonly luceneQuerySyntax: string;
readonly percolate: string;
readonly queryDsl: string;
readonly autocompleteChanges: string;
};
readonly date: {
readonly dateMath: string;
readonly dateMathIndexNames: string;
};
readonly management: Record<string, string>;
readonly ml: Record<string, string>;
readonly transforms: Record<string, string>;
readonly visualize: Record<string, string>;
readonly apis: Readonly<{
bulkIndexAlias: string;
byteSizeUnits: string;
createAutoFollowPattern: string;
createFollower: string;
createIndex: string;
createSnapshotLifecyclePolicy: string;
createRoleMapping: string;
createRoleMappingTemplates: string;
createRollupJobsRequest: 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;
putIndexTemplateV1: string;
putWatch: string;
simulatePipeline: string;
timeUnits: string;
updateTransform: string;
}>;
readonly observability: Readonly<{
guide: string;
infrastructureThreshold: string;
logsThreshold: string;
metricsThreshold: string;
monitorStatus: string;
monitorUptime: string;
tlsCertificate: string;
uptimeDurationAnomaly: string;
}>;
readonly alerting: Record<string, string>;
readonly maps: Record<string, string>;
readonly monitoring: Record<string, string>;
readonly security: Readonly<{
apiKeyServiceSettings: string;
clusterPrivileges: string;
elasticsearchSettings: string;
elasticsearchEnableSecurity: string;
indicesPrivileges: string;
kibanaTLS: string;
kibanaPrivileges: string;
mappingRoles: string;
mappingRolesFieldRules: string;
runAsPrivilege: string;
}>;
readonly spaces: Readonly<{
kibanaLegacyUrlAliases: string;
kibanaDisableLegacyUrlAliasesApi: string;
}>;
readonly watcher: Record<string, string>;
readonly ccs: Record<string, string>;
readonly plugins: Record<string, string>;
readonly snapshotRestore: Record<string, string>;
readonly ingest: Record<string, string>;
readonly fleet: Readonly<{
guide: string;
fleetServer: string;
fleetServerAddFleetServer: string;
settings: string;
settingsFleetServerHostSettings: string;
troubleshooting: string;
elasticAgent: string;
datastreams: string;
datastreamsNamingScheme: string;
upgradeElasticAgent: string;
upgradeElasticAgent712lower: string;
learnMoreBlog: string;
apiKeysLearnMore: string;
}>;
readonly ecs: {
readonly guide: string;
};
readonly clients: {
readonly guide: string;
readonly goOverview: string;
readonly javaIndex: string;
readonly jsIntro: string;
readonly netGuide: string;
readonly perlGuide: string;
readonly phpGuide: string;
readonly pythonGuide: string;
readonly rubyOverview: string;
readonly rustGuide: string;
};
} | | +| [links](./kibana-plugin-core-public.doclinksstart.links.md) | {
readonly settings: string;
readonly elasticStackGetStarted: string;
readonly apm: {
readonly kibanaSettings: string;
readonly supportedServiceMaps: string;
readonly customLinks: string;
readonly droppedTransactionSpans: string;
readonly upgrading: string;
readonly metaData: string;
};
readonly canvas: {
readonly guide: string;
};
readonly dashboard: {
readonly guide: string;
readonly drilldowns: string;
readonly drilldownsTriggerPicker: string;
readonly urlDrilldownTemplateSyntax: string;
readonly urlDrilldownVariables: string;
};
readonly discover: Record<string, string>;
readonly filebeat: {
readonly base: string;
readonly installation: string;
readonly configuration: string;
readonly elasticsearchOutput: string;
readonly elasticsearchModule: string;
readonly startup: string;
readonly exportedFields: string;
readonly suricataModule: string;
readonly zeekModule: string;
};
readonly auditbeat: {
readonly base: string;
readonly auditdModule: string;
readonly systemModule: 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 libbeat: {
readonly getStarted: 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: {
readonly overview: string;
readonly mapping: 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 search: {
readonly sessions: string;
readonly sessionLimits: string;
};
readonly indexPatterns: {
readonly introduction: string;
readonly fieldFormattersNumber: string;
readonly fieldFormattersString: string;
readonly runtimeFields: string;
};
readonly addData: string;
readonly kibana: string;
readonly upgradeAssistant: string;
readonly rollupJobs: string;
readonly elasticsearch: Record<string, string>;
readonly siem: {
readonly privileges: string;
readonly guide: string;
readonly gettingStarted: string;
readonly ml: string;
readonly ruleChangeLog: string;
readonly detectionsReq: string;
readonly networkMap: string;
readonly troubleshootGaps: string;
};
readonly securitySolution: {
readonly trustedApps: string;
};
readonly query: {
readonly eql: string;
readonly kueryQuerySyntax: string;
readonly luceneQuerySyntax: string;
readonly percolate: string;
readonly queryDsl: string;
readonly autocompleteChanges: string;
};
readonly date: {
readonly dateMath: string;
readonly dateMathIndexNames: string;
};
readonly management: Record<string, string>;
readonly ml: Record<string, string>;
readonly transforms: Record<string, string>;
readonly visualize: Record<string, string>;
readonly apis: Readonly<{
bulkIndexAlias: string;
byteSizeUnits: string;
createAutoFollowPattern: string;
createFollower: string;
createIndex: string;
createSnapshotLifecyclePolicy: string;
createRoleMapping: string;
createRoleMappingTemplates: string;
createRollupJobsRequest: 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;
putIndexTemplateV1: string;
putWatch: string;
simulatePipeline: string;
timeUnits: string;
updateTransform: string;
}>;
readonly observability: Readonly<{
guide: string;
infrastructureThreshold: string;
logsThreshold: string;
metricsThreshold: string;
monitorStatus: string;
monitorUptime: string;
tlsCertificate: string;
uptimeDurationAnomaly: string;
}>;
readonly alerting: Record<string, string>;
readonly maps: Record<string, string>;
readonly monitoring: Record<string, string>;
readonly security: Readonly<{
apiKeyServiceSettings: string;
clusterPrivileges: string;
elasticsearchSettings: string;
elasticsearchEnableSecurity: string;
indicesPrivileges: string;
kibanaTLS: string;
kibanaPrivileges: string;
mappingRoles: string;
mappingRolesFieldRules: string;
runAsPrivilege: string;
}>;
readonly spaces: Readonly<{
kibanaLegacyUrlAliases: string;
kibanaDisableLegacyUrlAliasesApi: string;
}>;
readonly watcher: Record<string, string>;
readonly ccs: Record<string, string>;
readonly plugins: Record<string, string>;
readonly snapshotRestore: Record<string, string>;
readonly ingest: Record<string, string>;
readonly fleet: Readonly<{
guide: string;
fleetServer: string;
fleetServerAddFleetServer: string;
settings: string;
settingsFleetServerHostSettings: string;
troubleshooting: string;
elasticAgent: string;
datastreams: string;
datastreamsNamingScheme: string;
installElasticAgent: string;
upgradeElasticAgent: string;
upgradeElasticAgent712lower: string;
learnMoreBlog: string;
apiKeysLearnMore: string;
}>;
readonly ecs: {
readonly guide: string;
};
readonly clients: {
readonly guide: string;
readonly goOverview: string;
readonly javaIndex: string;
readonly jsIntro: string;
readonly netGuide: string;
readonly perlGuide: string;
readonly phpGuide: string;
readonly pythonGuide: string;
readonly rubyOverview: string;
readonly rustGuide: string;
};
} | | diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts index a07e12eae8d71..91ad185447986 100644 --- a/src/core/public/doc_links/doc_links_service.ts +++ b/src/core/public/doc_links/doc_links_service.ts @@ -477,9 +477,10 @@ export class DocLinksService { settings: `${FLEET_DOCS}fleet-settings.html#fleet-server-hosts-setting`, settingsFleetServerHostSettings: `${FLEET_DOCS}fleet-settings.html#fleet-server-hosts-setting`, troubleshooting: `${FLEET_DOCS}fleet-troubleshooting.html`, - elasticAgent: `${FLEET_DOCS}elastic-agent-installation-configuration.html`, + elasticAgent: `${FLEET_DOCS}elastic-agent-installation.html`, datastreams: `${FLEET_DOCS}data-streams.html`, datastreamsNamingScheme: `${FLEET_DOCS}data-streams.html#data-streams-naming-scheme`, + installElasticAgent: `${FLEET_DOCS}install-fleet-managed-elastic-agent.html`, upgradeElasticAgent: `${FLEET_DOCS}upgrade-elastic-agent.html`, upgradeElasticAgent712lower: `${FLEET_DOCS}upgrade-elastic-agent.html#upgrade-7.12-lower`, learnMoreBlog: `${ELASTIC_WEBSITE_URL}blog/elastic-agent-and-fleet-make-it-easier-to-integrate-your-systems-with-elastic`, @@ -740,6 +741,7 @@ export interface DocLinksStart { elasticAgent: string; datastreams: string; datastreamsNamingScheme: string; + installElasticAgent: string; upgradeElasticAgent: string; upgradeElasticAgent712lower: string; learnMoreBlog: string; diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index 4c7f8aab5b767..508299686b0d9 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -706,6 +706,7 @@ export interface DocLinksStart { elasticAgent: string; datastreams: string; datastreamsNamingScheme: string; + installElasticAgent: string; upgradeElasticAgent: string; upgradeElasticAgent712lower: string; learnMoreBlog: string; diff --git a/x-pack/plugins/fleet/public/components/enrollment_instructions/manual/index.tsx b/x-pack/plugins/fleet/public/components/enrollment_instructions/manual/index.tsx index ecbcf309c5992..6d4d6a7172534 100644 --- a/x-pack/plugins/fleet/public/components/enrollment_instructions/manual/index.tsx +++ b/x-pack/plugins/fleet/public/components/enrollment_instructions/manual/index.tsx @@ -78,7 +78,7 @@ export const ManualInstructions: React.FunctionComponent = ({ defaultMessage="See the {link} for RPM / DEB deploy instructions." values={{ link: ( - + Date: Tue, 19 Oct 2021 17:15:52 +0200 Subject: [PATCH 064/204] [Transform] Add alerting rules management to Transform UI (#115363) * transform alert flyout * fetch alerting rules * show alerting rules indicators * filter continuous transforms * add alert rules to the expanded row * edit alert rule from the list * fix ts issues * fix types * update texts * refactor using context, wip create alert from the list * update unit test * fix ts issue * privilege check --- .../common/api_schemas/transforms.ts | 4 +- .../transform/common/types/alerting.ts | 4 +- .../transform/common/types/transform.ts | 17 ++- .../alerting/transform_alerting_flyout.tsx | 127 ++++++++++++++++++ .../public/app/__mocks__/app_dependencies.tsx | 2 + .../transform/public/app/app_dependencies.tsx | 2 + .../public/app/common/transform_list.ts | 9 +- .../public/app/hooks/use_get_transforms.ts | 1 + .../components/authorization_provider.tsx | 12 +- .../lib/authorization/components/common.ts | 10 ++ .../public/app/mount_management_section.ts | 3 +- .../clone_transform_section.tsx | 4 +- .../step_create/step_create_form.tsx | 38 +++++- .../components/step_details/common.ts | 4 +- .../step_details/step_details_form.tsx | 4 +- .../components/wizard/wizard.tsx | 4 +- .../create_alert_rule_action_name.tsx | 38 ++++++ .../components/action_create_alert/index.ts | 8 ++ .../use_create_alert_rule_action.tsx | 47 +++++++ .../transform_list/expanded_row.test.tsx | 3 +- .../transform_list/expanded_row.tsx | 52 +++++-- .../expanded_row_details_pane.tsx | 5 +- .../transform_list/transform_list.tsx | 14 +- .../transform_list/use_actions.test.tsx | 1 + .../components/transform_list/use_actions.tsx | 3 + .../transform_list/use_columns.test.tsx | 15 ++- .../components/transform_list/use_columns.tsx | 34 +++++ .../transform_management_section.tsx | 20 ++- x-pack/plugins/transform/public/plugin.ts | 4 +- .../transform_health_service.ts | 97 ++++++++++--- .../transform/server/routes/api/transforms.ts | 12 ++ .../transform/server/services/license.ts | 9 +- .../services/endpoint.ts | 6 +- 33 files changed, 539 insertions(+), 74 deletions(-) create mode 100644 x-pack/plugins/transform/public/alerting/transform_alerting_flyout.tsx create mode 100644 x-pack/plugins/transform/public/app/sections/transform_management/components/action_create_alert/create_alert_rule_action_name.tsx create mode 100644 x-pack/plugins/transform/public/app/sections/transform_management/components/action_create_alert/index.ts create mode 100644 x-pack/plugins/transform/public/app/sections/transform_management/components/action_create_alert/use_create_alert_rule_action.tsx diff --git a/x-pack/plugins/transform/common/api_schemas/transforms.ts b/x-pack/plugins/transform/common/api_schemas/transforms.ts index 7fb1a62a67bb8..8867ecb5cc760 100644 --- a/x-pack/plugins/transform/common/api_schemas/transforms.ts +++ b/x-pack/plugins/transform/common/api_schemas/transforms.ts @@ -12,7 +12,7 @@ import type { ES_FIELD_TYPES } from '../../../../../src/plugins/data/common'; import type { Dictionary } from '../types/common'; import type { PivotAggDict } from '../types/pivot_aggs'; import type { PivotGroupByDict } from '../types/pivot_group_by'; -import type { TransformId, TransformPivotConfig } from '../types/transform'; +import type { TransformId, TransformConfigUnion } from '../types/transform'; import { transformStateSchema, runtimeMappingsSchema } from './common'; @@ -33,7 +33,7 @@ export type GetTransformsRequestSchema = TypeOf; + +export type TransformHealthAlertRule = Omit, 'apiKey'>; diff --git a/x-pack/plugins/transform/common/types/transform.ts b/x-pack/plugins/transform/common/types/transform.ts index f1e7efdadca9d..a478946ff917c 100644 --- a/x-pack/plugins/transform/common/types/transform.ts +++ b/x-pack/plugins/transform/common/types/transform.ts @@ -5,11 +5,12 @@ * 2.0. */ -import { EuiComboBoxOptionOption } from '@elastic/eui/src/components/combo_box/types'; +import type { EuiComboBoxOptionOption } from '@elastic/eui/src/components/combo_box/types'; import type { LatestFunctionConfig, PutTransformsRequestSchema } from '../api_schemas/transforms'; import { isPopulatedObject } from '../shared_imports'; -import { PivotGroupByDict } from './pivot_group_by'; -import { PivotAggDict } from './pivot_aggs'; +import type { PivotGroupByDict } from './pivot_group_by'; +import type { PivotAggDict } from './pivot_aggs'; +import type { TransformHealthAlertRule } from './alerting'; export type IndexName = string; export type IndexPattern = string; @@ -22,6 +23,7 @@ export type TransformBaseConfig = PutTransformsRequestSchema & { id: TransformId; create_time?: number; version?: string; + alerting_rules?: TransformHealthAlertRule[]; }; export interface PivotConfigDefinition { @@ -45,6 +47,11 @@ export type TransformLatestConfig = Omit & { export type TransformConfigUnion = TransformPivotConfig | TransformLatestConfig; +export type ContinuousTransform = Omit & + Required<{ + sync: TransformConfigUnion['sync']; + }>; + export function isPivotTransform(transform: unknown): transform is TransformPivotConfig { return isPopulatedObject(transform, ['pivot']); } @@ -53,6 +60,10 @@ export function isLatestTransform(transform: unknown): transform is TransformLat return isPopulatedObject(transform, ['latest']); } +export function isContinuousTransform(transform: unknown): transform is ContinuousTransform { + return isPopulatedObject(transform, ['sync']); +} + export interface LatestFunctionConfigUI { unique_key: Array> | undefined; sort: EuiComboBoxOptionOption | undefined; diff --git a/x-pack/plugins/transform/public/alerting/transform_alerting_flyout.tsx b/x-pack/plugins/transform/public/alerting/transform_alerting_flyout.tsx new file mode 100644 index 0000000000000..63d00f280f3f3 --- /dev/null +++ b/x-pack/plugins/transform/public/alerting/transform_alerting_flyout.tsx @@ -0,0 +1,127 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { createContext, FC, useContext, useMemo } from 'react'; +import { memoize } from 'lodash'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { pluck } from 'rxjs/operators'; +import useObservable from 'react-use/lib/useObservable'; +import { useAppDependencies } from '../app/app_dependencies'; +import { TransformHealthAlertRule, TransformHealthRuleParams } from '../../common/types/alerting'; +import { TRANSFORM_RULE_TYPE } from '../../common'; + +interface TransformAlertFlyoutProps { + initialAlert?: TransformHealthAlertRule | null; + ruleParams?: TransformHealthRuleParams | null; + onSave?: () => void; + onCloseFlyout: () => void; +} + +export const TransformAlertFlyout: FC = ({ + initialAlert, + ruleParams, + onCloseFlyout, + onSave, +}) => { + const { triggersActionsUi } = useAppDependencies(); + + const AlertFlyout = useMemo(() => { + if (!triggersActionsUi) return; + + const commonProps = { + onClose: () => { + onCloseFlyout(); + }, + onSave: async () => { + if (onSave) { + onSave(); + } + }, + }; + + if (initialAlert) { + return triggersActionsUi.getEditAlertFlyout({ + ...commonProps, + initialAlert, + }); + } + + return triggersActionsUi.getAddAlertFlyout({ + ...commonProps, + consumer: 'stackAlerts', + canChangeTrigger: false, + alertTypeId: TRANSFORM_RULE_TYPE.TRANSFORM_HEALTH, + metadata: {}, + initialValues: { + params: ruleParams!, + }, + }); + // deps on id to avoid re-rendering on auto-refresh + }, [triggersActionsUi, initialAlert, ruleParams, onCloseFlyout, onSave]); + + return <>{AlertFlyout}; +}; + +interface AlertRulesManage { + editAlertRule$: Observable; + createAlertRule$: Observable; + setEditAlertRule: (alertRule: TransformHealthAlertRule) => void; + setCreateAlertRule: (transformId: string) => void; + hideAlertFlyout: () => void; +} + +export const getAlertRuleManageContext = memoize(function (): AlertRulesManage { + const ruleState$ = new BehaviorSubject<{ + editAlertRule: null | TransformHealthAlertRule; + createAlertRule: null | TransformHealthRuleParams; + }>({ + editAlertRule: null, + createAlertRule: null, + }); + return { + editAlertRule$: ruleState$.pipe(pluck('editAlertRule')), + createAlertRule$: ruleState$.pipe(pluck('createAlertRule')), + setEditAlertRule: (initialRule) => { + ruleState$.next({ + createAlertRule: null, + editAlertRule: initialRule, + }); + }, + setCreateAlertRule: (transformId: string) => { + ruleState$.next({ + createAlertRule: { includeTransforms: [transformId] }, + editAlertRule: null, + }); + }, + hideAlertFlyout: () => { + ruleState$.next({ + createAlertRule: null, + editAlertRule: null, + }); + }, + }; +}); + +export const AlertRulesManageContext = createContext(getAlertRuleManageContext()); + +export function useAlertRuleFlyout(): AlertRulesManage { + return useContext(AlertRulesManageContext); +} + +export const TransformAlertFlyoutWrapper = () => { + const { editAlertRule$, createAlertRule$, hideAlertFlyout } = useAlertRuleFlyout(); + const editAlertRule = useObservable(editAlertRule$); + const createAlertRule = useObservable(createAlertRule$); + + return editAlertRule || createAlertRule ? ( + + ) : null; +}; diff --git a/x-pack/plugins/transform/public/app/__mocks__/app_dependencies.tsx b/x-pack/plugins/transform/public/app/__mocks__/app_dependencies.tsx index 8dc0e277c284d..ab38d05ec9f8f 100644 --- a/x-pack/plugins/transform/public/app/__mocks__/app_dependencies.tsx +++ b/x-pack/plugins/transform/public/app/__mocks__/app_dependencies.tsx @@ -19,6 +19,7 @@ import { Storage } from '../../../../../../src/plugins/kibana_utils/public'; import type { AppDependencies } from '../app_dependencies'; import { MlSharedContext } from './shared_context'; import type { GetMlSharedImportsReturnType } from '../../shared_imports'; +import type { TriggersAndActionsUIPublicPluginStart } from '../../../../triggers_actions_ui/public'; const coreSetup = coreMock.createSetup(); const coreStart = coreMock.createStart(); @@ -43,6 +44,7 @@ const appDependencies: AppDependencies = { savedObjectsPlugin: savedObjectsPluginMock.createStartContract(), share: { urlGenerators: { getUrlGenerator: jest.fn() } } as unknown as SharePluginStart, ml: {} as GetMlSharedImportsReturnType, + triggersActionsUi: {} as jest.Mocked, }; export const useAppDependencies = () => { diff --git a/x-pack/plugins/transform/public/app/app_dependencies.tsx b/x-pack/plugins/transform/public/app/app_dependencies.tsx index d3f356f3e83b3..da1178e395720 100644 --- a/x-pack/plugins/transform/public/app/app_dependencies.tsx +++ b/x-pack/plugins/transform/public/app/app_dependencies.tsx @@ -16,6 +16,7 @@ import { useKibana } from '../../../../../src/plugins/kibana_react/public'; import type { Storage } from '../../../../../src/plugins/kibana_utils/public'; import type { GetMlSharedImportsReturnType } from '../shared_imports'; +import type { TriggersAndActionsUIPublicPluginStart } from '../../../triggers_actions_ui/public'; export interface AppDependencies { application: CoreStart['application']; @@ -34,6 +35,7 @@ export interface AppDependencies { share: SharePluginStart; ml: GetMlSharedImportsReturnType; spaces?: SpacesPluginStart; + triggersActionsUi: TriggersAndActionsUIPublicPluginStart; } export const useAppDependencies = () => { diff --git a/x-pack/plugins/transform/public/app/common/transform_list.ts b/x-pack/plugins/transform/public/app/common/transform_list.ts index d73018e284a8c..c8ddd32f6a8ff 100644 --- a/x-pack/plugins/transform/public/app/common/transform_list.ts +++ b/x-pack/plugins/transform/public/app/common/transform_list.ts @@ -5,10 +5,10 @@ * 2.0. */ -import { EuiTableActionsColumnType } from '@elastic/eui'; - -import { TransformConfigUnion, TransformId } from '../../../common/types/transform'; -import { TransformStats } from '../../../common/types/transform_stats'; +import type { EuiTableActionsColumnType } from '@elastic/eui'; +import type { TransformConfigUnion, TransformId } from '../../../common/types/transform'; +import type { TransformStats } from '../../../common/types/transform_stats'; +import type { TransformHealthAlertRule } from '../../../common/types/alerting'; // Used to pass on attribute names to table columns export enum TRANSFORM_LIST_COLUMN { @@ -21,6 +21,7 @@ export interface TransformListRow { config: TransformConfigUnion; mode?: string; // added property on client side to allow filtering by this field stats: TransformStats; + alerting_rules?: TransformHealthAlertRule[]; } // The single Action type is not exported as is diff --git a/x-pack/plugins/transform/public/app/hooks/use_get_transforms.ts b/x-pack/plugins/transform/public/app/hooks/use_get_transforms.ts index 2d3425dfeedca..7879e15118a33 100644 --- a/x-pack/plugins/transform/public/app/hooks/use_get_transforms.ts +++ b/x-pack/plugins/transform/public/app/hooks/use_get_transforms.ts @@ -87,6 +87,7 @@ export const useGetTransforms = ( mode: typeof config.sync !== 'undefined' ? TRANSFORM_MODE.CONTINUOUS : TRANSFORM_MODE.BATCH, stats, + alerting_rules: config.alerting_rules, }); return reducedtableRows; }, [] as TransformListRow[]); diff --git a/x-pack/plugins/transform/public/app/lib/authorization/components/authorization_provider.tsx b/x-pack/plugins/transform/public/app/lib/authorization/components/authorization_provider.tsx index 875c0f60969ed..cc6313bf058c6 100644 --- a/x-pack/plugins/transform/public/app/lib/authorization/components/authorization_provider.tsx +++ b/x-pack/plugins/transform/public/app/lib/authorization/components/authorization_provider.tsx @@ -20,12 +20,14 @@ interface Authorization { capabilities: Capabilities; } -const initialCapabalities: Capabilities = { +const initialCapabilities: Capabilities = { canGetTransform: false, canDeleteTransform: false, canPreviewTransform: false, canCreateTransform: false, canStartStopTransform: false, + canCreateTransformAlerts: false, + canUseTransformAlerts: false, }; const initialValue: Authorization = { @@ -35,7 +37,7 @@ const initialValue: Authorization = { hasAllPrivileges: false, missingPrivileges: {}, }, - capabilities: initialCapabalities, + capabilities: initialCapabilities, }; export const AuthorizationContext = createContext({ ...initialValue }); @@ -58,7 +60,7 @@ export const AuthorizationProvider = ({ privilegesEndpoint, children }: Props) = const value = { isLoading, privileges: isLoading ? { ...initialValue.privileges } : privilegesData, - capabilities: { ...initialCapabalities }, + capabilities: { ...initialCapabilities }, apiError: error ? (error as Error) : null, }; @@ -85,6 +87,10 @@ export const AuthorizationProvider = ({ privilegesEndpoint, children }: Props) = hasPrivilege(['cluster', 'cluster:admin/transform/start_task']) && hasPrivilege(['cluster', 'cluster:admin/transform/stop']); + value.capabilities.canCreateTransformAlerts = value.capabilities.canCreateTransform; + + value.capabilities.canUseTransformAlerts = value.capabilities.canGetTransform; + return ( {children} ); diff --git a/x-pack/plugins/transform/public/app/lib/authorization/components/common.ts b/x-pack/plugins/transform/public/app/lib/authorization/components/common.ts index d059f73a76137..d430a4d059e5c 100644 --- a/x-pack/plugins/transform/public/app/lib/authorization/components/common.ts +++ b/x-pack/plugins/transform/public/app/lib/authorization/components/common.ts @@ -16,6 +16,8 @@ export interface Capabilities { canPreviewTransform: boolean; canCreateTransform: boolean; canStartStopTransform: boolean; + canCreateTransformAlerts: boolean; + canUseTransformAlerts: boolean; } export type Privilege = [string, string]; @@ -67,6 +69,14 @@ export function createCapabilityFailureMessage( defaultMessage: 'You do not have permission to create transforms.', }); break; + case 'canCreateTransformAlerts': + message = i18n.translate( + 'xpack.transform.capability.noPermission.canCreateTransformAlertsTooltip', + { + defaultMessage: 'You do not have permission to create transform alert rules.', + } + ); + break; case 'canStartStopTransform': message = i18n.translate( 'xpack.transform.capability.noPermission.startOrStopTransformTooltip', diff --git a/x-pack/plugins/transform/public/app/mount_management_section.ts b/x-pack/plugins/transform/public/app/mount_management_section.ts index 1747330818547..6e63094064584 100644 --- a/x-pack/plugins/transform/public/app/mount_management_section.ts +++ b/x-pack/plugins/transform/public/app/mount_management_section.ts @@ -29,7 +29,7 @@ export async function mountManagementSection( const startServices = await getStartServices(); const [core, plugins] = startServices; const { application, chrome, docLinks, i18n, overlays, savedObjects, uiSettings } = core; - const { data, share, spaces } = plugins; + const { data, share, spaces, triggersActionsUi } = plugins; const { docTitle } = chrome; // Initialize services @@ -55,6 +55,7 @@ export async function mountManagementSection( share, spaces, ml: await getMlSharedImports(), + triggersActionsUi, }; const unmountAppCallback = renderApp(element, appDependencies); diff --git a/x-pack/plugins/transform/public/app/sections/clone_transform/clone_transform_section.tsx b/x-pack/plugins/transform/public/app/sections/clone_transform/clone_transform_section.tsx index 8aecf403186c5..218edb95c5f4f 100644 --- a/x-pack/plugins/transform/public/app/sections/clone_transform/clone_transform_section.tsx +++ b/x-pack/plugins/transform/public/app/sections/clone_transform/clone_transform_section.tsx @@ -21,7 +21,7 @@ import { } from '@elastic/eui'; import { APP_CREATE_TRANSFORM_CLUSTER_PRIVILEGES } from '../../../../common/constants'; -import { TransformPivotConfig } from '../../../../common/types/transform'; +import { TransformConfigUnion } from '../../../../common/types/transform'; import { isHttpFetchError } from '../../common/request'; import { useApi } from '../../hooks/use_api'; @@ -50,7 +50,7 @@ export const CloneTransformSection: FC = ({ match, location }) => { const transformId = match.params.transformId; - const [transformConfig, setTransformConfig] = useState(); + const [transformConfig, setTransformConfig] = useState(); const [errorMessage, setErrorMessage] = useState(); const [isInitialized, setIsInitialized] = useState(false); const { error: searchItemsError, searchItems, setSavedObjectId } = useSearchItems(undefined); diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_create/step_create_form.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_create/step_create_form.tsx index 7ccf986d5d497..859ea77ea5a14 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_create/step_create_form.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_create/step_create_form.tsx @@ -23,6 +23,7 @@ import { EuiText, } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; import { toMountPoint } from '../../../../../../../../../src/plugins/kibana_react/public'; import { @@ -52,7 +53,8 @@ import { } from '../../../../../../common/api_schemas/transforms'; import type { RuntimeField } from '../../../../../../../../../src/plugins/data/common'; import { isPopulatedObject } from '../../../../../../common/shared_imports'; -import { isLatestTransform } from '../../../../../../common/types/transform'; +import { isContinuousTransform, isLatestTransform } from '../../../../../../common/types/transform'; +import { TransformAlertFlyout } from '../../../../../alerting/transform_alerting_flyout'; export interface StepDetailsExposedState { created: boolean; @@ -86,6 +88,7 @@ export const StepCreateForm: FC = React.memo( const [loading, setLoading] = useState(false); const [created, setCreated] = useState(defaults.created); const [started, setStarted] = useState(defaults.started); + const [alertFlyoutVisible, setAlertFlyoutVisible] = useState(false); const [indexPatternId, setIndexPatternId] = useState(defaults.indexPatternId); const [progressPercentComplete, setProgressPercentComplete] = useState( undefined @@ -398,6 +401,31 @@ export const StepCreateForm: FC = React.memo( )} + {isContinuousTransform(transformConfig) && created ? ( + + + + + + + + + {i18n.translate('xpack.transform.stepCreateForm.createAlertRuleDescription', { + defaultMessage: + 'Opens a wizard to create an alert rule for monitoring transform health.', + })} + + + + ) : null} = React.memo( {i18n.translate('xpack.transform.stepCreateForm.createTransformDescription', { defaultMessage: - 'Create the transform without starting it. You will be able to start the transform later by returning to the transforms list.', + 'Creates the transform without starting it. You will be able to start the transform later by returning to the transforms list.', })} @@ -535,6 +563,12 @@ export const StepCreateForm: FC = React.memo( )} + {alertFlyoutVisible ? ( + + ) : null}
); } diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/common.ts b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/common.ts index fbe32e9bea12f..39b1a2de26f8e 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/common.ts +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/common.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { TransformId, TransformPivotConfig } from '../../../../../../common/types/transform'; +import type { TransformConfigUnion, TransformId } from '../../../../../../common/types/transform'; export type EsIndexName = string; export type IndexPatternTitle = string; @@ -55,7 +55,7 @@ export function getDefaultStepDetailsState(): StepDetailsExposedState { export function applyTransformConfigToDetailsState( state: StepDetailsExposedState, - transformConfig?: TransformPivotConfig + transformConfig?: TransformConfigUnion ): StepDetailsExposedState { // apply the transform configuration to wizard DETAILS state if (transformConfig !== undefined) { diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx index 0d39ec77d059f..7a47cc539c4aa 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx @@ -29,7 +29,7 @@ import { isEsIndices, isPostTransformsPreviewResponseSchema, } from '../../../../../../common/api_schemas/type_guards'; -import { TransformId, TransformPivotConfig } from '../../../../../../common/types/transform'; +import { TransformId } from '../../../../../../common/types/transform'; import { isValidIndexName } from '../../../../../../common/utils/es_utils'; import { getErrorMessage } from '../../../../../../common/utils/errors'; @@ -158,7 +158,7 @@ export const StepDetailsForm: FC = React.memo( ), }); } else { - setTransformIds(resp.transforms.map((transform: TransformPivotConfig) => transform.id)); + setTransformIds(resp.transforms.map((transform) => transform.id)); } const indices = await api.getEsIndices(); diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/wizard/wizard.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/wizard/wizard.tsx index 63e21e5d8aa14..27c43ed01a934 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/wizard/wizard.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/wizard/wizard.tsx @@ -11,7 +11,7 @@ import { i18n } from '@kbn/i18n'; import { EuiSteps, EuiStepStatus } from '@elastic/eui'; -import { TransformPivotConfig } from '../../../../../../common/types/transform'; +import type { TransformConfigUnion } from '../../../../../../common/types/transform'; import { getCreateTransformRequestBody } from '../../../../common'; import { SearchItems } from '../../../../hooks/use_search_items'; @@ -81,7 +81,7 @@ const StepDefine: FC = ({ }; interface WizardProps { - cloneConfig?: TransformPivotConfig; + cloneConfig?: TransformConfigUnion; searchItems: SearchItems; } diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_create_alert/create_alert_rule_action_name.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_create_alert/create_alert_rule_action_name.tsx new file mode 100644 index 0000000000000..c8d67a86d579a --- /dev/null +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_create_alert/create_alert_rule_action_name.tsx @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC } from 'react'; +import { EuiToolTip } from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; +import { createCapabilityFailureMessage } from '../../../../lib/authorization'; + +interface CreateAlertRuleActionProps { + disabled: boolean; +} + +export const crateAlertRuleActionNameText = i18n.translate( + 'xpack.transform.transformList.createAlertRuleNameText', + { + defaultMessage: 'Create alert rule', + } +); + +export const CreateAlertRuleActionName: FC = ({ disabled }) => { + if (disabled) { + return ( + + <>{crateAlertRuleActionNameText} + + ); + } + + return <>{crateAlertRuleActionNameText}; +}; diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_create_alert/index.ts b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_create_alert/index.ts new file mode 100644 index 0000000000000..80999d774bdcb --- /dev/null +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_create_alert/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { useCreateAlertRuleAction } from './use_create_alert_rule_action'; diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/action_create_alert/use_create_alert_rule_action.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_create_alert/use_create_alert_rule_action.tsx new file mode 100644 index 0000000000000..070f1eb08ac60 --- /dev/null +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/action_create_alert/use_create_alert_rule_action.tsx @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback, useContext, useMemo } from 'react'; +import { AuthorizationContext } from '../../../../lib/authorization'; +import { TransformListAction, TransformListRow } from '../../../../common'; +import { + crateAlertRuleActionNameText, + CreateAlertRuleActionName, +} from './create_alert_rule_action_name'; +import { useAlertRuleFlyout } from '../../../../../alerting/transform_alerting_flyout'; +import { isContinuousTransform } from '../../../../../../common/types/transform'; + +export type CreateAlertRuleAction = ReturnType; +export const useCreateAlertRuleAction = (forceDisable: boolean) => { + const { canCreateTransformAlerts } = useContext(AuthorizationContext).capabilities; + const { setCreateAlertRule } = useAlertRuleFlyout(); + + const clickHandler = useCallback( + (item: TransformListRow) => { + setCreateAlertRule(item.id); + }, + [setCreateAlertRule] + ); + + const action: TransformListAction = useMemo( + () => ({ + name: (item: TransformListRow) => ( + + ), + available: (item: TransformListRow) => isContinuousTransform(item.config), + enabled: () => canCreateTransformAlerts && !forceDisable, + description: crateAlertRuleActionNameText, + type: 'icon', + icon: 'bell', + onClick: clickHandler, + 'data-test-subj': 'transformActionCreateAlertRule', + }), + [canCreateTransformAlerts, forceDisable, clickHandler] + ); + + return { action }; +}; diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row.test.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row.test.tsx index bccd3aff72c58..af85049ce6915 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row.test.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row.test.tsx @@ -22,6 +22,7 @@ import { getMlSharedImports } from '../../../../../shared_imports'; // FLAKY https://github.com/elastic/kibana/issues/112922 describe.skip('Transform: Transform List ', () => { + const onAlertEdit = jest.fn(); // Set timezone to US/Eastern for consistent test results. beforeEach(() => { moment.tz.setDefault('US/Eastern'); @@ -38,7 +39,7 @@ describe.skip('Transform: Transform List ', () => { render( - + ); diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row.tsx index dff2ba17cb3f0..84110e67d701e 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row.tsx @@ -7,17 +7,18 @@ import React, { FC } from 'react'; -import { EuiTabbedContent } from '@elastic/eui'; +import { EuiButtonEmpty, EuiTabbedContent } from '@elastic/eui'; import { Optional } from '@kbn/utility-types'; import { i18n } from '@kbn/i18n'; import moment from 'moment-timezone'; import { TransformListRow } from '../../../../common'; import { useAppDependencies } from '../../../../app_dependencies'; -import { ExpandedRowDetailsPane, SectionConfig } from './expanded_row_details_pane'; +import { ExpandedRowDetailsPane, SectionConfig, SectionItem } from './expanded_row_details_pane'; import { ExpandedRowJsonPane } from './expanded_row_json_pane'; import { ExpandedRowMessagesPane } from './expanded_row_messages_pane'; import { ExpandedRowPreviewPane } from './expanded_row_preview_pane'; +import { TransformHealthAlertRule } from '../../../../../../common/types/alerting'; function getItemDescription(value: any) { if (typeof value === 'object') { @@ -44,18 +45,16 @@ export function stringHash(str: string): number { return hash < 0 ? hash * -2 : hash; } -interface Item { - title: string; - description: any; -} +type Item = SectionItem; interface Props { item: TransformListRow; + onAlertEdit: (alertRule: TransformHealthAlertRule) => void; } type StateValues = Optional; -export const ExpandedRow: FC = ({ item }) => { +export const ExpandedRow: FC = ({ item, onAlertEdit }) => { const { ml: { formatHumanReadableDateTimeSeconds }, } = useAppDependencies(); @@ -166,12 +165,40 @@ export const ExpandedRow: FC = ({ item }) => { } } + const alertRuleItems: Item[] | undefined = item.alerting_rules?.map((rule) => { + return { + title: ( + { + onAlertEdit(rule); + }} + flush="left" + size={'xs'} + iconSize={'s'} + > + {rule.name} + + ), + description: rule.executionStatus.status, + }; + }); + const checkpointing: SectionConfig = { title: 'Checkpointing', items: checkpointingItems, position: 'right', }; + const alertingRules: SectionConfig = { + title: i18n.translate('xpack.transform.transformList.transformDetails.alertRulesTitle', { + defaultMessage: 'Alert rules', + }), + items: alertRuleItems!, + position: 'right', + }; + const stats: SectionConfig = { title: 'Stats', items: Object.entries(item.stats.stats).map((s) => { @@ -192,7 +219,16 @@ export const ExpandedRow: FC = ({ item }) => { defaultMessage: 'Details', } ), - content: , + content: ( + + ), }, { id: `transform-stats-tab-${tabId}`, diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row_details_pane.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row_details_pane.tsx index 03e2fb2115d62..1b2dde0a2e576 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row_details_pane.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row_details_pane.tsx @@ -17,9 +17,10 @@ import { } from '@elastic/eui'; export interface SectionItem { - title: string; - description: string; + title: string | JSX.Element; + description: string | number | JSX.Element; } + export interface SectionConfig { title: string; position: 'left' | 'right'; diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_list.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_list.tsx index ab30f4793a315..8b7aaf1cf8fd2 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_list.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_list.tsx @@ -50,15 +50,18 @@ import { useColumns } from './use_columns'; import { ExpandedRow } from './expanded_row'; import { transformFilters, filterTransforms } from './transform_search_bar_filters'; import { useTableSettings } from './use_table_settings'; +import { useAlertRuleFlyout } from '../../../../../alerting/transform_alerting_flyout'; +import { TransformHealthAlertRule } from '../../../../../../common/types/alerting'; function getItemIdToExpandedRowMap( itemIds: TransformId[], - transforms: TransformListRow[] + transforms: TransformListRow[], + onAlertEdit: (alertRule: TransformHealthAlertRule) => void ): ItemIdToExpandedRowMap { return itemIds.reduce((m: ItemIdToExpandedRowMap, transformId: TransformId) => { const item = transforms.find((transform) => transform.config.id === transformId); if (item !== undefined) { - m[transformId] = ; + m[transformId] = ; } return m; }, {} as ItemIdToExpandedRowMap); @@ -79,6 +82,7 @@ export const TransformList: FC = ({ }) => { const [isLoading, setIsLoading] = useState(false); const { refresh } = useRefreshTransformList({ isLoading: setIsLoading }); + const { setEditAlertRule } = useAlertRuleFlyout(); const [filterActive, setFilterActive] = useState(false); @@ -171,7 +175,11 @@ export const TransformList: FC = ({ ); } - const itemIdToExpandedRowMap = getItemIdToExpandedRowMap(expandedRowItemIds, transforms); + const itemIdToExpandedRowMap = getItemIdToExpandedRowMap( + expandedRowItemIds, + transforms, + setEditAlertRule + ); const bulkActionMenuItems = [
diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_actions.test.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_actions.test.tsx index b7d5a2b7104ae..20d2f784a4d8b 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_actions.test.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_actions.test.tsx @@ -27,6 +27,7 @@ describe('Transform: Transform List Actions', () => { // in the runtime result here anyway. expect(actions.map((a: any) => a['data-test-subj'])).toStrictEqual([ 'transformActionDiscover', + 'transformActionCreateAlertRule', 'transformActionStart', 'transformActionStop', 'transformActionEdit', diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_actions.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_actions.tsx index 81e51cdafc32e..40b40cfa8c7ba 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_actions.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_actions.tsx @@ -18,6 +18,7 @@ import { EditTransformFlyout } from '../edit_transform_flyout'; import { useEditAction } from '../action_edit'; import { useStartAction, StartActionModal } from '../action_start'; import { useStopAction } from '../action_stop'; +import { useCreateAlertRuleAction } from '../action_create_alert'; export const useActions = ({ forceDisable, @@ -35,6 +36,7 @@ export const useActions = ({ const editAction = useEditAction(forceDisable, transformNodes); const startAction = useStartAction(forceDisable, transformNodes); const stopAction = useStopAction(forceDisable); + const createAlertRuleAction = useCreateAlertRuleAction(forceDisable); return { modals: ( @@ -52,6 +54,7 @@ export const useActions = ({ ), actions: [ discoverAction.action, + createAlertRuleAction.action, startAction.action, stopAction.action, editAction.action, diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_columns.test.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_columns.test.tsx index af2325ede2021..a26ccf0348c9a 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_columns.test.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_columns.test.tsx @@ -20,14 +20,15 @@ describe('Transform: Job List Columns', () => { const columns: ReturnType['columns'] = result.current.columns; - expect(columns).toHaveLength(8); + expect(columns).toHaveLength(9); expect(columns[0].isExpander).toBeTruthy(); expect(columns[1].name).toBe('ID'); - expect(columns[2].name).toBe('Description'); - expect(columns[3].name).toBe('Type'); - expect(columns[4].name).toBe('Status'); - expect(columns[5].name).toBe('Mode'); - expect(columns[6].name).toBe('Progress'); - expect(columns[7].name).toBe('Actions'); + expect(columns[2].id).toBe('alertRule'); + expect(columns[3].name).toBe('Description'); + expect(columns[4].name).toBe('Type'); + expect(columns[5].name).toBe('Status'); + expect(columns[6].name).toBe('Mode'); + expect(columns[7].name).toBe('Progress'); + expect(columns[8].name).toBe('Actions'); }); }); diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_columns.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_columns.tsx index dbdd3409c7e34..bad42c212293d 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_columns.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_columns.tsx @@ -21,6 +21,7 @@ import { EuiText, EuiToolTip, RIGHT_ALIGNMENT, + EuiIcon, } from '@elastic/eui'; import { @@ -95,6 +96,7 @@ export const useColumns = ( const columns: [ EuiTableComputedColumnType, EuiTableFieldDataColumnType, + EuiTableComputedColumnType, EuiTableFieldDataColumnType, EuiTableComputedColumnType, EuiTableComputedColumnType, @@ -143,6 +145,38 @@ export const useColumns = ( truncateText: true, scope: 'row', }, + { + id: 'alertRule', + name: ( + +

+ +

+
+ ), + width: '30px', + render: (item) => { + return Array.isArray(item.alerting_rules) ? ( + + } + > + + + ) : ( + + ); + }, + }, { field: TRANSFORM_LIST_COLUMN.DESCRIPTION, 'data-test-subj': 'transformListColumnDescription', diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/transform_management_section.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/transform_management_section.tsx index 2479d34f1579a..055e1e50701f8 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/transform_management_section.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/transform_management_section.tsx @@ -35,6 +35,11 @@ import { useRefreshInterval } from './components/transform_list/use_refresh_inte import { SearchSelection } from './components/search_selection'; import { TransformList } from './components/transform_list'; import { TransformStatsBar } from './components/transform_list/transforms_stats_bar'; +import { + AlertRulesManageContext, + getAlertRuleManageContext, + TransformAlertFlyoutWrapper, +} from '../../../alerting/transform_alerting_flyout'; export const TransformManagement: FC = () => { const { esTransform } = useDocumentationLinks(); @@ -149,12 +154,15 @@ export const TransformManagement: FC = () => { )} {typeof errorMessage === 'undefined' && ( - + + + + )} )} diff --git a/x-pack/plugins/transform/public/plugin.ts b/x-pack/plugins/transform/public/plugin.ts index da280452c1f0f..a7d0dce256640 100644 --- a/x-pack/plugins/transform/public/plugin.ts +++ b/x-pack/plugins/transform/public/plugin.ts @@ -16,7 +16,7 @@ import type { SharePluginStart } from 'src/plugins/share/public'; import type { SpacesApi } from '../../spaces/public'; import { registerFeature } from './register_feature'; import type { PluginSetupContract as AlertingSetup } from '../../alerting/public'; -import type { TriggersAndActionsUIPublicPluginSetup } from '../../triggers_actions_ui/public'; +import type { TriggersAndActionsUIPublicPluginStart } from '../../triggers_actions_ui/public'; import { getTransformHealthRuleType } from './alerting'; export interface PluginsDependencies { @@ -27,7 +27,7 @@ export interface PluginsDependencies { share: SharePluginStart; spaces?: SpacesApi; alerting?: AlertingSetup; - triggersActionsUi?: TriggersAndActionsUIPublicPluginSetup; + triggersActionsUi: TriggersAndActionsUIPublicPluginStart; } export class TransformUiPlugin { diff --git a/x-pack/plugins/transform/server/lib/alerting/transform_health_rule_type/transform_health_service.ts b/x-pack/plugins/transform/server/lib/alerting/transform_health_rule_type/transform_health_service.ts index 88b5396c7b110..eb51c04e0bca7 100644 --- a/x-pack/plugins/transform/server/lib/alerting/transform_health_rule_type/transform_health_service.ts +++ b/x-pack/plugins/transform/server/lib/alerting/transform_health_rule_type/transform_health_service.ts @@ -8,16 +8,21 @@ import { ElasticsearchClient } from 'kibana/server'; import { i18n } from '@kbn/i18n'; import type { Transform as EsTransform } from '@elastic/elasticsearch/api/types'; +import { keyBy } from 'lodash'; import { TransformHealthRuleParams } from './schema'; import { ALL_TRANSFORMS_SELECTION, TRANSFORM_HEALTH_CHECK_NAMES, + TRANSFORM_RULE_TYPE, } from '../../../../common/constants'; import { getResultTestConfig } from '../../../../common/utils/alerts'; import { NotStartedTransformResponse, TransformHealthAlertContext, } from './register_transform_health_rule_type'; +import type { RulesClient } from '../../../../../alerting/server'; +import type { TransformHealthAlertRule } from '../../../../common/types/alerting'; +import { isContinuousTransform } from '../../../../common/types/transform'; interface TestResult { name: string; @@ -27,37 +32,48 @@ interface TestResult { // @ts-ignore FIXME update types in the elasticsearch client type Transform = EsTransform & { id: string; description?: string; sync: object }; -export function transformHealthServiceProvider(esClient: ElasticsearchClient) { +type TransformWithAlertingRules = Transform & { alerting_rules: TransformHealthAlertRule[] }; + +export function transformHealthServiceProvider( + esClient: ElasticsearchClient, + rulesClient?: RulesClient +) { const transformsDict = new Map(); /** * Resolves result transform selection. * @param includeTransforms * @param excludeTransforms + * @param skipIDsCheck */ const getResultsTransformIds = async ( includeTransforms: string[], - excludeTransforms: string[] | null + excludeTransforms: string[] | null, + skipIDsCheck = false ): Promise => { const includeAll = includeTransforms.some((id) => id === ALL_TRANSFORMS_SELECTION); - // Fetch transforms to make sure assigned transforms exists. - const transformsResponse = ( - await esClient.transform.getTransform({ - ...(includeAll ? {} : { transform_id: includeTransforms.join(',') }), - allow_no_match: true, - size: 1000, - }) - ).body.transforms as Transform[]; - let resultTransformIds: string[] = []; - transformsResponse.forEach((t) => { - transformsDict.set(t.id, t); - if (t.sync) { - resultTransformIds.push(t.id); - } - }); + if (skipIDsCheck) { + resultTransformIds = includeTransforms; + } else { + // Fetch transforms to make sure assigned transforms exists. + const transformsResponse = ( + await esClient.transform.getTransform({ + ...(includeAll ? {} : { transform_id: includeTransforms.join(',') }), + allow_no_match: true, + size: 1000, + }) + ).body.transforms as Transform[]; + + transformsResponse.forEach((t) => { + transformsDict.set(t.id, t); + if (t.sync) { + resultTransformIds.push(t.id); + } + }); + } if (excludeTransforms && excludeTransforms.length > 0) { const excludeIdsSet = new Set(excludeTransforms); @@ -129,6 +145,53 @@ export function transformHealthServiceProvider(esClient: ElasticsearchClient) { return result; }, + + /** + * Updates transform list with associated alerting rules. + */ + async populateTransformsWithAssignedRules( + transforms: Transform[] + ): Promise { + const newList = transforms.filter(isContinuousTransform) as TransformWithAlertingRules[]; + + if (!rulesClient) { + throw new Error('Rules client is missing'); + } + + const transformMap = keyBy(newList, 'id'); + + const transformAlertingRules = await rulesClient.find({ + options: { + perPage: 1000, + filter: `alert.attributes.alertTypeId:${TRANSFORM_RULE_TYPE.TRANSFORM_HEALTH}`, + }, + }); + + for (const ruleInstance of transformAlertingRules.data) { + // Retrieve result transform IDs + const resultTransformIds: string[] = await getResultsTransformIds( + ruleInstance.params.includeTransforms.includes(ALL_TRANSFORMS_SELECTION) + ? Object.keys(transformMap) + : ruleInstance.params.includeTransforms, + ruleInstance.params.excludeTransforms, + true + ); + + resultTransformIds.forEach((transformId) => { + const transformRef = transformMap[transformId] as TransformWithAlertingRules; + + if (transformRef) { + if (Array.isArray(transformRef.alerting_rules)) { + transformRef.alerting_rules.push(ruleInstance); + } else { + transformRef.alerting_rules = [ruleInstance]; + } + } + }); + } + + return newList; + }, }; } diff --git a/x-pack/plugins/transform/server/routes/api/transforms.ts b/x-pack/plugins/transform/server/routes/api/transforms.ts index 76aac9686c37e..4a657ae615d94 100644 --- a/x-pack/plugins/transform/server/routes/api/transforms.ts +++ b/x-pack/plugins/transform/server/routes/api/transforms.ts @@ -63,6 +63,7 @@ import { registerTransformNodesRoutes } from './transforms_nodes'; import { IIndexPattern } from '../../../../../../src/plugins/data/common'; import { isLatestTransform } from '../../../common/types/transform'; import { isKeywordDuplicate } from '../../../common/utils/field_utils'; +import { transformHealthServiceProvider } from '../../lib/alerting/transform_health_rule_type/transform_health_service'; enum TRANSFORM_ACTIONS { STOP = 'stop', @@ -90,6 +91,17 @@ export function registerTransformsRoutes(routeDependencies: RouteDependencies) { size: 1000, ...req.params, }); + + if (ctx.alerting) { + const transformHealthService = transformHealthServiceProvider( + ctx.core.elasticsearch.client.asCurrentUser, + ctx.alerting.getRulesClient() + ); + + // @ts-ignore + await transformHealthService.populateTransformsWithAssignedRules(body.transforms); + } + return res.ok({ body }); } catch (e) { return res.customError(wrapError(wrapEsError(e))); diff --git a/x-pack/plugins/transform/server/services/license.ts b/x-pack/plugins/transform/server/services/license.ts index 978912ce08baf..ce28e0365bb21 100644 --- a/x-pack/plugins/transform/server/services/license.ts +++ b/x-pack/plugins/transform/server/services/license.ts @@ -15,6 +15,7 @@ import { } from 'kibana/server'; import { LicensingPluginSetup, LicenseType } from '../../../licensing/server'; +import type { AlertingApiRequestHandlerContext } from '../../../alerting/server'; export interface LicenseStatus { isValid: boolean; @@ -28,6 +29,10 @@ interface SetupSettings { defaultErrorMessage: string; } +type TransformRequestHandlerContext = RequestHandlerContext & { + alerting?: AlertingApiRequestHandlerContext; +}; + export class License { private licenseStatus: LicenseStatus = { isValid: false, @@ -64,7 +69,9 @@ export class License { }); } - guardApiRoute(handler: RequestHandler) { + guardApiRoute( + handler: RequestHandler + ) { const license = this; return function licenseCheck( diff --git a/x-pack/test/security_solution_endpoint/services/endpoint.ts b/x-pack/test/security_solution_endpoint/services/endpoint.ts index 2e774dcd84782..5bcc5c415a0db 100644 --- a/x-pack/test/security_solution_endpoint/services/endpoint.ts +++ b/x-pack/test/security_solution_endpoint/services/endpoint.ts @@ -18,7 +18,7 @@ import { IndexedHostsAndAlertsResponse, indexHostsAndAlerts, } from '../../../plugins/security_solution/common/endpoint/index_data'; -import { TransformPivotConfig } from '../../../plugins/transform/common/types/transform'; +import { TransformConfigUnion } from '../../../plugins/transform/common/types/transform'; import { GetTransformsResponseSchema } from '../../../plugins/transform/common/api_schemas/transforms'; import { catchAndWrapError } from '../../../plugins/security_solution/server/endpoint/utils'; import { installOrUpgradeEndpointFleetPackage } from '../../../plugins/security_solution/common/endpoint/data_loaders/setup_fleet_for_endpoint'; @@ -38,9 +38,9 @@ export class EndpointTestResources extends FtrService { * * @param [endpointPackageVersion] if set, it will be used to get the specific transform this this package version. Else just returns first one found */ - async getTransform(endpointPackageVersion?: string): Promise { + async getTransform(endpointPackageVersion?: string): Promise { const transformId = this.generateTransformId(endpointPackageVersion); - let transform: TransformPivotConfig | undefined; + let transform: TransformConfigUnion | undefined; if (endpointPackageVersion) { await this.transform.api.waitForTransformToExist(transformId); From adbb8088932ea7069381009a408d1d7cea24f5de Mon Sep 17 00:00:00 2001 From: Zacqary Adam Xeper Date: Tue, 19 Oct 2021 11:02:32 -0500 Subject: [PATCH 065/204] [Logs/Metrics UI] Add deprecated field configuration to Deprecations API (#115103) * [Logs/Metrics UI] Add deprecated field configuration to Deprecations API * Add correction steps * Add unit test for source config deprecations * Apply suggestions from code review Co-authored-by: Chris Cowan * Lint fix Co-authored-by: Chris Cowan --- .../plugins/infra/server/deprecations.test.ts | 86 +++++++ x-pack/plugins/infra/server/deprecations.ts | 211 ++++++++++++++++++ x-pack/plugins/infra/server/plugin.ts | 7 + 3 files changed, 304 insertions(+) create mode 100644 x-pack/plugins/infra/server/deprecations.test.ts create mode 100644 x-pack/plugins/infra/server/deprecations.ts diff --git a/x-pack/plugins/infra/server/deprecations.test.ts b/x-pack/plugins/infra/server/deprecations.test.ts new file mode 100644 index 0000000000000..318f1e50d6662 --- /dev/null +++ b/x-pack/plugins/infra/server/deprecations.test.ts @@ -0,0 +1,86 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getInfraDeprecationsFactory } from './deprecations'; + +describe('Infra plugin deprecations', () => { + describe('Source configuration deprecations', () => { + test('returns no deprecations when all fields are set to the default values', async () => { + const sources = { + getAllSourceConfigurations: () => [ + { + configuration: { + name: 'Default', + fields: { + timestamp: '@timestamp', + tiebreaker: '_doc', + container: 'container.id', + host: 'host.name', + pod: 'kubernetes.pod.uid', + }, + }, + }, + { + configuration: { + name: 'Alternate', + fields: { + timestamp: '@timestamp', + tiebreaker: '_doc', + container: 'container.id', + host: 'host.name', + pod: 'kubernetes.pod.uid', + }, + }, + }, + ], + }; + const getDeprecations = getInfraDeprecationsFactory(sources as any); + const deprecations = await getDeprecations({} as any); + expect(deprecations.length).toBe(0); + }); + }); + test('returns expected deprecations when some fields are not set to default values in one or more source configurations', async () => { + const sources = { + getAllSourceConfigurations: () => [ + { + configuration: { + name: 'Default', + fields: { + timestamp: 'not-@timestamp', + tiebreaker: '_doc', + container: 'not-container.id', + host: 'host.name', + pod: 'not-kubernetes.pod.uid', + }, + }, + }, + { + configuration: { + name: 'Alternate', + fields: { + timestamp: 'not-@timestamp', + tiebreaker: 'not-_doc', + container: 'container.id', + host: 'not-host.name', + pod: 'kubernetes.pod.uid', + }, + }, + }, + ], + }; + const getDeprecations = getInfraDeprecationsFactory(sources as any); + const deprecations = await getDeprecations({} as any); + expect(deprecations.length).toBe(5); + expect( + deprecations.map((d) => + d.title.replace(/Source configuration field "(.*)" is deprecated./, '$1') + ) + ).toEqual( + expect.arrayContaining(['timestamp', 'tiebreaker', 'container ID', 'host name', 'pod ID']) + ); + }); +}); diff --git a/x-pack/plugins/infra/server/deprecations.ts b/x-pack/plugins/infra/server/deprecations.ts new file mode 100644 index 0000000000000..27c2b235f769b --- /dev/null +++ b/x-pack/plugins/infra/server/deprecations.ts @@ -0,0 +1,211 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import { get } from 'lodash'; +import { + ConfigDeprecationProvider, + ConfigDeprecation, + DeprecationsDetails, + GetDeprecationsContext, +} from 'src/core/server'; +import { InfraSources } from './lib/sources'; + +const deprecatedFieldMessage = (fieldName: string, defaultValue: string, configNames: string[]) => + i18n.translate('xpack.infra.deprecations.deprecatedFieldDescription', { + defaultMessage: + 'Configuring the "{fieldName}" field has been deprecated and will be removed in 8.0.0. This plugin is designed to work with ECS, and expects this field to have a value of `{defaultValue}`. It has a different value configured in Source {configCount, plural, one {Configuration} other {Configurations}}: {configNames}', + values: { + fieldName, + defaultValue, + configNames: configNames.join(', '), + configCount: configNames.length, + }, + }); + +const DEFAULT_VALUES = { + timestamp: '@timestamp', + tiebreaker: '_doc', + container: 'container.id', + host: 'host.name', + pod: 'kubernetes.pod.uid', +}; + +const FIELD_DEPRECATION_FACTORIES: Record DeprecationsDetails> = + { + timestamp: (configNames) => ({ + level: 'critical', + title: i18n.translate('xpack.infra.deprecations.timestampFieldTitle', { + defaultMessage: 'Source configuration field "timestamp" is deprecated.', + }), + message: deprecatedFieldMessage( + i18n.translate('xpack.infra.deprecations.timestampFieldName', { + defaultMessage: 'timestamp', + }), + DEFAULT_VALUES.timestamp, + configNames + ), + correctiveActions: { + manualSteps: [ + i18n.translate('xpack.infra.deprecations.timestampAdjustIndexing', { + defaultMessage: 'Adjust your indexing to use "{field}" as a timestamp.', + values: { field: '@timestamp' }, + }), + ], + }, + }), + tiebreaker: (configNames) => ({ + level: 'critical', + title: i18n.translate('xpack.infra.deprecations.tiebreakerFieldTitle', { + defaultMessage: 'Source configuration field "tiebreaker" is deprecated.', + }), + message: deprecatedFieldMessage( + i18n.translate('xpack.infra.deprecations.tiebreakerFieldName', { + defaultMessage: 'tiebreaker', + }), + DEFAULT_VALUES.tiebreaker, + configNames + ), + correctiveActions: { + manualSteps: [ + i18n.translate('xpack.infra.deprecations.tiebreakerAdjustIndexing', { + defaultMessage: 'Adjust your indexing to use "{field}" as a tiebreaker.', + values: { field: '_doc' }, + }), + ], + }, + }), + host: (configNames) => ({ + level: 'critical', + title: i18n.translate('xpack.infra.deprecations.hostnameFieldTitle', { + defaultMessage: 'Source configuration field "host name" is deprecated.', + }), + message: deprecatedFieldMessage( + i18n.translate('xpack.infra.deprecations.hostnameFieldName', { + defaultMessage: 'host name', + }), + DEFAULT_VALUES.host, + configNames + ), + correctiveActions: { + manualSteps: [ + i18n.translate('xpack.infra.deprecations.hostAdjustIndexing', { + defaultMessage: 'Adjust your indexing to identify hosts using "{field}"', + values: { field: 'host.name' }, + }), + ], + }, + }), + pod: (configNames) => ({ + level: 'critical', + title: i18n.translate('xpack.infra.deprecations.podIdFieldTitle', { + defaultMessage: 'Source configuration field "pod ID" is deprecated.', + }), + message: deprecatedFieldMessage( + i18n.translate('xpack.infra.deprecations.podIdFieldName', { defaultMessage: 'pod ID' }), + DEFAULT_VALUES.pod, + configNames + ), + correctiveActions: { + manualSteps: [ + i18n.translate('xpack.infra.deprecations.podAdjustIndexing', { + defaultMessage: 'Adjust your indexing to identify Kubernetes pods using "{field}"', + values: { field: 'kubernetes.pod.uid' }, + }), + ], + }, + }), + container: (configNames) => ({ + level: 'critical', + title: i18n.translate('xpack.infra.deprecations.containerIdFieldTitle', { + defaultMessage: 'Source configuration field "container ID" is deprecated.', + }), + message: deprecatedFieldMessage( + i18n.translate('xpack.infra.deprecations.containerIdFieldName', { + defaultMessage: 'container ID', + }), + DEFAULT_VALUES.container, + configNames + ), + correctiveActions: { + manualSteps: [ + i18n.translate('xpack.infra.deprecations.containerAdjustIndexing', { + defaultMessage: 'Adjust your indexing to identify Docker containers using "{field}"', + values: { field: 'container.id' }, + }), + ], + }, + }), + }; + +export const configDeprecations: ConfigDeprecationProvider = () => [ + ...Object.keys(FIELD_DEPRECATION_FACTORIES).map( + (key): ConfigDeprecation => + (completeConfig, rootPath, addDeprecation) => { + const configuredValue = get(completeConfig, `xpack.infra.sources.default.fields.${key}`); + if (typeof configuredValue === 'undefined') { + return completeConfig; + } + addDeprecation({ + title: i18n.translate('xpack.infra.deprecations.deprecatedFieldConfigTitle', { + defaultMessage: '"{fieldKey}" is deprecated.', + values: { + fieldKey: key, + }, + }), + message: i18n.translate('xpack.infra.deprecations.deprecatedFieldConfigDescription', { + defaultMessage: + 'Configuring "xpack.infra.sources.default.fields.{fieldKey}" has been deprecated and will be removed in 8.0.0.', + values: { + fieldKey: key, + }, + }), + level: 'warning', + correctiveActions: { + manualSteps: [ + i18n.translate('xpack.infra.deprecations.removeConfigField', { + defaultMessage: + 'Remove "xpack.infra.sources.default.fields.{fieldKey}" from your Kibana configuration.', + values: { fieldKey: key }, + }), + ], + }, + } as Parameters[0]); + + return completeConfig; + } + ), +]; + +export const getInfraDeprecationsFactory = + (sources: InfraSources) => + async ({ savedObjectsClient }: GetDeprecationsContext) => { + const deprecatedFieldsToSourceConfigMap: Map = new Map(); + const sourceConfigurations = await sources.getAllSourceConfigurations(savedObjectsClient); + + for (const { configuration } of sourceConfigurations) { + const { name, fields } = configuration; + for (const [key, defaultValue] of Object.entries(DEFAULT_VALUES)) { + const configuredValue = Reflect.get(fields, key); + if (configuredValue !== defaultValue) { + const affectedConfigNames = deprecatedFieldsToSourceConfigMap.get(key) ?? []; + affectedConfigNames.push(name); + deprecatedFieldsToSourceConfigMap.set(key, affectedConfigNames); + } + } + } + + const deprecations: DeprecationsDetails[] = []; + if (deprecatedFieldsToSourceConfigMap.size > 0) { + for (const [fieldName, affectedConfigNames] of deprecatedFieldsToSourceConfigMap.entries()) { + const deprecationFactory = Reflect.get(FIELD_DEPRECATION_FACTORIES, fieldName); + deprecations.push(deprecationFactory(affectedConfigNames)); + } + } + + return deprecations; + }; diff --git a/x-pack/plugins/infra/server/plugin.ts b/x-pack/plugins/infra/server/plugin.ts index d1ea60dd23dfc..bf9c5a152058e 100644 --- a/x-pack/plugins/infra/server/plugin.ts +++ b/x-pack/plugins/infra/server/plugin.ts @@ -40,6 +40,7 @@ import { UsageCollector } from './usage/usage_collector'; import { createGetLogQueryFields } from './services/log_queries/get_log_query_fields'; import { handleEsError } from '../../../../src/plugins/es_ui_shared/server'; import { RulesService } from './services/rules'; +import { configDeprecations, getInfraDeprecationsFactory } from './deprecations'; export const config: PluginConfigDescriptor = { schema: schema.object({ @@ -67,6 +68,7 @@ export const config: PluginConfigDescriptor = { }) ), }), + deprecations: configDeprecations, }; export type InfraConfig = TypeOf; @@ -191,6 +193,11 @@ export class InfraServerPlugin implements Plugin { const logEntriesService = new LogEntriesService(); logEntriesService.setup(core, { ...plugins, sources }); + // register deprecated source configuration fields + core.deprecations.registerDeprecations({ + getDeprecations: getInfraDeprecationsFactory(sources), + }); + return { defineInternalSourceConfiguration(sourceId, sourceProperties) { sources.defineInternalSourceConfiguration(sourceId, sourceProperties); From 9caab5858d62dd662321049e0d6bd0b13d597614 Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Tue, 19 Oct 2021 19:05:53 +0300 Subject: [PATCH 066/204] [i18n] remove i18n html extractor (#115004) --- .../test_plugin_1/test_file_1.jsx | 13 +- .../test_plugin_1/test_file_4.html | 8 - .../test_plugin_2/test_file.html | 1 - .../test_plugin_2/test_file.jsx | 8 + .../test_file.jsx | 0 .../test_file.jsx | 8 - .../extract_default_translations.test.js.snap | 22 +- src/dev/i18n/extract_default_translations.js | 27 +- .../i18n/extract_default_translations.test.js | 22 +- .../__snapshots__/html.test.js.snap | 77 ------ src/dev/i18n/extractors/html.js | 252 ------------------ src/dev/i18n/extractors/html.test.js | 103 ------- src/dev/i18n/extractors/index.js | 1 - x-pack/.i18nrc.json | 2 +- 14 files changed, 36 insertions(+), 508 deletions(-) delete mode 100644 src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_1/test_file_4.html delete mode 100644 src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_2/test_file.html create mode 100644 src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_2/test_file.jsx rename src/dev/i18n/__fixtures__/extract_default_translations/{test_plugin_3 => test_plugin_2_additional_path}/test_file.jsx (100%) delete mode 100644 src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_3_additional_path/test_file.jsx delete mode 100644 src/dev/i18n/extractors/__snapshots__/html.test.js.snap delete mode 100644 src/dev/i18n/extractors/html.js delete mode 100644 src/dev/i18n/extractors/html.test.js diff --git a/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_1/test_file_1.jsx b/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_1/test_file_1.jsx index a52047f00feb0..8dc47a53da421 100644 --- a/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_1/test_file_1.jsx +++ b/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_1/test_file_1.jsx @@ -1,11 +1,8 @@ /* eslint-disable */ -// Angular service -i18n('plugin_1.id_1', { defaultMessage: 'Message 1' }); - // @kbn/i18n -i18n.translate('plugin_1.id_2', { - defaultMessage: 'Message 2', +i18n.translate('plugin_1.id_1', { + defaultMessage: 'Message 1', description: 'Message description', }); @@ -15,10 +12,10 @@ class Component extends PureComponent { return (
- {intl.formatMessage({ id: 'plugin_1.id_4', defaultMessage: 'Message 4' })} + {intl.formatMessage({ id: 'plugin_1.id_3', defaultMessage: 'Message 3' })}
); } diff --git a/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_1/test_file_4.html b/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_1/test_file_4.html deleted file mode 100644 index f9c8a8383d647..0000000000000 --- a/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_1/test_file_4.html +++ /dev/null @@ -1,8 +0,0 @@ - -
-
-
-
diff --git a/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_2/test_file.html b/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_2/test_file.html deleted file mode 100644 index c12843602b13b..0000000000000 --- a/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_2/test_file.html +++ /dev/null @@ -1 +0,0 @@ -

{{ ::'plugin_2.message-id' | i18n: { defaultMessage: 'Message text' } }}

diff --git a/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_2/test_file.jsx b/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_2/test_file.jsx new file mode 100644 index 0000000000000..8f41a58bf82d1 --- /dev/null +++ b/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_2/test_file.jsx @@ -0,0 +1,8 @@ +/* eslint-disable */ + +i18n.translate('plugin_2.duplicate_id', { defaultMessage: 'Message 1' }); + +i18n.translate('plugin_2.duplicate_id', { + defaultMessage: 'Message 2', + description: 'Message description', +}); diff --git a/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_3/test_file.jsx b/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_2_additional_path/test_file.jsx similarity index 100% rename from src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_3/test_file.jsx rename to src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_2_additional_path/test_file.jsx diff --git a/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_3_additional_path/test_file.jsx b/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_3_additional_path/test_file.jsx deleted file mode 100644 index 7fa370dec5ebb..0000000000000 --- a/src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_3_additional_path/test_file.jsx +++ /dev/null @@ -1,8 +0,0 @@ -/* eslint-disable */ - -i18n('plugin_3.duplicate_id', { defaultMessage: 'Message 1' }); - -i18n.translate('plugin_3.duplicate_id', { - defaultMessage: 'Message 2', - description: 'Message description', -}); diff --git a/src/dev/i18n/__snapshots__/extract_default_translations.test.js.snap b/src/dev/i18n/__snapshots__/extract_default_translations.test.js.snap index b19b366a8db7b..c9215b9aed98b 100644 --- a/src/dev/i18n/__snapshots__/extract_default_translations.test.js.snap +++ b/src/dev/i18n/__snapshots__/extract_default_translations.test.js.snap @@ -5,14 +5,14 @@ Array [ Array [ "plugin_1.id_1", Object { - "description": undefined, + "description": "Message description", "message": "Message 1", }, ], Array [ "plugin_1.id_2", Object { - "description": "Message description", + "description": undefined, "message": "Message 2", }, ], @@ -23,27 +23,13 @@ Array [ "message": "Message 3", }, ], - Array [ - "plugin_1.id_4", - Object { - "description": undefined, - "message": "Message 4", - }, - ], - Array [ - "plugin_1.id_7", - Object { - "description": undefined, - "message": "Message 7", - }, - ], ] `; exports[`dev/i18n/extract_default_translations throws on id collision 1`] = ` Array [ - " I18N ERROR  Error in src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_3/test_file.jsx -Error: There is more than one default message for the same id \\"plugin_3.duplicate_id\\": + " I18N ERROR  Error in src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_2/test_file.jsx +Error: There is more than one default message for the same id \\"plugin_2.duplicate_id\\": \\"Message 1\\" and \\"Message 2\\"", ] `; diff --git a/src/dev/i18n/extract_default_translations.js b/src/dev/i18n/extract_default_translations.js index 97554704edc7f..a453b0bbae2fb 100644 --- a/src/dev/i18n/extract_default_translations.js +++ b/src/dev/i18n/extract_default_translations.js @@ -8,7 +8,7 @@ import path from 'path'; -import { extractHtmlMessages, extractCodeMessages } from './extractors'; +import { extractCodeMessages } from './extractors'; import { globAsync, readFileAsync, normalizePath } from './utils'; import { createFailError, isFailError } from '@kbn/dev-utils'; @@ -59,7 +59,7 @@ export async function matchEntriesWithExctractors(inputPath, options = {}) { '**/*.d.ts', ].concat(additionalIgnore); - const entries = await globAsync('*.{js,jsx,ts,tsx,html}', { + const entries = await globAsync('*.{js,jsx,ts,tsx}', { cwd: inputPath, matchBase: true, ignore, @@ -67,25 +67,14 @@ export async function matchEntriesWithExctractors(inputPath, options = {}) { absolute, }); - const { htmlEntries, codeEntries } = entries.reduce( - (paths, entry) => { - const resolvedPath = path.resolve(inputPath, entry); + const codeEntries = entries.reduce((paths, entry) => { + const resolvedPath = path.resolve(inputPath, entry); + paths.push(resolvedPath); - if (resolvedPath.endsWith('.html')) { - paths.htmlEntries.push(resolvedPath); - } else { - paths.codeEntries.push(resolvedPath); - } - - return paths; - }, - { htmlEntries: [], codeEntries: [] } - ); + return paths; + }, []); - return [ - [htmlEntries, extractHtmlMessages], - [codeEntries, extractCodeMessages], - ]; + return [[codeEntries, extractCodeMessages]]; } export async function extractMessagesFromPathToMap(inputPath, targetMap, config, reporter) { diff --git a/src/dev/i18n/extract_default_translations.test.js b/src/dev/i18n/extract_default_translations.test.js index 4b0da570ca551..e5b33eba7a4db 100644 --- a/src/dev/i18n/extract_default_translations.test.js +++ b/src/dev/i18n/extract_default_translations.test.js @@ -18,17 +18,15 @@ const fixturesPath = path.resolve(__dirname, '__fixtures__', 'extract_default_tr const pluginsPaths = [ path.join(fixturesPath, 'test_plugin_1'), path.join(fixturesPath, 'test_plugin_2'), - path.join(fixturesPath, 'test_plugin_3'), - path.join(fixturesPath, 'test_plugin_3_additional_path'), + path.join(fixturesPath, 'test_plugin_2_additional_path'), ]; const config = { paths: { plugin_1: ['src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_1'], - plugin_2: ['src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_2'], - plugin_3: [ - 'src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_3', - 'src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_3_additional_path', + plugin_2: [ + 'src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_2', + 'src/dev/i18n/__fixtures__/extract_default_translations/test_plugin_2_additional_path', ], }, exclude: [], @@ -44,7 +42,7 @@ describe('dev/i18n/extract_default_translations', () => { }); test('throws on id collision', async () => { - const [, , pluginPath] = pluginsPaths; + const [, pluginPath] = pluginsPaths; const reporter = new ErrorReporter(); await expect( @@ -57,20 +55,20 @@ describe('dev/i18n/extract_default_translations', () => { const id = 'plugin_2.message-id'; const filePath = path.resolve( __dirname, - '__fixtures__/extract_default_translations/test_plugin_2/test_file.html' + '__fixtures__/extract_default_translations/test_plugin_2/test_file.jsx' ); expect(() => validateMessageNamespace(id, filePath, config.paths)).not.toThrow(); }); test('validates message namespace with multiple paths', () => { - const id = 'plugin_3.message-id'; + const id = 'plugin_2.message-id'; const filePath1 = path.resolve( __dirname, - '__fixtures__/extract_default_translations/test_plugin_3/test_file.html' + '__fixtures__/extract_default_translations/test_plugin_2/test_file.jsx' ); const filePath2 = path.resolve( __dirname, - '__fixtures__/extract_default_translations/test_plugin_3_additional_path/test_file.html' + '__fixtures__/extract_default_translations/test_plugin_2_additional_path/test_file.jsx' ); expect(() => validateMessageNamespace(id, filePath1, config.paths)).not.toThrow(); expect(() => validateMessageNamespace(id, filePath2, config.paths)).not.toThrow(); @@ -81,7 +79,7 @@ describe('dev/i18n/extract_default_translations', () => { const id = 'wrong_plugin_namespace.message-id'; const filePath = path.resolve( __dirname, - '__fixtures__/extract_default_translations/test_plugin_2/test_file.html' + '__fixtures__/extract_default_translations/test_plugin_2/test_file.jsx' ); expect(() => validateMessageNamespace(id, filePath, config.paths, { report })).not.toThrow(); diff --git a/src/dev/i18n/extractors/__snapshots__/html.test.js.snap b/src/dev/i18n/extractors/__snapshots__/html.test.js.snap deleted file mode 100644 index f911674400d45..0000000000000 --- a/src/dev/i18n/extractors/__snapshots__/html.test.js.snap +++ /dev/null @@ -1,77 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`dev/i18n/extractors/html extracts default messages from HTML 1`] = ` -Array [ - Array [ - "kbn.dashboard.id-1", - Object { - "description": "Message description 1", - "message": "Message text 1 {value}", - }, - ], - Array [ - "kbn.dashboard.id-2", - Object { - "description": undefined, - "message": "Message text 2", - }, - ], - Array [ - "kbn.dashboard.id-3", - Object { - "description": "Message description 3", - "message": "Message text 3", - }, - ], -] -`; - -exports[`dev/i18n/extractors/html extracts default messages from HTML with one-time binding 1`] = ` -Array [ - Array [ - "kbn.id", - Object { - "description": undefined, - "message": "Message text with {value}", - }, - ], -] -`; - -exports[`dev/i18n/extractors/html extracts message from i18n filter in interpolating directive 1`] = ` -Array [ - Array [ - "namespace.messageId", - Object { - "description": undefined, - "message": "Message", - }, - ], -] -`; - -exports[`dev/i18n/extractors/html throws on empty i18n-id 1`] = ` -Array [ - Array [ - [Error: Empty "i18n-id" value in angular directive is not allowed.], - ], -] -`; - -exports[`dev/i18n/extractors/html throws on i18n filter usage in complex angular expression 1`] = ` -Array [ - Array [ - [Error: Couldn't parse angular i18n expression: -Missing semicolon. (1:5): - mode as ('metricVis.colorModes.' + mode], - ], -] -`; - -exports[`dev/i18n/extractors/html throws on missing i18n-default-message attribute 1`] = ` -Array [ - Array [ - [Error: Empty defaultMessage in angular directive is not allowed ("message-id").], - ], -] -`; diff --git a/src/dev/i18n/extractors/html.js b/src/dev/i18n/extractors/html.js deleted file mode 100644 index 922f67ac2fb09..0000000000000 --- a/src/dev/i18n/extractors/html.js +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import cheerio from 'cheerio'; -import { parse } from '@babel/parser'; -import { isObjectExpression, isStringLiteral } from '@babel/types'; - -import { - isPropertyWithKey, - formatHTMLString, - formatJSString, - traverseNodes, - checkValuesProperty, - createParserErrorMessage, - extractMessageValueFromNode, - extractValuesKeysFromNode, - extractDescriptionValueFromNode, -} from '../utils'; -import { DEFAULT_MESSAGE_KEY, DESCRIPTION_KEY, VALUES_KEY } from '../constants'; -import { createFailError, isFailError } from '@kbn/dev-utils'; - -/** - * Find all substrings of "{{ any text }}" pattern allowing '{' and '}' chars in single quote strings - * - * Example: `{{ ::'message.id' | i18n: { defaultMessage: 'Message with {{curlyBraces}}' } }}` - */ -const ANGULAR_EXPRESSION_REGEX = /{{([^{}]|({([^']|('([^']|(\\'))*'))*?}))*}}+/g; - -const LINEBREAK_REGEX = /\n/g; -const I18N_FILTER_MARKER = '| i18n: '; - -function parseExpression(expression) { - let ast; - - try { - ast = parse(`+${expression}`.replace(LINEBREAK_REGEX, ' ')); - } catch (error) { - if (error instanceof SyntaxError) { - const errorWithContext = createParserErrorMessage(` ${expression}`, error); - throw createFailError(`Couldn't parse angular i18n expression:\n${errorWithContext}`); - } - } - - return ast; -} - -/** - * Extract default message from an angular filter expression argument - * @param {string} expression JavaScript code containing a filter object - * @param {string} messageId id of the message - * @returns {{ message?: string, description?: string, valuesKeys: string[]] }} - */ -function parseFilterObjectExpression(expression, messageId) { - const ast = parseExpression(expression); - const objectExpressionNode = [...traverseNodes(ast.program.body)].find((node) => - isObjectExpression(node) - ); - - if (!objectExpressionNode) { - return {}; - } - - const [messageProperty, descriptionProperty, valuesProperty] = [ - DEFAULT_MESSAGE_KEY, - DESCRIPTION_KEY, - VALUES_KEY, - ].map((key) => - objectExpressionNode.properties.find((property) => isPropertyWithKey(property, key)) - ); - - const message = messageProperty - ? formatJSString(extractMessageValueFromNode(messageProperty.value, messageId)) - : undefined; - - const description = descriptionProperty - ? formatJSString(extractDescriptionValueFromNode(descriptionProperty.value, messageId)) - : undefined; - - const valuesKeys = valuesProperty - ? extractValuesKeysFromNode(valuesProperty.value, messageId) - : []; - - return { message, description, valuesKeys }; -} - -function parseIdExpression(expression) { - const ast = parseExpression(expression); - const stringNode = [...traverseNodes(ast.program.body)].find((node) => isStringLiteral(node)); - - if (!stringNode) { - throw createFailError(`Message id should be a string literal, but got: \n${expression}`); - } - - return stringNode ? formatJSString(stringNode.value) : null; -} - -function trimCurlyBraces(string) { - if (string.startsWith('{{') && string.endsWith('}}')) { - return string.slice(2, -2).trim(); - } - - return string; -} - -/** - * Removes one-time binding operator `::` from the start of a string. - * - * Example: `::'id' | i18n: { defaultMessage: 'Message' }` - * @param {string} string string to trim - */ -function trimOneTimeBindingOperator(string) { - if (string.startsWith('::')) { - return string.slice(2); - } - - return string; -} - -function* extractExpressions(htmlContent) { - const elements = cheerio.load(htmlContent)('*').toArray(); - - for (const element of elements) { - for (const node of element.children) { - if (node.type === 'text') { - yield* (node.data.match(ANGULAR_EXPRESSION_REGEX) || []) - .filter((expression) => expression.includes(I18N_FILTER_MARKER)) - .map(trimCurlyBraces); - } - } - - for (const attribute of Object.values(element.attribs)) { - if (attribute.includes(I18N_FILTER_MARKER)) { - yield trimCurlyBraces(attribute); - } - } - } -} - -function* getFilterMessages(htmlContent, reporter) { - for (const expression of extractExpressions(htmlContent)) { - const filterStart = expression.indexOf(I18N_FILTER_MARKER); - - const idExpression = trimOneTimeBindingOperator(expression.slice(0, filterStart).trim()); - const filterObjectExpression = expression.slice(filterStart + I18N_FILTER_MARKER.length).trim(); - - try { - if (!filterObjectExpression || !idExpression) { - throw createFailError(`Cannot parse i18n filter expression: ${expression}`); - } - - const messageId = parseIdExpression(idExpression); - - if (!messageId) { - throw createFailError('Empty "id" value in angular filter expression is not allowed.'); - } - - const { message, description, valuesKeys } = parseFilterObjectExpression( - filterObjectExpression, - messageId - ); - - if (!message) { - throw createFailError( - `Empty defaultMessage in angular filter expression is not allowed ("${messageId}").` - ); - } - - checkValuesProperty(valuesKeys, message, messageId); - - yield [messageId, { message, description }]; - } catch (error) { - if (!isFailError(error)) { - throw error; - } - - reporter.report(error); - } - } -} - -function* getDirectiveMessages(htmlContent, reporter) { - const $ = cheerio.load(htmlContent); - - const elements = $('[i18n-id]') - .map((idx, el) => { - const $el = $(el); - - return { - id: $el.attr('i18n-id'), - defaultMessage: $el.attr('i18n-default-message'), - description: $el.attr('i18n-description'), - values: $el.attr('i18n-values'), - }; - }) - .toArray(); - - for (const element of elements) { - const messageId = formatHTMLString(element.id); - if (!messageId) { - reporter.report( - createFailError('Empty "i18n-id" value in angular directive is not allowed.') - ); - continue; - } - - const message = formatHTMLString(element.defaultMessage); - if (!message) { - reporter.report( - createFailError( - `Empty defaultMessage in angular directive is not allowed ("${messageId}").` - ) - ); - continue; - } - - try { - if (element.values) { - const ast = parseExpression(element.values); - const valuesObjectNode = [...traverseNodes(ast.program.body)].find((node) => - isObjectExpression(node) - ); - const valuesKeys = extractValuesKeysFromNode(valuesObjectNode); - - checkValuesProperty(valuesKeys, message, messageId); - } else { - checkValuesProperty([], message, messageId); - } - - yield [ - messageId, - { message, description: formatHTMLString(element.description) || undefined }, - ]; - } catch (error) { - if (!isFailError(error)) { - throw error; - } - - reporter.report(error); - } - } -} - -export function* extractHtmlMessages(buffer, reporter) { - const content = buffer.toString(); - yield* getDirectiveMessages(content, reporter); - yield* getFilterMessages(content, reporter); -} diff --git a/src/dev/i18n/extractors/html.test.js b/src/dev/i18n/extractors/html.test.js deleted file mode 100644 index 5f0c7ac39e8f6..0000000000000 --- a/src/dev/i18n/extractors/html.test.js +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { extractHtmlMessages } from './html'; - -const htmlSourceBuffer = Buffer.from(` -
-
-

-
-
- {{ 'kbn.dashboard.id-2' | i18n: { defaultMessage: 'Message text 2' } }} -
-
- {{ 'kbn.dashboard.id-3' | i18n: { defaultMessage: 'Message text 3', description: 'Message description 3' } }} -
-
-`); - -const report = jest.fn(); - -describe('dev/i18n/extractors/html', () => { - beforeEach(() => { - report.mockClear(); - }); - - test('extracts default messages from HTML', () => { - const actual = Array.from(extractHtmlMessages(htmlSourceBuffer)); - expect(actual.sort()).toMatchSnapshot(); - }); - - test('extracts default messages from HTML with one-time binding', () => { - const actual = Array.from( - extractHtmlMessages(` -
- {{::'kbn.id' | i18n: { defaultMessage: 'Message text with {value}', values: { value: 'value' } }}} -
-`) - ); - expect(actual.sort()).toMatchSnapshot(); - }); - - test('throws on empty i18n-id', () => { - const source = Buffer.from(`\ -

-`); - - expect(() => extractHtmlMessages(source, { report }).next()).not.toThrow(); - expect(report.mock.calls).toMatchSnapshot(); - }); - - test('throws on missing i18n-default-message attribute', () => { - const source = Buffer.from(`\ -

-`); - - expect(() => extractHtmlMessages(source, { report }).next()).not.toThrow(); - expect(report.mock.calls).toMatchSnapshot(); - }); - - test('throws on i18n filter usage in complex angular expression', () => { - const source = Buffer.from(`\ -
-`); - - expect(() => extractHtmlMessages(source, { report }).next()).not.toThrow(); - expect(report.mock.calls).toMatchSnapshot(); - }); - - test('extracts message from i18n filter in interpolating directive', () => { - const source = Buffer.from(` - -`); - - expect(Array.from(extractHtmlMessages(source))).toMatchSnapshot(); - }); -}); diff --git a/src/dev/i18n/extractors/index.js b/src/dev/i18n/extractors/index.js index dc269586f5abe..601a080c80b1b 100644 --- a/src/dev/i18n/extractors/index.js +++ b/src/dev/i18n/extractors/index.js @@ -7,4 +7,3 @@ */ export { extractCodeMessages } from './code'; -export { extractHtmlMessages } from './html'; diff --git a/x-pack/.i18nrc.json b/x-pack/.i18nrc.json index 372812d4d0dc1..b51363f1b7006 100644 --- a/x-pack/.i18nrc.json +++ b/x-pack/.i18nrc.json @@ -64,7 +64,7 @@ "xpack.observability": "plugins/observability", "xpack.banners": "plugins/banners" }, - "exclude": ["examples", "plugins/monitoring/public/angular/angular_i18n"], + "exclude": ["examples"], "translations": [ "plugins/translations/translations/zh-CN.json", "plugins/translations/translations/ja-JP.json" From b7577b5695eebf8e62f151bad7559bc59c346804 Mon Sep 17 00:00:00 2001 From: Jason Stoltzfus Date: Tue, 19 Oct 2021 12:09:51 -0400 Subject: [PATCH 067/204] [App Search] Wired up organic results on Curation Suggestions view (#114717) --- .../app_search/components/curations/types.ts | 10 +- .../curation_suggestion.test.tsx | 129 +++-- .../curation_suggestion.tsx | 37 +- .../curation_suggestion_logic.test.ts | 259 ++++------ .../curation_suggestion_logic.ts | 147 ++---- .../views/curation_suggestion/temp_data.ts | 470 ------------------ .../search_relevance_suggestions.test.ts | 7 +- .../search_relevance_suggestions.ts | 15 +- 8 files changed, 274 insertions(+), 800 deletions(-) delete mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/temp_data.ts diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/types.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/types.ts index 7479505ea86da..b67664d8efde2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/types.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/types.ts @@ -13,11 +13,19 @@ export interface CurationSuggestion { updated_at: string; promoted: string[]; status: 'pending' | 'applied' | 'automated' | 'rejected' | 'disabled'; - curation_id?: string; + curation_id?: string; // The id of an existing curation that this suggestion would affect operation: 'create' | 'update' | 'delete'; override_manual_curation?: boolean; } +// A curation suggestion with linked ids hydrated with actual values +export interface HydratedCurationSuggestion + extends Omit { + organic: Curation['organic']; + promoted: Curation['promoted']; + curation?: Curation; +} + export interface Curation { id: string; last_updated: string; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_suggestion.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_suggestion.test.tsx index 1c3f4645d89e9..604d2930a4b5d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_suggestion.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_suggestion.test.tsx @@ -14,6 +14,8 @@ import React from 'react'; import { shallow } from 'enzyme'; +import { EuiEmptyPrompt } from '@elastic/eui'; + import { AppSearchPageTemplate } from '../../../layout'; import { Result } from '../../../result'; @@ -26,44 +28,29 @@ describe('CurationSuggestion', () => { suggestion: { query: 'foo', updated_at: '2021-07-08T14:35:50Z', - promoted: ['1', '2', '3'], - }, - suggestedPromotedDocuments: [ - { - id: { - raw: '1', - }, - _meta: { - id: '1', - engine: 'some-engine', - }, - }, - { - id: { - raw: '2', - }, - _meta: { - id: '2', - engine: 'some-engine', - }, - }, - { - id: { - raw: '3', - }, - _meta: { - id: '3', - engine: 'some-engine', - }, - }, - ], - curation: { - promoted: [ + promoted: [{ id: '4', foo: 'foo' }], + organic: [ { - id: '4', - foo: 'foo', + id: { raw: '3', snippet: null }, + foo: { raw: 'bar', snippet: null }, + _meta: { id: '3' }, }, ], + curation: { + promoted: [{ id: '1', foo: 'foo' }], + organic: [ + { + id: { raw: '5', snippet: null }, + foo: { raw: 'bar', snippet: null }, + _meta: { id: '5' }, + }, + { + id: { raw: '6', snippet: null }, + foo: { raw: 'bar', snippet: null }, + _meta: { id: '6' }, + }, + ], + }, }, isMetaEngine: true, engine: { @@ -99,11 +86,10 @@ describe('CurationSuggestion', () => { it('shows existing promoted documents', () => { const wrapper = shallow(); const suggestedResultsPanel = wrapper.find(CurationResultPanel).at(0); - // gets populated from 'curation' in state, and converted to results format (i.e, has raw properties, etc.) expect(suggestedResultsPanel.prop('results')).toEqual([ { id: { - raw: '4', + raw: '1', snippet: null, }, foo: { @@ -111,7 +97,7 @@ describe('CurationSuggestion', () => { snippet: null, }, _meta: { - id: '4', + id: '1', }, }, ]); @@ -120,7 +106,21 @@ describe('CurationSuggestion', () => { it('shows suggested promoted documents', () => { const wrapper = shallow(); const suggestedResultsPanel = wrapper.find(CurationResultPanel).at(1); - expect(suggestedResultsPanel.prop('results')).toEqual(values.suggestedPromotedDocuments); + expect(suggestedResultsPanel.prop('results')).toEqual([ + { + id: { + raw: '4', + snippet: null, + }, + foo: { + raw: 'foo', + snippet: null, + }, + _meta: { + id: '4', + }, + }, + ]); }); it('displays the query in the title', () => { @@ -142,9 +142,15 @@ describe('CurationSuggestion', () => { it('displays proposed organic results', () => { const wrapper = shallow(); wrapper.find('[data-test-subj="showOrganicResults"]').simulate('click'); - expect(wrapper.find('[data-test-subj="proposedOrganicResults"]').find(Result).length).toBe(4); - expect(wrapper.find(Result).at(0).prop('isMetaEngine')).toEqual(true); - expect(wrapper.find(Result).at(0).prop('schemaForTypeHighlights')).toEqual( + const resultsWrapper = wrapper.find('[data-test-subj="proposedOrganicResults"]').find(Result); + expect(resultsWrapper.length).toBe(1); + expect(resultsWrapper.find(Result).at(0).prop('result')).toEqual({ + id: { raw: '3', snippet: null }, + foo: { raw: 'bar', snippet: null }, + _meta: { id: '3' }, + }); + expect(resultsWrapper.find(Result).at(0).prop('isMetaEngine')).toEqual(true); + expect(resultsWrapper.find(Result).at(0).prop('schemaForTypeHighlights')).toEqual( values.engine.schema ); }); @@ -152,10 +158,43 @@ describe('CurationSuggestion', () => { it('displays current organic results', () => { const wrapper = shallow(); wrapper.find('[data-test-subj="showOrganicResults"]').simulate('click'); - expect(wrapper.find('[data-test-subj="currentOrganicResults"]').find(Result).length).toBe(4); - expect(wrapper.find(Result).at(0).prop('isMetaEngine')).toEqual(true); - expect(wrapper.find(Result).at(0).prop('schemaForTypeHighlights')).toEqual( + const resultWrapper = wrapper.find('[data-test-subj="currentOrganicResults"]').find(Result); + expect(resultWrapper.length).toBe(2); + expect(resultWrapper.find(Result).at(0).prop('result')).toEqual({ + id: { raw: '5', snippet: null }, + foo: { raw: 'bar', snippet: null }, + _meta: { id: '5' }, + }); + expect(resultWrapper.find(Result).at(0).prop('isMetaEngine')).toEqual(true); + expect(resultWrapper.find(Result).at(0).prop('schemaForTypeHighlights')).toEqual( values.engine.schema ); }); + + it('shows an empty prompt when there are no organic results', () => { + setMockValues({ + ...values, + suggestion: { + ...values.suggestion, + organic: [], + curation: { + ...values.suggestion.curation, + organic: [], + }, + }, + }); + const wrapper = shallow(); + wrapper.find('[data-test-subj="showOrganicResults"]').simulate('click'); + expect(wrapper.find('[data-test-subj="currentOrganicResults"]').exists()).toBe(false); + expect(wrapper.find('[data-test-subj="proposedOrganicResults"]').exists()).toBe(false); + expect(wrapper.find(EuiEmptyPrompt).exists()).toBe(true); + }); + + it('renders even if no data is set yet', () => { + setMockValues({ + suggestion: null, + }); + const wrapper = shallow(); + expect(wrapper.find(AppSearchPageTemplate).exists()).toBe(true); + }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_suggestion.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_suggestion.tsx index 7539055253732..4e344d8cc2a39 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_suggestion.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_suggestion.tsx @@ -11,6 +11,7 @@ import { useActions, useValues } from 'kea'; import { EuiButtonEmpty, + EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, @@ -20,6 +21,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { LeafIcon } from '../../../../../shared/icons'; import { useDecodedParams } from '../../../../utils/encode_path_params'; import { EngineLogic } from '../../../engine'; import { AppSearchPageTemplate } from '../../../layout'; @@ -32,19 +34,23 @@ import { CurationActionBar } from './curation_action_bar'; import { CurationResultPanel } from './curation_result_panel'; import { CurationSuggestionLogic } from './curation_suggestion_logic'; -import { DATA } from './temp_data'; export const CurationSuggestion: React.FC = () => { const { query } = useDecodedParams(); const { engine, isMetaEngine } = useValues(EngineLogic); const curationSuggestionLogic = CurationSuggestionLogic({ query }); const { loadSuggestion } = useActions(curationSuggestionLogic); - const { suggestion, suggestedPromotedDocuments, curation, dataLoading } = - useValues(curationSuggestionLogic); + const { suggestion, dataLoading } = useValues(curationSuggestionLogic); const [showOrganicResults, setShowOrganicResults] = useState(false); - const currentOrganicResults = [...DATA].splice(5, 4); - const proposedOrganicResults = [...DATA].splice(2, 4); - const existingCurationResults = curation ? curation.promoted.map(convertToResultFormat) : []; + const currentOrganicResults = suggestion?.curation?.organic || []; + const proposedOrganicResults = suggestion?.organic || []; + const totalNumberOfOrganicResults = currentOrganicResults.length + proposedOrganicResults.length; + const existingCurationResults = suggestion?.curation + ? suggestion.curation.promoted.map(convertToResultFormat) + : []; + const suggestedPromotedDocuments = suggestion?.promoted + ? suggestion?.promoted.map(convertToResultFormat) + : []; const suggestionQuery = suggestion?.query || ''; @@ -114,7 +120,24 @@ export const CurationSuggestion: React.FC = () => { { defaultMessage: 'Expand organic search results' } )} - {showOrganicResults && ( + {showOrganicResults && totalNumberOfOrganicResults === 0 && ( + + {i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.curations.suggestedCuration.noOrganicResultsTitle', + { defaultMessage: 'No results' } + )} + + } + body={i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.curations.suggestedCuration.noOrganicResultsDescription', + { defaultMessage: 'No organic search results were returned for this query' } + )} + /> + )} + {showOrganicResults && totalNumberOfOrganicResults > 0 && ( <> diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_suggestion_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_suggestion_logic.test.ts index 7a91171cc2cc7..e6a847f6e9ec6 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_suggestion_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_suggestion_logic.test.ts @@ -12,109 +12,141 @@ import { mockKibanaValues, } from '../../../../../__mocks__/kea_logic'; -import { set } from 'lodash/fp'; - import '../../../../__mocks__/engine_logic.mock'; import { nextTick } from '@kbn/test/jest'; -import { CurationSuggestion } from '../../types'; +import { HydratedCurationSuggestion } from '../../types'; import { CurationSuggestionLogic } from './curation_suggestion_logic'; const DEFAULT_VALUES = { dataLoading: true, suggestion: null, - suggestedPromotedDocuments: [], - curation: null, }; -const suggestion: CurationSuggestion = { +const suggestion: HydratedCurationSuggestion = { query: 'foo', updated_at: '2021-07-08T14:35:50Z', - promoted: ['1', '2', '3'], - status: 'pending', - operation: 'create', -}; - -const curation = { - id: 'cur-6155e69c7a2f2e4f756303fd', - queries: ['foo'], promoted: [ { - id: '5', - }, - ], - hidden: [], - last_updated: 'September 30, 2021 at 04:32PM', - organic: [], -}; - -const suggestedPromotedDocuments = [ - { - id: { - raw: '1', - }, - _meta: { id: '1', - engine: 'some-engine', - }, - }, - { - id: { - raw: '2', }, - _meta: { + { id: '2', - engine: 'some-engine', - }, - }, - { - id: { - raw: '3', }, - _meta: { + { id: '3', - engine: 'some-engine', - }, - }, -]; - -const MOCK_RESPONSE = { - meta: { - page: { - current: 1, - size: 10, - total_results: 1, - total_pages: 1, }, + ], + status: 'pending', + operation: 'create', + curation: { + id: 'cur-6155e69c7a2f2e4f756303fd', + queries: ['foo'], + promoted: [ + { + id: '5', + }, + ], + hidden: [], + last_updated: 'September 30, 2021 at 04:32PM', + organic: [ + { + id: { + raw: '1', + }, + _meta: { + id: '1', + engine: 'some-engine', + }, + }, + ], }, - results: [suggestion], -}; - -const MOCK_DOCUMENTS_RESPONSE = { - results: [ + organic: [ { id: { - raw: '2', + raw: '1', }, _meta: { - id: '2', + id: '1', engine: 'some-engine', }, }, { id: { - raw: '1', + raw: '2', }, _meta: { - id: '1', + id: '2', engine: 'some-engine', }, }, ], }; +const MOCK_RESPONSE = { + query: 'foo', + status: 'pending', + updated_at: '2021-07-08T14:35:50Z', + operation: 'create', + suggestion: { + promoted: [ + { + id: '1', + }, + { + id: '2', + }, + { + id: '3', + }, + ], + organic: [ + { + id: { + raw: '1', + }, + _meta: { + id: '1', + engine: 'some-engine', + }, + }, + { + id: { + raw: '2', + }, + _meta: { + id: '2', + engine: 'some-engine', + }, + }, + ], + }, + curation: { + id: 'cur-6155e69c7a2f2e4f756303fd', + queries: ['foo'], + promoted: [ + { + id: '5', + }, + ], + hidden: [], + last_updated: 'September 30, 2021 at 04:32PM', + organic: [ + { + id: { + raw: '1', + }, + _meta: { + id: '1', + engine: 'some-engine', + }, + }, + ], + }, +}; + describe('CurationSuggestionLogic', () => { const { mount } = new LogicMounter(CurationSuggestionLogic); const { flashAPIErrors, setQueuedErrorMessage } = mockFlashMessageHelpers; @@ -177,14 +209,10 @@ describe('CurationSuggestionLogic', () => { mountLogic(); CurationSuggestionLogic.actions.onSuggestionLoaded({ suggestion, - suggestedPromotedDocuments, - curation, }); expect(CurationSuggestionLogic.values).toEqual({ ...DEFAULT_VALUES, suggestion, - suggestedPromotedDocuments, - curation, dataLoading: false, }); }); @@ -205,100 +233,24 @@ describe('CurationSuggestionLogic', () => { }); it('should make API calls to fetch data and trigger onSuggestionLoaded', async () => { - http.post.mockReturnValueOnce(Promise.resolve(MOCK_RESPONSE)); - http.post.mockReturnValueOnce(Promise.resolve(MOCK_DOCUMENTS_RESPONSE)); + http.get.mockReturnValueOnce(Promise.resolve(MOCK_RESPONSE)); mountLogic(); jest.spyOn(CurationSuggestionLogic.actions, 'onSuggestionLoaded'); CurationSuggestionLogic.actions.loadSuggestion(); await nextTick(); - expect(http.post).toHaveBeenCalledWith( + expect(http.get).toHaveBeenCalledWith( '/internal/app_search/engines/some-engine/search_relevance_suggestions/foo-query', { - body: JSON.stringify({ - page: { - current: 1, - size: 1, - }, - filters: { - status: ['pending'], - type: 'curation', - }, - }), - } - ); - - expect(http.post).toHaveBeenCalledWith('/internal/app_search/engines/some-engine/search', { - query: { query: '' }, - body: JSON.stringify({ - page: { - size: 100, - }, - filters: { - // The results of the first API call are used to make the second http call for document details - id: MOCK_RESPONSE.results[0].promoted, - }, - }), - }); - - expect(CurationSuggestionLogic.actions.onSuggestionLoaded).toHaveBeenCalledWith({ - suggestion: { - query: 'foo', - updated_at: '2021-07-08T14:35:50Z', - promoted: ['1', '2', '3'], - status: 'pending', - operation: 'create', - }, - // Note that these were re-ordered to match the 'promoted' list above, and since document - // 3 was not found it is not included in this list - suggestedPromotedDocuments: [ - { - id: { - raw: '1', - }, - _meta: { - id: '1', - engine: 'some-engine', - }, - }, - { - id: { - raw: '2', - }, - _meta: { - id: '2', - engine: 'some-engine', - }, + query: { + type: 'curation', }, - ], - curation: null, - }); - }); - - it('will also fetch curation details if the suggestion has a curation_id', async () => { - http.post.mockReturnValueOnce( - Promise.resolve( - set('results[0].curation_id', 'cur-6155e69c7a2f2e4f756303fd', MOCK_RESPONSE) - ) - ); - http.post.mockReturnValueOnce(Promise.resolve(MOCK_DOCUMENTS_RESPONSE)); - http.get.mockReturnValueOnce(Promise.resolve(curation)); - mountLogic(); - jest.spyOn(CurationSuggestionLogic.actions, 'onSuggestionLoaded'); - - CurationSuggestionLogic.actions.loadSuggestion(); - await nextTick(); - - expect(http.get).toHaveBeenCalledWith( - '/internal/app_search/engines/some-engine/curations/cur-6155e69c7a2f2e4f756303fd', - { query: { skip_record_analytics: 'true' } } + } ); expect(CurationSuggestionLogic.actions.onSuggestionLoaded).toHaveBeenCalledWith({ - suggestion: expect.any(Object), - suggestedPromotedDocuments: expect.any(Object), - curation, + suggestion, }); }); @@ -306,7 +258,12 @@ describe('CurationSuggestionLogic', () => { // the back button, etc. The suggestion still exists, it's just not in a "pending" state // so we can show it.ga it('will redirect if the suggestion is not found', async () => { - http.post.mockReturnValueOnce(Promise.resolve(set('results', [], MOCK_RESPONSE))); + http.get.mockReturnValueOnce( + Promise.reject({ + response: { status: 404 }, + }) + ); + mountLogic(); CurationSuggestionLogic.actions.loadSuggestion(); await nextTick(); @@ -314,7 +271,7 @@ describe('CurationSuggestionLogic', () => { expect(navigateToUrl).toHaveBeenCalledWith('/engines/some-engine/curations'); }); - itHandlesErrors(http.post, () => { + itHandlesErrors(http.get, () => { CurationSuggestionLogic.actions.loadSuggestion(); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_suggestion_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_suggestion_logic.ts index 4ca1b0adb7814..b206c0c79ed26 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_suggestion_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_suggestion_logic.ts @@ -19,33 +19,20 @@ import { HttpLogic } from '../../../../../shared/http'; import { KibanaLogic } from '../../../../../shared/kibana'; import { ENGINE_CURATIONS_PATH, ENGINE_CURATION_PATH } from '../../../../routes'; import { EngineLogic, generateEnginePath } from '../../../engine'; -import { Result } from '../../../result/types'; -import { Curation, CurationSuggestion } from '../../types'; +import { CurationSuggestion, HydratedCurationSuggestion } from '../../types'; interface Error { error: string; } interface CurationSuggestionValues { dataLoading: boolean; - suggestion: CurationSuggestion | null; - suggestedPromotedDocuments: Result[]; - curation: Curation | null; + suggestion: HydratedCurationSuggestion | null; } interface CurationSuggestionActions { loadSuggestion(): void; - onSuggestionLoaded({ - suggestion, - suggestedPromotedDocuments, - curation, - }: { - suggestion: CurationSuggestion; - suggestedPromotedDocuments: Result[]; - curation: Curation; - }): { - suggestion: CurationSuggestion; - suggestedPromotedDocuments: Result[]; - curation: Curation; + onSuggestionLoaded({ suggestion }: { suggestion: HydratedCurationSuggestion }): { + suggestion: HydratedCurationSuggestion; }; acceptSuggestion(): void; acceptAndAutomateSuggestion(): void; @@ -63,10 +50,8 @@ export const CurationSuggestionLogic = kea< path: ['enterprise_search', 'app_search', 'curations', 'suggestion_logic'], actions: () => ({ loadSuggestion: true, - onSuggestionLoaded: ({ suggestion, suggestedPromotedDocuments, curation }) => ({ + onSuggestionLoaded: ({ suggestion }) => ({ suggestion, - suggestedPromotedDocuments, - curation, }), acceptSuggestion: true, acceptAndAutomateSuggestion: true, @@ -87,18 +72,6 @@ export const CurationSuggestionLogic = kea< onSuggestionLoaded: (_, { suggestion }) => suggestion, }, ], - suggestedPromotedDocuments: [ - [], - { - onSuggestionLoaded: (_, { suggestedPromotedDocuments }) => suggestedPromotedDocuments, - }, - ], - curation: [ - null, - { - onSuggestionLoaded: (_, { curation }) => curation, - }, - ], }), listeners: ({ actions, values, props }) => ({ loadSuggestion: async () => { @@ -106,34 +79,41 @@ export const CurationSuggestionLogic = kea< const { engineName } = EngineLogic.values; try { - const suggestion = await getSuggestion(http, engineName, props.query); - if (!suggestion) return; - const promotedIds: string[] = suggestion.promoted; - const documentDetailsResopnse = getDocumentDetails(http, engineName, promotedIds); - - let promises = [documentDetailsResopnse]; - if (suggestion.curation_id) { - promises = [...promises, getCuration(http, engineName, suggestion.curation_id)]; - } - - const [documentDetails, curation] = await Promise.all(promises); + const suggestionResponse = await http.get( + `/internal/app_search/engines/${engineName}/search_relevance_suggestions/${props.query}`, + { + query: { + type: 'curation', + }, + } + ); - // Filter out docs that were not found and maintain promoted order - const suggestedPromotedDocuments = promotedIds.reduce((acc: Result[], id: string) => { - const found = documentDetails.results.find( - (documentDetail: Result) => documentDetail.id.raw === id - ); - if (!found) return acc; - return [...acc, found]; - }, []); + // We pull the `organic` and `promoted` fields up to the main body of the suggestion, + // out of the nested `suggestion` field on the response + const { suggestion, ...baseSuggestion } = suggestionResponse; + const suggestionData = { + ...baseSuggestion, + promoted: suggestion.promoted, + organic: suggestion.organic, + }; actions.onSuggestionLoaded({ - suggestion, - suggestedPromotedDocuments, - curation: curation || null, + suggestion: suggestionData, }); } catch (e) { - flashAPIErrors(e); + if (e.response?.status === 404) { + const message = i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.curations.suggestedCuration.notFoundError', + { + defaultMessage: + 'Could not find suggestion, it may have already been applied or rejected.', + } + ); + setQueuedErrorMessage(message); + KibanaLogic.values.navigateToUrl(generateEnginePath(ENGINE_CURATIONS_PATH)); + } else { + flashAPIErrors(e); + } } }, acceptSuggestion: async () => { @@ -289,63 +269,6 @@ const updateSuggestion = async ( return response.results[0] as CurationSuggestion; }; -const getSuggestion = async ( - http: HttpSetup, - engineName: string, - query: string -): Promise => { - const response = await http.post( - `/internal/app_search/engines/${engineName}/search_relevance_suggestions/${query}`, - { - body: JSON.stringify({ - page: { - current: 1, - size: 1, - }, - filters: { - status: ['pending'], - type: 'curation', - }, - }), - } - ); - - if (response.results.length < 1) { - const message = i18n.translate( - 'xpack.enterpriseSearch.appSearch.engine.curations.suggestedCuration.notFoundError', - { - defaultMessage: 'Could not find suggestion, it may have already been applied or rejected.', - } - ); - setQueuedErrorMessage(message); - KibanaLogic.values.navigateToUrl(generateEnginePath(ENGINE_CURATIONS_PATH)); - return; - } - - const suggestion = response.results[0] as CurationSuggestion; - return suggestion; -}; - -const getDocumentDetails = async (http: HttpSetup, engineName: string, documentIds: string[]) => { - return http.post(`/internal/app_search/engines/${engineName}/search`, { - query: { query: '' }, - body: JSON.stringify({ - page: { - size: 100, - }, - filters: { - id: documentIds, - }, - }), - }); -}; - -const getCuration = async (http: HttpSetup, engineName: string, curationId: string) => { - return http.get(`/internal/app_search/engines/${engineName}/curations/${curationId}`, { - query: { skip_record_analytics: 'true' }, - }); -}; - const confirmDialog = (msg: string) => { return new Promise(function (resolve) { const confirmed = window.confirm(msg); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/temp_data.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/temp_data.ts deleted file mode 100644 index 83bbc977427a9..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/temp_data.ts +++ /dev/null @@ -1,470 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { Result } from '../../../result/types'; - -export const DATA: Result[] = [ - { - visitors: { - raw: 5028868.0, - }, - square_km: { - raw: 3082.7, - }, - world_heritage_site: { - raw: 'true', - snippet: 'true', - }, - date_established: { - raw: '1890-10-01T05:00:00+00:00', - }, - description: { - raw: "Yosemite features sheer granite cliffs, exceptionally tall waterfalls, and old-growth forests at a unique intersection of geology and hydrology. Half Dome and El Capitan rise from the park's centerpiece, the glacier-carved Yosemite Valley, and from its vertical walls drop Yosemite Falls, one of North America's tallest waterfalls at 2,425 feet (739 m) high. Three giant sequoia groves, along with a pristine wilderness in the heart of the Sierra Nevada, are home to a wide variety of rare plant and animal species.", - snippet: - 'Yosemite features sheer granite cliffs, exceptionally tall waterfalls, and old-growth forests', - }, - location: { - raw: '37.83,-119.5', - }, - acres: { - raw: 761747.5, - }, - title: { - raw: 'Yosemite', - snippet: 'Yosemite', - }, - nps_link: { - raw: 'https://www.nps.gov/yose/index.htm', - snippet: 'https://www.nps.gov/yose/index.htm', - }, - states: { - raw: ['California'], - snippet: 'California', - }, - _meta: { - engine: 'national-parks-demo', - score: 7543305.0, - id: 'park_yosemite', - }, - id: { - raw: 'park_yosemite', - }, - }, - { - visitors: { - raw: 4517585.0, - }, - square_km: { - raw: 1075.6, - }, - world_heritage_site: { - raw: 'false', - snippet: 'false', - }, - date_established: { - raw: '1915-01-26T06:00:00+00:00', - }, - description: { - raw: 'Bisected north to south by the Continental Divide, this portion of the Rockies has ecosystems varying from over 150 riparian lakes to montane and subalpine forests to treeless alpine tundra. Wildlife including mule deer, bighorn sheep, black bears, and cougars inhabit its igneous mountains and glacial valleys. Longs Peak, a classic Colorado fourteener, and the scenic Bear Lake are popular destinations, as well as the historic Trail Ridge Road, which reaches an elevation of more than 12,000 feet (3,700 m).', - snippet: - ' varying from over 150 riparian lakes to montane and subalpine forests to treeless alpine tundra. Wildlife', - }, - location: { - raw: '40.4,-105.58', - }, - acres: { - raw: 265795.2, - }, - title: { - raw: 'Rocky Mountain', - snippet: 'Rocky Mountain', - }, - nps_link: { - raw: 'https://www.nps.gov/romo/index.htm', - snippet: 'https://www.nps.gov/romo/index.htm', - }, - states: { - raw: ['Colorado'], - snippet: 'Colorado', - }, - _meta: { - engine: 'national-parks-demo', - score: 6776380.0, - id: 'park_rocky-mountain', - }, - id: { - raw: 'park_rocky-mountain', - }, - }, - { - visitors: { - raw: 4295127.0, - }, - square_km: { - raw: 595.8, - }, - world_heritage_site: { - raw: 'false', - snippet: 'false', - }, - date_established: { - raw: '1919-11-19T06:00:00+00:00', - }, - description: { - raw: 'Located at the junction of the Colorado Plateau, Great Basin, and Mojave Desert, this park contains sandstone features such as mesas, rock towers, and canyons, including the Virgin River Narrows. The various sandstone formations and the forks of the Virgin River create a wilderness divided into four ecosystems: desert, riparian, woodland, and coniferous forest.', - snippet: ' into four ecosystems: desert, riparian, woodland, and coniferous forest.', - }, - location: { - raw: '37.3,-113.05', - }, - acres: { - raw: 147237.02, - }, - title: { - raw: 'Zion', - snippet: 'Zion', - }, - nps_link: { - raw: 'https://www.nps.gov/zion/index.htm', - snippet: 'https://www.nps.gov/zion/index.htm', - }, - states: { - raw: ['Utah'], - snippet: 'Utah', - }, - _meta: { - engine: 'national-parks-demo', - score: 6442695.0, - id: 'park_zion', - }, - id: { - raw: 'park_zion', - }, - }, - { - visitors: { - raw: 3303393.0, - }, - square_km: { - raw: 198.5, - }, - world_heritage_site: { - raw: 'false', - snippet: 'false', - }, - date_established: { - raw: '1919-02-26T06:00:00+00:00', - }, - description: { - raw: 'Covering most of Mount Desert Island and other coastal islands, Acadia features the tallest mountain on the Atlantic coast of the United States, granite peaks, ocean shoreline, woodlands, and lakes. There are freshwater, estuary, forest, and intertidal habitats.', - snippet: - ' mountain on the Atlantic coast of the United States, granite peaks, ocean shoreline, woodlands, and lakes. There are freshwater, estuary, forest, and intertidal habitats.', - }, - location: { - raw: '44.35,-68.21', - }, - acres: { - raw: 49057.36, - }, - title: { - raw: 'Acadia', - snippet: 'Acadia', - }, - nps_link: { - raw: 'https://www.nps.gov/acad/index.htm', - snippet: 'https://www.nps.gov/acad/index.htm', - }, - states: { - raw: ['Maine'], - snippet: 'Maine', - }, - _meta: { - engine: 'national-parks-demo', - score: 4955094.5, - id: 'park_acadia', - }, - id: { - raw: 'park_acadia', - }, - }, - { - visitors: { - raw: 1887580.0, - }, - square_km: { - raw: 1308.9, - }, - world_heritage_site: { - raw: 'true', - snippet: 'true', - }, - date_established: { - raw: '1916-08-01T05:00:00+00:00', - }, - description: { - raw: "This park on the Big Island protects the Kīlauea and Mauna Loa volcanoes, two of the world's most active geological features. Diverse ecosystems range from tropical forests at sea level to barren lava beds at more than 13,000 feet (4,000 m).", - snippet: - ' active geological features. Diverse ecosystems range from tropical forests at sea level to barren lava beds at more than 13,000 feet (4,000 m).', - }, - location: { - raw: '19.38,-155.2', - }, - acres: { - raw: 323431.38, - }, - title: { - raw: 'Hawaii Volcanoes', - snippet: 'Hawaii Volcanoes', - }, - nps_link: { - raw: 'https://www.nps.gov/havo/index.htm', - snippet: 'https://www.nps.gov/havo/index.htm', - }, - states: { - raw: ['Hawaii'], - snippet: 'Hawaii', - }, - _meta: { - engine: 'national-parks-demo', - score: 2831373.2, - id: 'park_hawaii-volcanoes', - }, - id: { - raw: 'park_hawaii-volcanoes', - }, - }, - { - visitors: { - raw: 1437341.0, - }, - square_km: { - raw: 806.1, - }, - world_heritage_site: { - raw: 'false', - snippet: 'false', - }, - date_established: { - raw: '1935-12-26T06:00:00+00:00', - }, - description: { - raw: "Shenandoah's Blue Ridge Mountains are covered by hardwood forests that teem with a wide variety of wildlife. The Skyline Drive and Appalachian Trail run the entire length of this narrow park, along with more than 500 miles (800 km) of hiking trails passing scenic overlooks and cataracts of the Shenandoah River.", - snippet: - 'Shenandoah's Blue Ridge Mountains are covered by hardwood forests that teem with a wide variety', - }, - location: { - raw: '38.53,-78.35', - }, - acres: { - raw: 199195.27, - }, - title: { - raw: 'Shenandoah', - snippet: 'Shenandoah', - }, - nps_link: { - raw: 'https://www.nps.gov/shen/index.htm', - snippet: 'https://www.nps.gov/shen/index.htm', - }, - states: { - raw: ['Virginia'], - snippet: 'Virginia', - }, - _meta: { - engine: 'national-parks-demo', - score: 2156015.5, - id: 'park_shenandoah', - }, - id: { - raw: 'park_shenandoah', - }, - }, - { - visitors: { - raw: 1356913.0, - }, - square_km: { - raw: 956.6, - }, - world_heritage_site: { - raw: 'false', - snippet: 'false', - }, - date_established: { - raw: '1899-03-02T06:00:00+00:00', - }, - description: { - raw: 'Mount Rainier, an active stratovolcano, is the most prominent peak in the Cascades and is covered by 26 named glaciers including Carbon Glacier and Emmons Glacier, the largest in the contiguous United States. The mountain is popular for climbing, and more than half of the park is covered by subalpine and alpine forests and meadows seasonally in bloom with wildflowers. Paradise on the south slope is the snowiest place on Earth where snowfall is measured regularly. The Longmire visitor center is the start of the Wonderland Trail, which encircles the mountain.', - snippet: - ' by subalpine and alpine forests and meadows seasonally in bloom with wildflowers. Paradise on the south slope', - }, - location: { - raw: '46.85,-121.75', - }, - acres: { - raw: 236381.64, - }, - title: { - raw: 'Mount Rainier', - snippet: 'Mount Rainier', - }, - nps_link: { - raw: 'https://www.nps.gov/mora/index.htm', - snippet: 'https://www.nps.gov/mora/index.htm', - }, - states: { - raw: ['Washington'], - snippet: 'Washington', - }, - _meta: { - engine: 'national-parks-demo', - score: 2035372.0, - id: 'park_mount-rainier', - }, - id: { - raw: 'park_mount-rainier', - }, - }, - { - visitors: { - raw: 1254688.0, - }, - square_km: { - raw: 1635.2, - }, - world_heritage_site: { - raw: 'false', - snippet: 'false', - }, - date_established: { - raw: '1890-09-25T05:00:00+00:00', - }, - description: { - raw: "This park protects the Giant Forest, which boasts some of the world's largest trees, the General Sherman being the largest measured tree in the park. Other features include over 240 caves, a long segment of the Sierra Nevada including the tallest mountain in the contiguous United States, and Moro Rock, a large granite dome.", - snippet: - 'This park protects the Giant Forest, which boasts some of the world's largest trees, the General', - }, - location: { - raw: '36.43,-118.68', - }, - acres: { - raw: 404062.63, - }, - title: { - raw: 'Sequoia', - snippet: 'Sequoia', - }, - nps_link: { - raw: 'https://www.nps.gov/seki/index.htm', - snippet: 'https://www.nps.gov/seki/index.htm', - }, - states: { - raw: ['California'], - snippet: 'California', - }, - _meta: { - engine: 'national-parks-demo', - score: 1882038.0, - id: 'park_sequoia', - }, - id: { - raw: 'park_sequoia', - }, - }, - { - visitors: { - raw: 643274.0, - }, - square_km: { - raw: 896.0, - }, - world_heritage_site: { - raw: 'false', - snippet: 'false', - }, - date_established: { - raw: '1962-12-09T06:00:00+00:00', - }, - description: { - raw: 'This portion of the Chinle Formation has a large concentration of 225-million-year-old petrified wood. The surrounding Painted Desert features eroded cliffs of red-hued volcanic rock called bentonite. Dinosaur fossils and over 350 Native American sites are also protected in this park.', - snippet: - 'This portion of the Chinle Formation has a large concentration of 225-million-year-old petrified', - }, - location: { - raw: '35.07,-109.78', - }, - acres: { - raw: 221415.77, - }, - title: { - raw: 'Petrified Forest', - snippet: 'Petrified Forest', - }, - nps_link: { - raw: 'https://www.nps.gov/pefo/index.htm', - snippet: 'https://www.nps.gov/pefo/index.htm', - }, - states: { - raw: ['Arizona'], - snippet: 'Arizona', - }, - _meta: { - engine: 'national-parks-demo', - score: 964919.94, - id: 'park_petrified-forest', - }, - id: { - raw: 'park_petrified-forest', - }, - }, - { - visitors: { - raw: 617377.0, - }, - square_km: { - raw: 137.5, - }, - world_heritage_site: { - raw: 'false', - snippet: 'false', - }, - date_established: { - raw: '1903-01-09T06:00:00+00:00', - }, - description: { - raw: "Wind Cave is distinctive for its calcite fin formations called boxwork, a unique formation rarely found elsewhere, and needle-like growths called frostwork. The cave is one of the longest and most complex caves in the world. Above ground is a mixed-grass prairie with animals such as bison, black-footed ferrets, and prairie dogs, and ponderosa pine forests that are home to cougars and elk. The cave is culturally significant to the Lakota people as the site 'from which Wakan Tanka, the Great Mystery, sent the buffalo out into their hunting grounds.'", - snippet: - '-footed ferrets, and prairie dogs, and ponderosa pine forests that are home to cougars and elk', - }, - location: { - raw: '43.57,-103.48', - }, - acres: { - raw: 33970.84, - }, - title: { - raw: 'Wind Cave', - snippet: 'Wind Cave', - }, - nps_link: { - raw: 'https://www.nps.gov/wica/index.htm', - snippet: 'https://www.nps.gov/wica/index.htm', - }, - states: { - raw: ['South Dakota'], - snippet: 'South Dakota', - }, - _meta: { - engine: 'national-parks-demo', - score: 926068.7, - id: 'park_wind-cave', - }, - id: { - raw: 'park_wind-cave', - }, - }, -]; diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/search_relevance_suggestions.test.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/search_relevance_suggestions.test.ts index 2bdcfb9fe9d58..daab7c35596bf 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/search_relevance_suggestions.test.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/search_relevance_suggestions.test.ts @@ -116,9 +116,9 @@ describe('search relevance insights routes', () => { }); }); - describe('POST /internal/app_search/engines/{name}/search_relevance_suggestions/{query}', () => { + describe('GET /internal/app_search/engines/{engineName}/search_relevance_suggestions/{query}', () => { const mockRouter = new MockRouter({ - method: 'post', + method: 'get', path: '/internal/app_search/engines/{engineName}/search_relevance_suggestions/{query}', }); @@ -132,10 +132,11 @@ describe('search relevance insights routes', () => { it('creates a request to enterprise search', () => { mockRouter.callRoute({ params: { engineName: 'some-engine', query: 'foo' }, + query: { type: 'curation' }, }); expect(mockRequestHandler.createRequest).toHaveBeenCalledWith({ - path: '/api/as/v0/engines/:engineName/search_relevance_suggestions/:query', + path: '/as/engines/:engineName/search_relevance_suggestions/:query', }); }); }); diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/search_relevance_suggestions.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/search_relevance_suggestions.ts index 8b3b204c24d70..95b50a9c4971e 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/search_relevance_suggestions.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/search_relevance_suggestions.ts @@ -81,7 +81,7 @@ export function registerSearchRelevanceSuggestionsRoutes({ }) ); - router.post( + router.get( { path: '/internal/app_search/engines/{engineName}/search_relevance_suggestions/{query}', validate: { @@ -89,20 +89,13 @@ export function registerSearchRelevanceSuggestionsRoutes({ engineName: schema.string(), query: schema.string(), }), - body: schema.object({ - page: schema.object({ - current: schema.number(), - size: schema.number(), - }), - filters: schema.object({ - status: schema.arrayOf(schema.string()), - type: schema.string(), - }), + query: schema.object({ + type: schema.string(), }), }, }, enterpriseSearchRequestHandler.createRequest({ - path: '/api/as/v0/engines/:engineName/search_relevance_suggestions/:query', + path: '/as/engines/:engineName/search_relevance_suggestions/:query', }) ); } From 498050e05b3505b8afdb03860cb7d26db780c689 Mon Sep 17 00:00:00 2001 From: Constance Date: Tue, 19 Oct 2021 09:15:38 -0700 Subject: [PATCH 068/204] Upgrade EUI to v39.1.1 (#114732) * Upversion to EUI 39.1.0 * Update i18n_eui_mapping tokens @see https://github.com/elastic/eui/blob/master/i18ntokens_changelog.json * Merge refractor in yarn.lock * Fix functional table filter selector - Popover ID was removed in recent EUI a11y fix, so we're using child-position selection to target the Tags filter now * Update snaphots * Upgrade to 39.1.1 for extra bugfixes * Update i18n mappings * Fix i18n snapshot * Attempt to harden flaky Security Cypress test * More combobox entry hardening - Got a flake on clicking the combobox dropdown on run 17/20 locally Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- package.json | 2 +- .../__snapshots__/i18n_service.test.tsx.snap | 1 + src/core/public/i18n/i18n_eui_mapping.tsx | 5 +- src/dev/license_checker/config.ts | 2 +- .../__snapshots__/data_view.test.tsx.snap | 1114 +++++++++-------- .../List/__snapshots__/List.test.tsx.snap | 4 +- .../workpad_table.stories.storyshot | 8 +- .../report_listing.test.tsx.snap | 584 ++++----- .../cypress/screens/timeline.ts | 2 + .../cypress/tasks/timeline.ts | 10 +- .../alert_summary_view.test.tsx.snap | 4 - .../__snapshots__/donut_chart.test.tsx.snap | 2 - .../functional/tests/som_integration.ts | 5 +- yarn.lock | 12 +- 14 files changed, 897 insertions(+), 858 deletions(-) diff --git a/package.json b/package.json index 47ed5e110b000..b3b9b96e75566 100644 --- a/package.json +++ b/package.json @@ -102,7 +102,7 @@ "@elastic/datemath": "link:bazel-bin/packages/elastic-datemath", "@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@^8.0.0-canary.21", "@elastic/ems-client": "7.16.0", - "@elastic/eui": "39.0.0", + "@elastic/eui": "39.1.1", "@elastic/filesaver": "1.1.2", "@elastic/maki": "6.3.0", "@elastic/node-crypto": "1.2.1", diff --git a/src/core/public/i18n/__snapshots__/i18n_service.test.tsx.snap b/src/core/public/i18n/__snapshots__/i18n_service.test.tsx.snap index d714f2159d1a2..197714df7f207 100644 --- a/src/core/public/i18n/__snapshots__/i18n_service.test.tsx.snap +++ b/src/core/public/i18n/__snapshots__/i18n_service.test.tsx.snap @@ -175,6 +175,7 @@ exports[`#start() returns \`Context\` component 1`] = ` "euiRefreshInterval.legend": "Refresh every", "euiRefreshInterval.start": "Start", "euiRefreshInterval.stop": "Stop", + "euiRelativeTab.dateInputError": "Must be a valid range", "euiRelativeTab.fullDescription": [Function], "euiRelativeTab.numberInputError": "Must be >= 0", "euiRelativeTab.numberInputLabel": "Time span amount", diff --git a/src/core/public/i18n/i18n_eui_mapping.tsx b/src/core/public/i18n/i18n_eui_mapping.tsx index 7585ada886c05..f28add25056ee 100644 --- a/src/core/public/i18n/i18n_eui_mapping.tsx +++ b/src/core/public/i18n/i18n_eui_mapping.tsx @@ -64,7 +64,7 @@ export const getEuiContextMapping = (): EuiTokensObject => { }), 'euiBasicTable.tablePagination': ({ tableCaption }: EuiValues) => i18n.translate('core.euiBasicTable.tablePagination', { - defaultMessage: 'Pagination for preceding table: {tableCaption}', + defaultMessage: 'Pagination for table: {tableCaption}', values: { tableCaption }, description: 'Screen reader text to describe the pagination controls', }), @@ -861,6 +861,9 @@ export const getEuiContextMapping = (): EuiTokensObject => { 'euiRelativeTab.numberInputLabel': i18n.translate('core.euiRelativeTab.numberInputLabel', { defaultMessage: 'Time span amount', }), + 'euiRelativeTab.dateInputError': i18n.translate('core.euiRelativeTab.dateInputError', { + defaultMessage: 'Must be a valid range', + }), 'euiResizableButton.horizontalResizerAriaLabel': i18n.translate( 'core.euiResizableButton.horizontalResizerAriaLabel', { diff --git a/src/dev/license_checker/config.ts b/src/dev/license_checker/config.ts index a4ae39848735e..efa54e74fdf2f 100644 --- a/src/dev/license_checker/config.ts +++ b/src/dev/license_checker/config.ts @@ -75,6 +75,6 @@ export const LICENSE_OVERRIDES = { 'jsts@1.6.2': ['Eclipse Distribution License - v 1.0'], // cf. https://github.com/bjornharrtell/jsts '@mapbox/jsonlint-lines-primitives@2.0.2': ['MIT'], // license in readme https://github.com/tmcw/jsonlint '@elastic/ems-client@7.16.0': ['Elastic License 2.0'], - '@elastic/eui@39.0.0': ['SSPL-1.0 OR Elastic License 2.0'], + '@elastic/eui@39.1.1': ['SSPL-1.0 OR Elastic License 2.0'], 'language-subtag-registry@0.3.21': ['CC-BY-4.0'], // retired ODC‑By license https://github.com/mattcg/language-subtag-registry }; diff --git a/src/plugins/data/public/utils/table_inspector_view/components/__snapshots__/data_view.test.tsx.snap b/src/plugins/data/public/utils/table_inspector_view/components/__snapshots__/data_view.test.tsx.snap index eae2032748396..7773f2209bf96 100644 --- a/src/plugins/data/public/utils/table_inspector_view/components/__snapshots__/data_view.test.tsx.snap +++ b/src/plugins/data/public/utils/table_inspector_view/components/__snapshots__/data_view.test.tsx.snap @@ -1220,93 +1220,75 @@ exports[`Inspector Data View component should render single table without select
- -
- -
- - - +
+
+ + + - -
- - - : - 20 - - } - closePopover={[Function]} - display="inlineBlock" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelPaddingSize="none" +
-
-
+ - - + + + +
-
-
-
-
- -
+
+
+ - - - -
- -
-
-
-
- + + +
+ +
+ + +
+ + @@ -2791,93 +2806,75 @@ exports[`Inspector Data View component should support multiple datatables 1`] = - -
- -
- - - +
+
+ + + - -
- - - : - 20 - - } - closePopover={[Function]} - display="inlineBlock" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelPaddingSize="none" +
-
-
+ - - + + + +
-
-
-
-
- -
+
+
+ - - - -
- -
-
-
-
- + + +
+
+ +
+ + + + diff --git a/x-pack/plugins/apm/public/components/app/error_group_overview/List/__snapshots__/List.test.tsx.snap b/x-pack/plugins/apm/public/components/app/error_group_overview/List/__snapshots__/List.test.tsx.snap index c8c7bf82dff04..ee68630daa469 100644 --- a/x-pack/plugins/apm/public/components/app/error_group_overview/List/__snapshots__/List.test.tsx.snap +++ b/x-pack/plugins/apm/public/components/app/error_group_overview/List/__snapshots__/List.test.tsx.snap @@ -151,7 +151,6 @@ exports[`ErrorGroupOverview -> List should render empty state 1`] = ` className="euiTableHeaderCell" data-test-subj="tableHeaderCell_handled_3" role="columnheader" - scope="col" style={ Object { "width": undefined, @@ -447,7 +446,6 @@ exports[`ErrorGroupOverview -> List should render with data 1`] = ` className="euiTableHeaderCell" data-test-subj="tableHeaderCell_handled_3" role="columnheader" - scope="col" style={ Object { "width": undefined, @@ -1265,6 +1263,7 @@ exports[`ErrorGroupOverview -> List should render with data 1`] = ` className="euiFlexItem euiFlexItem--flexGrowZero" > + + +
+ + + + + + `; diff --git a/x-pack/plugins/security_solution/cypress/screens/timeline.ts b/x-pack/plugins/security_solution/cypress/screens/timeline.ts index 2e412bbed6fdc..bc3a4282df1c9 100644 --- a/x-pack/plugins/security_solution/cypress/screens/timeline.ts +++ b/x-pack/plugins/security_solution/cypress/screens/timeline.ts @@ -28,6 +28,8 @@ export const CLOSE_TIMELINE_BTN = '[data-test-subj="close-timeline"]'; export const COMBO_BOX = '.euiComboBoxOption__content'; +export const COMBO_BOX_INPUT = '[data-test-subj="comboBoxInput"]'; + export const CREATE_NEW_TIMELINE = '[data-test-subj="timeline-new"]'; export const CREATE_NEW_TIMELINE_TEMPLATE = '[data-test-subj="template-timeline-new"]'; diff --git a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts index 4c6b73de80940..c7cb56c89e9df 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts @@ -20,6 +20,7 @@ import { CASE, CLOSE_TIMELINE_BTN, COMBO_BOX, + COMBO_BOX_INPUT, CREATE_NEW_TIMELINE, FIELD_BROWSER, ID_HEADER_FIELD, @@ -164,9 +165,12 @@ export const addDataProvider = (filter: TimelineFilter): Cypress.Chainable { // open the filter dropdown - // the first class selector before the id is of course useless. Only here to help cleaning that once we got - // testSubjects in EUI filters. + // This CSS selector should be cleaned up once we have testSubjects in EUI filters. const filterButton = await find.byCssSelector( - '.euiFilterGroup #field_value_selection_1 .euiFilterButton' + '.euiFilterGroup > *:last-child .euiFilterButton' ); await filterButton.click(); // select the tags diff --git a/yarn.lock b/yarn.lock index e5e2f59359c9f..d715897b57e28 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2412,10 +2412,10 @@ resolved "https://registry.yarnpkg.com/@elastic/eslint-plugin-eui/-/eslint-plugin-eui-0.0.2.tgz#56b9ef03984a05cc213772ae3713ea8ef47b0314" integrity sha512-IoxURM5zraoQ7C8f+mJb9HYSENiZGgRVcG4tLQxE61yHNNRDXtGDWTZh8N1KIHcsqN1CEPETjuzBXkJYF/fDiQ== -"@elastic/eui@39.0.0": - version "39.0.0" - resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-39.0.0.tgz#abac19edd466eee13612d5668e5456961dc813b8" - integrity sha512-8sf8sbxjpRxV23dFTwbkaWH6LhWrOlMpdUUMVUC9zd0g5iQLj1IxkxQCeyYM/p++SQFl+1hshAuaH//DCz5Xrw== +"@elastic/eui@39.1.1": + version "39.1.1" + resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-39.1.1.tgz#52e59f1dd6448b2e80047259ca60c6c87e9873f0" + integrity sha512-zYCNitpp6Ds7U6eaa9QkJqc20ZMo2wjpZokNtd1WalFV22vdfiVizFg7DMtDjJrCDLmoXcLOOCMasKlmmJ1cRg== dependencies: "@types/chroma-js" "^2.0.0" "@types/lodash" "^4.14.160" @@ -2441,7 +2441,7 @@ react-is "~16.3.0" react-virtualized-auto-sizer "^1.0.2" react-window "^1.8.5" - refractor "^3.4.0" + refractor "^3.5.0" rehype-raw "^5.0.0" rehype-react "^6.0.0" rehype-stringify "^8.0.0" @@ -24731,7 +24731,7 @@ reflect.ownkeys@^0.2.0: resolved "https://registry.yarnpkg.com/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz#749aceec7f3fdf8b63f927a04809e90c5c0b3460" integrity sha1-dJrO7H8/34tj+SegSAnpDFwLNGA= -refractor@^3.2.0, refractor@^3.4.0: +refractor@^3.2.0, refractor@^3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/refractor/-/refractor-3.5.0.tgz#334586f352dda4beaf354099b48c2d18e0819aec" integrity sha512-QwPJd3ferTZ4cSPPjdP5bsYHMytwWYnAN5EEnLtGvkqp/FCCnGsBgxrm9EuIDnjUC3Uc/kETtvVi7fSIVC74Dg== From 83f12a9d821f7b7a081dffad59da8797a78c3686 Mon Sep 17 00:00:00 2001 From: Aleh Zasypkin Date: Tue, 19 Oct 2021 18:38:27 +0200 Subject: [PATCH 069/204] Change default session idle timeout to 8 hours. (#115565) --- docs/settings/security-settings.asciidoc | 2 +- docs/user/security/session-management.asciidoc | 2 +- x-pack/plugins/security/server/config.test.ts | 18 +++++++++--------- x-pack/plugins/security/server/config.ts | 2 +- .../security_usage_collector.test.ts | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/settings/security-settings.asciidoc b/docs/settings/security-settings.asciidoc index 11072509da1fc..c291b65c3c35b 100644 --- a/docs/settings/security-settings.asciidoc +++ b/docs/settings/security-settings.asciidoc @@ -272,7 +272,7 @@ You can configure the following settings in the `kibana.yml` file. |[[xpack-session-idleTimeout]] `xpack.security.session.idleTimeout` {ess-icon} | Ensures that user sessions will expire after a period of inactivity. This and <> are both -highly recommended. You can also specify this setting for <>. If this is set to `0`, then sessions will never expire due to inactivity. By default, this value is 1 hour. +highly recommended. You can also specify this setting for <>. If this is set to `0`, then sessions will never expire due to inactivity. By default, this value is 8 hours. 2+a| [TIP] diff --git a/docs/user/security/session-management.asciidoc b/docs/user/security/session-management.asciidoc index b0f27d45bb826..e896c8fe77254 100644 --- a/docs/user/security/session-management.asciidoc +++ b/docs/user/security/session-management.asciidoc @@ -12,7 +12,7 @@ To manage user sessions programmatically, {kib} exposes <[ms|s|m|h|d|w|M|Y]` (e.g. '20m', '24h', '7d', '1w'). For example, set the idle timeout to expire sessions after 30 minutes of inactivity: +By default, sessions expire after 8 hours of inactivity. To define another value for a sliding session expiration, set the property in the `kibana.yml` configuration file. The idle timeout is formatted as a duration of `[ms|s|m|h|d|w|M|Y]` (e.g. '20m', '24h', '7d', '1w'). For example, set the idle timeout to expire sessions after 30 minutes of inactivity: -- [source,yaml] diff --git a/x-pack/plugins/security/server/config.test.ts b/x-pack/plugins/security/server/config.test.ts index 1baf3fd4aac50..4034a7a79e6dd 100644 --- a/x-pack/plugins/security/server/config.test.ts +++ b/x-pack/plugins/security/server/config.test.ts @@ -63,7 +63,7 @@ describe('config schema', () => { "secureCookies": false, "session": Object { "cleanupInterval": "PT1H", - "idleTimeout": "PT1H", + "idleTimeout": "PT8H", "lifespan": "P30D", }, "showInsecureClusterWarning": true, @@ -117,7 +117,7 @@ describe('config schema', () => { "secureCookies": false, "session": Object { "cleanupInterval": "PT1H", - "idleTimeout": "PT1H", + "idleTimeout": "PT8H", "lifespan": "P30D", }, "showInsecureClusterWarning": true, @@ -170,7 +170,7 @@ describe('config schema', () => { "secureCookies": false, "session": Object { "cleanupInterval": "PT1H", - "idleTimeout": "PT1H", + "idleTimeout": "PT8H", "lifespan": "P30D", }, "showInsecureClusterWarning": true, @@ -1768,7 +1768,7 @@ describe('createConfig()', () => { expect(createMockConfig().session.getExpirationTimeouts({ type: 'basic', name: 'basic1' })) .toMatchInlineSnapshot(` Object { - "idleTimeout": "PT1H", + "idleTimeout": "PT8H", "lifespan": "P30D", } `); @@ -1818,7 +1818,7 @@ describe('createConfig()', () => { }) ).toMatchInlineSnapshot(` Object { - "idleTimeout": "PT1H", + "idleTimeout": "PT8H", "lifespan": "PT0.456S", } `); @@ -1852,7 +1852,7 @@ describe('createConfig()', () => { createMockConfig({ session: { lifespan: 456 } }).session.getExpirationTimeouts(provider) ).toMatchInlineSnapshot(` Object { - "idleTimeout": "PT1H", + "idleTimeout": "PT8H", "lifespan": "PT0.456S", } `); @@ -1933,14 +1933,14 @@ describe('createConfig()', () => { expect(configWithoutGlobal.session.getExpirationTimeouts({ type: 'basic', name: 'basic1' })) .toMatchInlineSnapshot(` Object { - "idleTimeout": "PT1H", + "idleTimeout": "PT8H", "lifespan": "PT0.654S", } `); expect(configWithoutGlobal.session.getExpirationTimeouts({ type: 'saml', name: 'saml1' })) .toMatchInlineSnapshot(` Object { - "idleTimeout": "PT1H", + "idleTimeout": "PT8H", "lifespan": "PT11M5.544S", } `); @@ -1957,7 +1957,7 @@ describe('createConfig()', () => { expect(configWithGlobal.session.getExpirationTimeouts({ type: 'basic', name: 'basic1' })) .toMatchInlineSnapshot(` Object { - "idleTimeout": "PT1H", + "idleTimeout": "PT8H", "lifespan": "PT0.654S", } `); diff --git a/x-pack/plugins/security/server/config.ts b/x-pack/plugins/security/server/config.ts index 89918e73369d3..23a1fd2efa382 100644 --- a/x-pack/plugins/security/server/config.ts +++ b/x-pack/plugins/security/server/config.ts @@ -211,7 +211,7 @@ export const ConfigSchema = schema.object({ ), session: schema.object({ idleTimeout: schema.oneOf([schema.duration(), schema.literal(null)], { - defaultValue: schema.duration().validate('1h'), + defaultValue: schema.duration().validate('8h'), }), lifespan: schema.oneOf([schema.duration(), schema.literal(null)], { defaultValue: schema.duration().validate('30d'), diff --git a/x-pack/plugins/security/server/usage_collector/security_usage_collector.test.ts b/x-pack/plugins/security/server/usage_collector/security_usage_collector.test.ts index 83f09ef017b01..3a53a2422770c 100644 --- a/x-pack/plugins/security/server/usage_collector/security_usage_collector.test.ts +++ b/x-pack/plugins/security/server/usage_collector/security_usage_collector.test.ts @@ -47,7 +47,7 @@ describe('Security UsageCollector', () => { enabledAuthProviders: ['basic'], loginSelectorEnabled: false, httpAuthSchemes: ['apikey', 'bearer'], - sessionIdleTimeoutInMinutes: 60, + sessionIdleTimeoutInMinutes: 480, sessionLifespanInMinutes: 43200, sessionCleanupInMinutes: 60, }; From 20b11c9f432a7a9b6ee3b5a1f23e027913795366 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Tue, 19 Oct 2021 19:39:51 +0300 Subject: [PATCH 070/204] [Cases][Connectors] ServiceNow ITOM: MVP (#114125) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- docs/management/action-types.asciidoc | 4 + .../action-types/servicenow-itom.asciidoc | 90 +++++ .../action-types/servicenow-sir.asciidoc | 2 +- .../action-types/servicenow.asciidoc | 2 +- .../images/servicenow-itom-connector.png | Bin 0 -> 197351 bytes .../images/servicenow-itom-params-test.png | Bin 0 -> 261091 bytes docs/management/connectors/index.asciidoc | 1 + x-pack/plugins/actions/README.md | 51 ++- .../server/builtin_action_types/index.ts | 13 +- .../servicenow/api.test.ts | 3 + .../servicenow/api_itom.test.ts | 53 +++ .../servicenow/api_itom.ts | 70 ++++ .../servicenow/config.test.ts | 11 + .../builtin_action_types/servicenow/config.ts | 10 + .../servicenow/index.test.ts | 6 +- .../builtin_action_types/servicenow/index.ts | 152 ++++++-- .../builtin_action_types/servicenow/mocks.ts | 30 ++ .../builtin_action_types/servicenow/schema.ts | 38 +- .../servicenow/service_itom.test.ts | 90 +++++ .../servicenow/service_itom.ts | 65 ++++ .../servicenow/service_sir.ts | 2 +- .../servicenow/translations.ts | 4 + .../builtin_action_types/servicenow/types.ts | 51 ++- .../servicenow/utils.test.ts | 54 ++- .../builtin_action_types/servicenow/utils.ts | 25 ++ .../actions/server/constants/connectors.ts | 3 + .../security_solution/common/constants.ts | 7 + .../components/builtin_action_types/index.ts | 13 +- .../servicenow/helpers.test.ts | 4 + .../servicenow/helpers.ts | 4 +- .../builtin_action_types/servicenow/index.ts | 6 +- .../servicenow/servicenow.test.tsx | 33 +- .../servicenow/servicenow.tsx | 46 +++ .../servicenow_connectors_no_app.tsx | 33 ++ .../servicenow_itom_params.test.tsx | 179 +++++++++ .../servicenow/servicenow_itom_params.tsx | 160 ++++++++ .../servicenow/servicenow_sir_params.tsx | 2 +- .../servicenow/translations.ts | 80 ++++ .../builtin_action_types/servicenow/types.ts | 6 + .../servicenow/use_choices.test.tsx | 164 +++++++++ .../servicenow/use_choices.tsx | 66 ++++ .../servicenow/use_get_choices.test.tsx | 24 +- .../servicenow/use_get_choices.tsx | 7 +- .../alerting_api_integration/common/config.ts | 1 + .../server/servicenow_simulation.ts | 8 + .../actions/builtin_action_types/jira.ts | 4 - .../actions/builtin_action_types/resilient.ts | 4 - .../builtin_action_types/servicenow_itom.ts | 342 ++++++++++++++++++ .../builtin_action_types/servicenow_itsm.ts | 4 - .../builtin_action_types/servicenow_sir.ts | 4 - .../actions/builtin_action_types/swimlane.ts | 4 - .../tests/actions/index.ts | 1 + 52 files changed, 1956 insertions(+), 80 deletions(-) create mode 100644 docs/management/connectors/action-types/servicenow-itom.asciidoc create mode 100644 docs/management/connectors/images/servicenow-itom-connector.png create mode 100644 docs/management/connectors/images/servicenow-itom-params-test.png create mode 100644 x-pack/plugins/actions/server/builtin_action_types/servicenow/api_itom.test.ts create mode 100644 x-pack/plugins/actions/server/builtin_action_types/servicenow/api_itom.ts create mode 100644 x-pack/plugins/actions/server/builtin_action_types/servicenow/service_itom.test.ts create mode 100644 x-pack/plugins/actions/server/builtin_action_types/servicenow/service_itom.ts create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors_no_app.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_itom_params.test.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_itom_params.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/use_choices.test.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/use_choices.tsx create mode 100644 x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow_itom.ts diff --git a/docs/management/action-types.asciidoc b/docs/management/action-types.asciidoc index 93d0ee3d2cab6..b2bf5f2bbe308 100644 --- a/docs/management/action-types.asciidoc +++ b/docs/management/action-types.asciidoc @@ -43,6 +43,10 @@ a| <> | Create a security incident in ServiceNow. +a| <> + +| Create an event in ServiceNow. + a| <> | Send a message to a Slack channel or user. diff --git a/docs/management/connectors/action-types/servicenow-itom.asciidoc b/docs/management/connectors/action-types/servicenow-itom.asciidoc new file mode 100644 index 0000000000000..017290dde9b15 --- /dev/null +++ b/docs/management/connectors/action-types/servicenow-itom.asciidoc @@ -0,0 +1,90 @@ +[role="xpack"] +[[servicenow-itom-action-type]] +=== ServiceNow connector and action +++++ +ServiceNow ITOM +++++ + +The ServiceNow ITOM connector uses the https://docs.servicenow.com/bundle/rome-it-operations-management/page/product/event-management/task/send-events-via-web-service.html[Event API] to create ServiceNow events. + +[float] +[[servicenow-itom-connector-configuration]] +==== Connector configuration + +ServiceNow ITOM connectors have the following configuration properties. + +Name:: The name of the connector. The name is used to identify a connector in the **Stack Management** UI connector listing, and in the connector list when configuring an action. +URL:: ServiceNow instance URL. +Username:: Username for HTTP Basic authentication. +Password:: Password for HTTP Basic authentication. + +The ServiceNow user requires at minimum read, create, and update access to the Event table and read access to the https://docs.servicenow.com/bundle/paris-platform-administration/page/administer/localization/reference/r_ChoicesTable.html[sys_choice]. If you don't provide access to sys_choice, then the choices will not render. + +[float] +[[servicenow-itom-connector-networking-configuration]] +==== Connector networking configuration + +Use the <> to customize connector networking configurations, such as proxies, certificates, or TLS settings. You can set configurations that apply to all your connectors or use `xpack.actions.customHostSettings` to set per-host configurations. + +[float] +[[Preconfigured-servicenow-itom-configuration]] +==== Preconfigured connector type + +[source,text] +-- + my-servicenow-itom: + name: preconfigured-servicenow-connector-type + actionTypeId: .servicenow-itom + config: + apiUrl: https://example.service-now.com/ + secrets: + username: testuser + password: passwordkeystorevalue +-- + +Config defines information for the connector type. + +`apiUrl`:: An address that corresponds to *URL*. + +Secrets defines sensitive information for the connector type. + +`username`:: A string that corresponds to *Username*. +`password`:: A string that corresponds to *Password*. Should be stored in the <>. + +[float] +[[define-servicenow-itom-ui]] +==== Define connector in Stack Management + +Define ServiceNow ITOM connector properties. + +[role="screenshot"] +image::management/connectors/images/servicenow-itom-connector.png[ServiceNow ITOM connector] + +Test ServiceNow ITOM action parameters. + +[role="screenshot"] +image::management/connectors/images/servicenow-itom-params-test.png[ServiceNow ITOM params test] + +[float] +[[servicenow-itom-action-configuration]] +==== Action configuration + +ServiceNow ITOM actions have the following configuration properties. + +Source:: The name of the event source type. +Node:: The Host that the event was triggered for. +Type:: The type of event. +Resource:: The name of the resource. +Metric name:: Name of the metric. +Source instance (event_class):: Specific instance of the source. +Message key:: All actions sharing this key will be associated with the same ServiceNow alert. Default value: `:`. +Severity:: The severity of the event. +Description:: The details about the event. + +Refer to https://docs.servicenow.com/bundle/rome-it-operations-management/page/product/event-management/task/send-events-via-web-service.html[ServiceNow documentation] for more information about the properties. + +[float] +[[configuring-servicenow-itom]] +==== Configure ServiceNow ITOM + +ServiceNow offers free https://developer.servicenow.com/dev.do#!/guides/madrid/now-platform/pdi-guide/obtaining-a-pdi[Personal Developer Instances], which you can use to test incidents. diff --git a/docs/management/connectors/action-types/servicenow-sir.asciidoc b/docs/management/connectors/action-types/servicenow-sir.asciidoc index 2fa49fe552c2e..40fb07897d206 100644 --- a/docs/management/connectors/action-types/servicenow-sir.asciidoc +++ b/docs/management/connectors/action-types/servicenow-sir.asciidoc @@ -36,7 +36,7 @@ Use the <> to customize connecto name: preconfigured-servicenow-connector-type actionTypeId: .servicenow-sir config: - apiUrl: https://dev94428.service-now.com/ + apiUrl: https://example.service-now.com/ secrets: username: testuser password: passwordkeystorevalue diff --git a/docs/management/connectors/action-types/servicenow.asciidoc b/docs/management/connectors/action-types/servicenow.asciidoc index f7c3187f3f024..eae1fce75731d 100644 --- a/docs/management/connectors/action-types/servicenow.asciidoc +++ b/docs/management/connectors/action-types/servicenow.asciidoc @@ -36,7 +36,7 @@ Use the <> to customize connecto name: preconfigured-servicenow-connector-type actionTypeId: .servicenow config: - apiUrl: https://dev94428.service-now.com/ + apiUrl: https://example.service-now.com/ secrets: username: testuser password: passwordkeystorevalue diff --git a/docs/management/connectors/images/servicenow-itom-connector.png b/docs/management/connectors/images/servicenow-itom-connector.png new file mode 100644 index 0000000000000000000000000000000000000000..5b73336d21b47ccd17fa4c365e7abdf6098bf93c GIT binary patch literal 197351 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

DKX_?cb0t2AuU+R8^!# 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 literal 0 HcmV?d00001 diff --git a/docs/management/connectors/images/servicenow-itom-params-test.png b/docs/management/connectors/images/servicenow-itom-params-test.png new file mode 100644 index 0000000000000000000000000000000000000000..8d7b1e075911a7b49fe5040d16d62b3cc74cbcf1 GIT binary patch literal 261091 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%0DiEGfSp=~e;hbwV090pXn@|b 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 literal 0 HcmV?d00001 diff --git a/docs/management/connectors/index.asciidoc b/docs/management/connectors/index.asciidoc index 536d05705181d..6968475cf3a4e 100644 --- a/docs/management/connectors/index.asciidoc +++ b/docs/management/connectors/index.asciidoc @@ -7,6 +7,7 @@ include::action-types/pagerduty.asciidoc[] include::action-types/server-log.asciidoc[] include::action-types/servicenow.asciidoc[] include::action-types/servicenow-sir.asciidoc[] +include::action-types/servicenow-itom.asciidoc[] include::action-types/swimlane.asciidoc[] include::action-types/slack.asciidoc[] include::action-types/webhook.asciidoc[] diff --git a/x-pack/plugins/actions/README.md b/x-pack/plugins/actions/README.md index 0d66c9d30f8b9..f838832b6ea66 100644 --- a/x-pack/plugins/actions/README.md +++ b/x-pack/plugins/actions/README.md @@ -45,9 +45,12 @@ Table of Contents - [`subActionParams (getFields)`](#subactionparams-getfields-1) - [`subActionParams (getIncident)`](#subactionparams-getincident-1) - [`subActionParams (getChoices)`](#subactionparams-getchoices-1) - - [| fields | An array of fields. Example: `[priority, category]`. | string[] |](#-fields----an-array-of-fields-example-priority-category--string-) - - [Jira](#jira) + - [ServiceNow ITOM](#servicenow-itom) - [`params`](#params-2) + - [`subActionParams (addEvent)`](#subactionparams-addevent) + - [`subActionParams (getChoices)`](#subactionparams-getchoices-2) + - [Jira](#jira) + - [`params`](#params-3) - [`subActionParams (pushToService)`](#subactionparams-pushtoservice-2) - [`subActionParams (getIncident)`](#subactionparams-getincident-2) - [`subActionParams (issueTypes)`](#subactionparams-issuetypes) @@ -56,13 +59,13 @@ Table of Contents - [`subActionParams (issue)`](#subactionparams-issue) - [`subActionParams (getFields)`](#subactionparams-getfields-2) - [IBM Resilient](#ibm-resilient) - - [`params`](#params-3) + - [`params`](#params-4) - [`subActionParams (pushToService)`](#subactionparams-pushtoservice-3) - [`subActionParams (getFields)`](#subactionparams-getfields-3) - [`subActionParams (incidentTypes)`](#subactionparams-incidenttypes) - [`subActionParams (severity)`](#subactionparams-severity) - [Swimlane](#swimlane) - - [`params`](#params-4) + - [`params`](#params-5) - [| severity | The severity of the incident. | string _(optional)_ |](#-severity-----the-severity-of-the-incident-----string-optional-) - [Command Line Utility](#command-line-utility) - [Developing New Action Types](#developing-new-action-types) @@ -355,6 +358,43 @@ No parameters for the `getFields` subaction. Provide an empty object `{}`. | Property | Description | Type | | -------- | ---------------------------------------------------- | -------- | | fields | An array of fields. Example: `[priority, category]`. | string[] | + +--- +## ServiceNow ITOM + +The [ServiceNow ITOM user documentation `params`](https://www.elastic.co/guide/en/kibana/master/servicenow-itom-action-type.html) lists configuration properties for the `addEvent` subaction. In addition, several other subaction types are available. +### `params` + +| Property | Description | Type | +| --------------- | ----------------------------------------------------------------- | ------ | +| subAction | The subaction to perform. It can be `addEvent`, and `getChoices`. | string | +| subActionParams | The parameters of the subaction. | object | + +#### `subActionParams (addEvent)` + + +| Property | Description | Type | +| --------------- | -------------------------------------------------------------------------------------------------------------------------------- | ------------------- | +| source | The name of the event source type. | string _(optional)_ | +| event_class | Specific instance of the source. | string _(optional)_ | +| resource | The name of the resource. | string _(optional)_ | +| node | The Host that the event was triggered for. | string _(optional)_ | +| metric_name | Name of the metric. | string _(optional)_ | +| type | The type of event. | string _(optional)_ | +| severity | The category in ServiceNow. | string _(optional)_ | +| description | The subcategory in ServiceNow. | string _(optional)_ | +| additional_info | Any additional information about the event. | string _(optional)_ | +| message_key | This value is used for de-duplication of events. All actions sharing this key will be associated with the same ServiceNow alert. | string _(optional)_ | +| time_of_event | The time of the event. | string _(optional)_ | + +Refer to [ServiceNow documentation](https://docs.servicenow.com/bundle/rome-it-operations-management/page/product/event-management/task/send-events-via-web-service.html) for more information about the properties. + +#### `subActionParams (getChoices)` + +| Property | Description | Type | +| -------- | ------------------------------------------ | -------- | +| fields | An array of fields. Example: `[severity]`. | string[] | + --- ## Jira @@ -418,6 +458,7 @@ No parameters for the `issueTypes` subaction. Provide an empty object `{}`. No parameters for the `getFields` subaction. Provide an empty object `{}`. --- + ## IBM Resilient The [IBM Resilient user documentation `params`](https://www.elastic.co/guide/en/kibana/master/resilient-action-type.html) lists configuration properties for the `pushToService` subaction. In addition, several other subaction types are available. @@ -545,4 +586,4 @@ Instead of `schema.maybe()`, use `schema.nullable()`, which is the same as `sche ## user interface -To make this action usable in the Kibana UI, you will need to provide all the UI editing aspects of the action. The existing action type user interfaces are defined in [`x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types`](../triggers_actions_ui/public/application/components/builtin_action_types). For more information, see the [UI documentation](../triggers_actions_ui/README.md#create-and-register-new-action-type-ui). +To make this action usable in the Kibana UI, you will need to provide all the UI editing aspects of the action. The existing action type user interfaces are defined in [`x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types`](../triggers_actions_ui/public/application/components/builtin_action_types). For more information, see the [UI documentation](../triggers_actions_ui/README.md#create-and-register-new-action-type-ui). \ No newline at end of file diff --git a/x-pack/plugins/actions/server/builtin_action_types/index.ts b/x-pack/plugins/actions/server/builtin_action_types/index.ts index 07859cba4c371..3351a36b38344 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/index.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/index.ts @@ -16,10 +16,15 @@ import { getActionType as getSwimlaneActionType } from './swimlane'; import { getActionType as getServerLogActionType } from './server_log'; import { getActionType as getSlackActionType } from './slack'; import { getActionType as getWebhookActionType } from './webhook'; -import { getServiceNowITSMActionType, getServiceNowSIRActionType } from './servicenow'; +import { + getServiceNowITSMActionType, + getServiceNowSIRActionType, + getServiceNowITOMActionType, +} from './servicenow'; import { getActionType as getJiraActionType } from './jira'; import { getActionType as getResilientActionType } from './resilient'; import { getActionType as getTeamsActionType } from './teams'; +import { ENABLE_ITOM } from '../constants/connectors'; export { ActionParamsType as EmailActionParams, ActionTypeId as EmailActionTypeId } from './email'; export { ActionParamsType as IndexActionParams, @@ -42,6 +47,7 @@ export { ActionParamsType as ServiceNowActionParams, ServiceNowITSMActionTypeId, ServiceNowSIRActionTypeId, + ServiceNowITOMActionTypeId, } from './servicenow'; export { ActionParamsType as JiraActionParams, ActionTypeId as JiraActionTypeId } from './jira'; export { @@ -75,4 +81,9 @@ export function registerBuiltInActionTypes({ actionTypeRegistry.register(getJiraActionType({ logger, configurationUtilities })); actionTypeRegistry.register(getResilientActionType({ logger, configurationUtilities })); actionTypeRegistry.register(getTeamsActionType({ logger, configurationUtilities })); + + // TODO: Remove when ITOM is ready + if (ENABLE_ITOM) { + actionTypeRegistry.register(getServiceNowITOMActionType({ logger, configurationUtilities })); + } } diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/api.test.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/api.test.ts index e1f66263729e2..7969f2e53d3d9 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/api.test.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/api.test.ts @@ -361,6 +361,7 @@ describe('api', () => { const res = await api.getFields({ externalService, params: {}, + logger: mockedLogger, }); expect(res).toEqual(serviceNowCommonFields); }); @@ -371,6 +372,7 @@ describe('api', () => { const res = await api.getChoices({ externalService, params: { fields: ['priority'] }, + logger: mockedLogger, }); expect(res).toEqual(serviceNowChoices); }); @@ -383,6 +385,7 @@ describe('api', () => { params: { externalId: 'incident-1', }, + logger: mockedLogger, }); expect(res).toEqual({ description: 'description from servicenow', diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/api_itom.test.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/api_itom.test.ts new file mode 100644 index 0000000000000..c918c4a52670a --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/api_itom.test.ts @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Logger } from '../../../../../../src/core/server'; +import { externalServiceITOMMock, itomEventParams } from './mocks'; +import { ExternalServiceITOM } from './types'; +import { apiITOM, prepareParams } from './api_itom'; +let mockedLogger: jest.Mocked; + +describe('api_itom', () => { + let externalService: jest.Mocked; + const eventParamsWithFormattedDate = { + ...itomEventParams, + time_of_event: '2021-10-13, 10:51:44', + }; + + beforeEach(() => { + externalService = externalServiceITOMMock.create(); + jest.clearAllMocks(); + }); + + describe('prepareParams', () => { + test('it prepares the params correctly', async () => { + expect(prepareParams(itomEventParams)).toEqual(eventParamsWithFormattedDate); + }); + + test('it removes null values', async () => { + const { time_of_event: timeOfEvent, ...rest } = itomEventParams; + expect(prepareParams({ ...rest, time_of_event: null })).toEqual(rest); + }); + + test('it set the time to null if it is not a proper date', async () => { + const { time_of_event: timeOfEvent, ...rest } = itomEventParams; + expect(prepareParams({ ...rest, time_of_event: 'not a proper date' })).toEqual(rest); + }); + }); + + describe('addEvent', () => { + test('it adds an event correctly', async () => { + await apiITOM.addEvent({ + externalService, + params: itomEventParams, + logger: mockedLogger, + }); + + expect(externalService.addEvent).toHaveBeenCalledWith(eventParamsWithFormattedDate); + }); + }); +}); diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/api_itom.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/api_itom.ts new file mode 100644 index 0000000000000..668e17a042718 --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/api_itom.ts @@ -0,0 +1,70 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { api } from './api'; +import { + ExecutorSubActionAddEventParams, + AddEventApiHandlerArgs, + ExternalServiceApiITOM, +} from './types'; + +const isValidDate = (d: Date) => !isNaN(d.valueOf()); + +const formatTimeOfEvent = (timeOfEvent: string | null): string | undefined => { + if (timeOfEvent != null) { + const date = new Date(timeOfEvent); + + return isValidDate(date) + ? // The format is: yyyy-MM-dd HH:mm:ss GMT + date.toLocaleDateString('en-CA', { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + hour12: false, + minute: '2-digit', + second: '2-digit', + timeZone: 'GMT', + }) + : undefined; + } +}; + +const removeNullValues = ( + params: ExecutorSubActionAddEventParams +): ExecutorSubActionAddEventParams => + (Object.keys(params) as Array).reduce( + (acc, key) => ({ + ...acc, + ...(params[key] != null ? { [key]: params[key] } : {}), + }), + {} as ExecutorSubActionAddEventParams + ); + +export const prepareParams = ( + params: ExecutorSubActionAddEventParams +): ExecutorSubActionAddEventParams => { + const timeOfEvent = formatTimeOfEvent(params.time_of_event); + return removeNullValues({ + ...params, + time_of_event: timeOfEvent ?? null, + }); +}; + +const addEventServiceHandler = async ({ + externalService, + params, +}: AddEventApiHandlerArgs): Promise => { + const itomExternalService = externalService; + const preparedParams = prepareParams(params); + await itomExternalService.addEvent(preparedParams); +}; + +export const apiITOM: ExternalServiceApiITOM = { + getChoices: api.getChoices, + addEvent: addEventServiceHandler, +}; diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/config.test.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/config.test.ts index babd360cbcb82..41f723bc9e2aa 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/config.test.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/config.test.ts @@ -37,4 +37,15 @@ describe('config', () => { commentFieldKey: 'work_notes', }); }); + + test('ITOM: the config are correct', async () => { + const snConfig = snExternalServiceConfig['.servicenow-itom']; + expect(snConfig).toEqual({ + importSetTable: 'x_elas2_inc_int_elastic_incident', + appScope: 'x_elas2_inc_int', + table: 'em_event', + useImportAPI: true, + commentFieldKey: 'work_notes', + }); + }); }); diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/config.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/config.ts index 37e4c6994b403..52d2eb7662f53 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/config.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/config.ts @@ -6,6 +6,7 @@ */ import { + ENABLE_ITOM, ENABLE_NEW_SN_ITSM_CONNECTOR, ENABLE_NEW_SN_SIR_CONNECTOR, } from '../../constants/connectors'; @@ -16,6 +17,7 @@ export const serviceNowSIRTable = 'sn_si_incident'; export const ServiceNowITSMActionTypeId = '.servicenow'; export const ServiceNowSIRActionTypeId = '.servicenow-sir'; +export const ServiceNowITOMActionTypeId = '.servicenow-itom'; export const snExternalServiceConfig: SNProductsConfig = { '.servicenow': { @@ -32,6 +34,14 @@ export const snExternalServiceConfig: SNProductsConfig = { useImportAPI: ENABLE_NEW_SN_SIR_CONNECTOR, commentFieldKey: 'work_notes', }, + '.servicenow-itom': { + importSetTable: 'x_elas2_inc_int_elastic_incident', + appScope: 'x_elas2_inc_int', + table: 'em_event', + useImportAPI: ENABLE_ITOM, + commentFieldKey: 'work_notes', + }, }; export const FIELD_PREFIX = 'u_'; +export const DEFAULT_ALERTS_GROUPING_KEY = '{{rule.id}}:{{alert.id}}'; diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/index.test.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/index.test.ts index b342844033994..6ba8b80dfc09c 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/index.test.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/index.test.ts @@ -8,7 +8,7 @@ import { actionsMock } from '../../mocks'; import { createActionTypeRegistry } from '../index.test'; import { - ServiceNowPublicConfigurationType, + ServiceNowPublicConfigurationBaseType, ServiceNowSecretConfigurationType, ExecutorParams, PushToServiceResponse, @@ -56,7 +56,7 @@ describe('ServiceNow', () => { beforeAll(() => { const { actionTypeRegistry } = createActionTypeRegistry(); actionType = actionTypeRegistry.get< - ServiceNowPublicConfigurationType, + ServiceNowPublicConfigurationBaseType, ServiceNowSecretConfigurationType, ExecutorParams, PushToServiceResponse | {} @@ -91,7 +91,7 @@ describe('ServiceNow', () => { beforeAll(() => { const { actionTypeRegistry } = createActionTypeRegistry(); actionType = actionTypeRegistry.get< - ServiceNowPublicConfigurationType, + ServiceNowPublicConfigurationBaseType, ServiceNowSecretConfigurationType, ExecutorParams, PushToServiceResponse | {} diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/index.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/index.ts index 29907381d45da..1e07cf858f332 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/index.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/index.ts @@ -11,9 +11,11 @@ import { schema, TypeOf } from '@kbn/config-schema'; import { validate } from './validators'; import { ExternalIncidentServiceConfiguration, + ExternalIncidentServiceConfigurationBase, ExternalIncidentServiceSecretConfiguration, ExecutorParamsSchemaITSM, ExecutorParamsSchemaSIR, + ExecutorParamsSchemaITOM, } from './schema'; import { ActionsConfigurationUtilities } from '../../actions_config'; import { ActionType, ActionTypeExecutorOptions, ActionTypeExecutorResult } from '../../types'; @@ -32,8 +34,14 @@ import { ExecutorSubActionGetChoicesParams, ServiceFactory, ExternalServiceAPI, + ExecutorParamsITOM, + ExecutorSubActionAddEventParams, + ExternalServiceApiITOM, + ExternalServiceITOM, + ServiceNowPublicConfigurationBaseType, } from './types'; import { + ServiceNowITOMActionTypeId, ServiceNowITSMActionTypeId, serviceNowITSMTable, ServiceNowSIRActionTypeId, @@ -42,12 +50,16 @@ import { } from './config'; import { createExternalServiceSIR } from './service_sir'; import { apiSIR } from './api_sir'; +import { throwIfSubActionIsNotSupported } from './utils'; +import { createExternalServiceITOM } from './service_itom'; +import { apiITOM } from './api_itom'; export { ServiceNowITSMActionTypeId, serviceNowITSMTable, ServiceNowSIRActionTypeId, serviceNowSIRTable, + ServiceNowITOMActionTypeId, }; export type ActionParamsType = @@ -59,21 +71,20 @@ interface GetActionTypeParams { configurationUtilities: ActionsConfigurationUtilities; } -export type ServiceNowActionType = ActionType< - ServiceNowPublicConfigurationType, - ServiceNowSecretConfigurationType, - ExecutorParams, - PushToServiceResponse | {} ->; +export type ServiceNowActionType< + C extends Record = ServiceNowPublicConfigurationBaseType, + T extends Record = ExecutorParams +> = ActionType; -export type ServiceNowActionTypeExecutorOptions = ActionTypeExecutorOptions< - ServiceNowPublicConfigurationType, - ServiceNowSecretConfigurationType, - ExecutorParams ->; +export type ServiceNowActionTypeExecutorOptions< + C extends Record = ServiceNowPublicConfigurationBaseType, + T extends Record = ExecutorParams +> = ActionTypeExecutorOptions; // action type definition -export function getServiceNowITSMActionType(params: GetActionTypeParams): ServiceNowActionType { +export function getServiceNowITSMActionType( + params: GetActionTypeParams +): ServiceNowActionType { const { logger, configurationUtilities } = params; return { id: ServiceNowITSMActionTypeId, @@ -98,7 +109,9 @@ export function getServiceNowITSMActionType(params: GetActionTypeParams): Servic }; } -export function getServiceNowSIRActionType(params: GetActionTypeParams): ServiceNowActionType { +export function getServiceNowSIRActionType( + params: GetActionTypeParams +): ServiceNowActionType { const { logger, configurationUtilities } = params; return { id: ServiceNowSIRActionTypeId, @@ -123,6 +136,33 @@ export function getServiceNowSIRActionType(params: GetActionTypeParams): Service }; } +export function getServiceNowITOMActionType( + params: GetActionTypeParams +): ServiceNowActionType { + const { logger, configurationUtilities } = params; + return { + id: ServiceNowITOMActionTypeId, + minimumLicenseRequired: 'platinum', + name: i18n.SERVICENOW_ITOM, + validate: { + config: schema.object(ExternalIncidentServiceConfigurationBase, { + validate: curry(validate.config)(configurationUtilities), + }), + secrets: schema.object(ExternalIncidentServiceSecretConfiguration, { + validate: curry(validate.secrets)(configurationUtilities), + }), + params: ExecutorParamsSchemaITOM, + }, + executor: curry(executorITOM)({ + logger, + configurationUtilities, + actionTypeId: ServiceNowITOMActionTypeId, + createService: createExternalServiceITOM, + api: apiITOM, + }), + }; +} + // action executor const supportedSubActions: string[] = ['getFields', 'pushToService', 'getChoices', 'getIncident']; async function executor( @@ -139,7 +179,10 @@ async function executor( createService: ServiceFactory; api: ExternalServiceAPI; }, - execOptions: ServiceNowActionTypeExecutorOptions + execOptions: ServiceNowActionTypeExecutorOptions< + ServiceNowPublicConfigurationType, + ExecutorParams + > ): Promise> { const { actionId, config, params, secrets } = execOptions; const { subAction, subActionParams } = params; @@ -156,17 +199,8 @@ async function executor( externalServiceConfig ); - if (!api[subAction]) { - const errorMessage = `[Action][ExternalService] Unsupported subAction type ${subAction}.`; - logger.error(errorMessage); - throw new Error(errorMessage); - } - - if (!supportedSubActions.includes(subAction)) { - const errorMessage = `[Action][ExternalService] subAction ${subAction} not implemented.`; - logger.error(errorMessage); - throw new Error(errorMessage); - } + const apiAsRecord = api as unknown as Record; + throwIfSubActionIsNotSupported({ api: apiAsRecord, subAction, supportedSubActions, logger }); if (subAction === 'pushToService') { const pushToServiceParams = subActionParams as ExecutorSubActionPushParams; @@ -187,6 +221,7 @@ async function executor( data = await api.getFields({ externalService, params: getFieldsParams, + logger, }); } @@ -195,6 +230,73 @@ async function executor( data = await api.getChoices({ externalService, params: getChoicesParams, + logger, + }); + } + + return { status: 'ok', data: data ?? {}, actionId }; +} + +const supportedSubActionsITOM = ['addEvent', 'getChoices']; + +async function executorITOM( + { + logger, + configurationUtilities, + actionTypeId, + createService, + api, + }: { + logger: Logger; + configurationUtilities: ActionsConfigurationUtilities; + actionTypeId: string; + createService: ServiceFactory; + api: ExternalServiceApiITOM; + }, + execOptions: ServiceNowActionTypeExecutorOptions< + ServiceNowPublicConfigurationBaseType, + ExecutorParamsITOM + > +): Promise> { + const { actionId, config, params, secrets } = execOptions; + const { subAction, subActionParams } = params; + const externalServiceConfig = snExternalServiceConfig[actionTypeId]; + let data: ServiceNowExecutorResultData | null = null; + + const externalService = createService( + { + config, + secrets, + }, + logger, + configurationUtilities, + externalServiceConfig + ) as ExternalServiceITOM; + + const apiAsRecord = api as unknown as Record; + + throwIfSubActionIsNotSupported({ + api: apiAsRecord, + subAction, + supportedSubActions: supportedSubActionsITOM, + logger, + }); + + if (subAction === 'addEvent') { + const eventParams = subActionParams as ExecutorSubActionAddEventParams; + await api.addEvent({ + externalService, + params: eventParams, + logger, + }); + } + + if (subAction === 'getChoices') { + const getChoicesParams = subActionParams as ExecutorSubActionGetChoicesParams; + data = await api.getChoices({ + externalService, + params: getChoicesParams, + logger, }); } diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/mocks.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/mocks.ts index 3629fb33915ae..1043fe62af1e1 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/mocks.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/mocks.ts @@ -12,6 +12,8 @@ import { ExternalServiceSIR, Observable, ObservableTypes, + ExternalServiceITOM, + ExecutorSubActionAddEventParams, } from './types'; export const serviceNowCommonFields = [ @@ -151,6 +153,16 @@ const createSIRMock = (): jest.Mocked => { return service; }; +const createITOMMock = (): jest.Mocked => { + const serviceMock = createMock(); + const service = { + getChoices: serviceMock.getChoices, + addEvent: jest.fn().mockImplementation(() => Promise.resolve()), + }; + + return service; +}; + export const externalServiceMock = { create: createMock, }; @@ -159,6 +171,10 @@ export const externalServiceSIRMock = { create: createSIRMock, }; +export const externalServiceITOMMock = { + create: createITOMMock, +}; + export const executorParams: ExecutorSubActionPushParams = { incident: { externalId: 'incident-3', @@ -227,3 +243,17 @@ export const observables: Observable[] = [ ]; export const apiParams = executorParams; + +export const itomEventParams: ExecutorSubActionAddEventParams = { + source: 'A source', + event_class: 'An event class', + resource: 'C:', + node: 'node.example.com', + metric_name: 'Percentage Logical Disk Free Space', + type: 'Disk space', + severity: '4', + description: 'desc', + additional_info: '{"alert": "test"}', + message_key: 'a key', + time_of_event: '2021-10-13T10:51:44.981Z', +}; diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/schema.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/schema.ts index dab68bb9d3e9d..5f57555a8f9e1 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/schema.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/schema.ts @@ -6,12 +6,21 @@ */ import { schema } from '@kbn/config-schema'; +import { DEFAULT_ALERTS_GROUPING_KEY } from './config'; -export const ExternalIncidentServiceConfiguration = { +export const ExternalIncidentServiceConfigurationBase = { apiUrl: schema.string(), +}; + +export const ExternalIncidentServiceConfiguration = { + ...ExternalIncidentServiceConfigurationBase, isLegacy: schema.boolean({ defaultValue: false }), }; +export const ExternalIncidentServiceConfigurationBaseSchema = schema.object( + ExternalIncidentServiceConfigurationBase +); + export const ExternalIncidentServiceConfigurationSchema = schema.object( ExternalIncidentServiceConfiguration ); @@ -80,6 +89,21 @@ export const ExecutorSubActionPushParamsSchemaSIR = schema.object({ comments: CommentsSchema, }); +// Schema for ServiceNow ITOM +export const ExecutorSubActionAddEventParamsSchema = schema.object({ + source: schema.nullable(schema.string()), + event_class: schema.nullable(schema.string()), + resource: schema.nullable(schema.string()), + node: schema.nullable(schema.string()), + metric_name: schema.nullable(schema.string()), + type: schema.nullable(schema.string()), + severity: schema.nullable(schema.string()), + description: schema.nullable(schema.string()), + additional_info: schema.nullable(schema.string()), + message_key: schema.nullable(schema.string({ defaultValue: DEFAULT_ALERTS_GROUPING_KEY })), + time_of_event: schema.nullable(schema.string()), +}); + export const ExecutorSubActionGetIncidentParamsSchema = schema.object({ externalId: schema.string(), }); @@ -138,3 +162,15 @@ export const ExecutorParamsSchemaSIR = schema.oneOf([ subActionParams: ExecutorSubActionGetChoicesParamsSchema, }), ]); + +// Executor parameters for ITOM +export const ExecutorParamsSchemaITOM = schema.oneOf([ + schema.object({ + subAction: schema.literal('addEvent'), + subActionParams: ExecutorSubActionAddEventParamsSchema, + }), + schema.object({ + subAction: schema.literal('getChoices'), + subActionParams: ExecutorSubActionGetChoicesParamsSchema, + }), +]); diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/service_itom.test.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/service_itom.test.ts new file mode 100644 index 0000000000000..5223add79d301 --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/service_itom.test.ts @@ -0,0 +1,90 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import axios from 'axios'; + +import { createExternalServiceITOM } from './service_itom'; +import * as utils from '../lib/axios_utils'; +import { ExternalServiceITOM } from './types'; +import { Logger } from '../../../../../../src/core/server'; +import { loggingSystemMock } from '../../../../../../src/core/server/mocks'; +import { actionsConfigMock } from '../../actions_config.mock'; +import { snExternalServiceConfig } from './config'; +import { itomEventParams, serviceNowChoices } from './mocks'; + +const logger = loggingSystemMock.create().get() as jest.Mocked; + +jest.mock('axios'); +jest.mock('../lib/axios_utils', () => { + const originalUtils = jest.requireActual('../lib/axios_utils'); + return { + ...originalUtils, + request: jest.fn(), + }; +}); + +axios.create = jest.fn(() => axios); +const requestMock = utils.request as jest.Mock; +const configurationUtilities = actionsConfigMock.create(); + +describe('ServiceNow SIR service', () => { + let service: ExternalServiceITOM; + + beforeEach(() => { + service = createExternalServiceITOM( + { + config: { apiUrl: 'https://example.com/' }, + secrets: { username: 'admin', password: 'admin' }, + }, + logger, + configurationUtilities, + snExternalServiceConfig['.servicenow-itom'] + ) as ExternalServiceITOM; + }); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('addEvent', () => { + test('it adds an event', async () => { + requestMock.mockImplementationOnce(() => ({ + data: { + result: { + 'Default Bulk Endpoint': '1 events were inserted', + }, + }, + })); + + await service.addEvent(itomEventParams); + expect(requestMock).toHaveBeenCalledWith({ + axios, + logger, + configurationUtilities, + url: 'https://example.com/api/global/em/jsonv2', + method: 'post', + data: { records: [itomEventParams] }, + }); + }); + }); + + describe('getChoices', () => { + test('it should call request with correct arguments', async () => { + requestMock.mockImplementation(() => ({ + data: { result: serviceNowChoices }, + })); + await service.getChoices(['severity']); + + expect(requestMock).toHaveBeenCalledWith({ + axios, + logger, + configurationUtilities, + url: 'https://example.com/api/now/table/sys_choice?sysparm_query=name=task^ORname=em_event^element=severity&sysparm_fields=label,value,dependent_value,element', + }); + }); + }); +}); diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/service_itom.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/service_itom.ts new file mode 100644 index 0000000000000..aa135e07dbc64 --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/service_itom.ts @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import axios from 'axios'; + +import { + ExternalServiceCredentials, + SNProductsConfigValue, + ServiceFactory, + ExternalServiceITOM, + ExecutorSubActionAddEventParams, +} from './types'; + +import { Logger } from '../../../../../../src/core/server'; +import { ServiceNowSecretConfigurationType } from './types'; +import { request } from '../lib/axios_utils'; +import { ActionsConfigurationUtilities } from '../../actions_config'; +import { createExternalService } from './service'; +import { createServiceError } from './utils'; + +const getAddEventURL = (url: string) => `${url}/api/global/em/jsonv2`; + +export const createExternalServiceITOM: ServiceFactory = ( + credentials: ExternalServiceCredentials, + logger: Logger, + configurationUtilities: ActionsConfigurationUtilities, + serviceConfig: SNProductsConfigValue +): ExternalServiceITOM => { + const snService = createExternalService( + credentials, + logger, + configurationUtilities, + serviceConfig + ); + + const { username, password } = credentials.secrets as ServiceNowSecretConfigurationType; + const axiosInstance = axios.create({ + auth: { username, password }, + }); + + const addEvent = async (params: ExecutorSubActionAddEventParams) => { + try { + const res = await request({ + axios: axiosInstance, + url: getAddEventURL(snService.getUrl()), + logger, + method: 'post', + data: { records: [params] }, + configurationUtilities, + }); + + snService.checkInstance(res); + } catch (error) { + throw createServiceError(error, `Unable to add event`); + } + }; + + return { + addEvent, + getChoices: snService.getChoices, + }; +}; diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/service_sir.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/service_sir.ts index fc8d8cc555bc8..03433f11f9465 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/service_sir.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/service_sir.ts @@ -29,7 +29,7 @@ const getAddObservableToIncidentURL = (url: string, incidentID: string) => const getBulkAddObservableToIncidentURL = (url: string, incidentID: string) => `${url}/api/x_elas2_sir_int/elastic_api/incident/${incidentID}/observables/bulk`; -export const createExternalServiceSIR: ServiceFactory = ( +export const createExternalServiceSIR: ServiceFactory = ( credentials: ExternalServiceCredentials, logger: Logger, configurationUtilities: ActionsConfigurationUtilities, diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/translations.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/translations.ts index b46e118a7235f..8b2bb9423d012 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/translations.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/translations.ts @@ -19,6 +19,10 @@ export const SERVICENOW_SIR = i18n.translate('xpack.actions.builtin.serviceNowSI defaultMessage: 'ServiceNow SecOps', }); +export const SERVICENOW_ITOM = i18n.translate('xpack.actions.builtin.serviceNowITOMTitle', { + defaultMessage: 'ServiceNow ITOM', +}); + export const ALLOWED_HOSTS_ERROR = (message: string) => i18n.translate('xpack.actions.builtin.configuration.apiAllowedHostsError', { defaultMessage: 'error configuring connector action: {message}', diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/types.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/types.ts index ecca1e55e0fec..31af3781c6b04 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/types.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/types.ts @@ -20,13 +20,21 @@ import { ExecutorParamsSchemaSIR, ExecutorSubActionPushParamsSchemaSIR, ExecutorSubActionGetChoicesParamsSchema, + ExecutorParamsSchemaITOM, + ExecutorSubActionAddEventParamsSchema, + ExternalIncidentServiceConfigurationBaseSchema, } from './schema'; import { ActionsConfigurationUtilities } from '../../actions_config'; import { Logger } from '../../../../../../src/core/server'; +export type ServiceNowPublicConfigurationBaseType = TypeOf< + typeof ExternalIncidentServiceConfigurationBaseSchema +>; + export type ServiceNowPublicConfigurationType = TypeOf< typeof ExternalIncidentServiceConfigurationSchema >; + export type ServiceNowSecretConfigurationType = TypeOf< typeof ExternalIncidentServiceSecretConfigurationSchema >; @@ -108,8 +116,9 @@ export type PushToServiceApiParams = ExecutorSubActionPushParams; export type PushToServiceApiParamsITSM = ExecutorSubActionPushParamsITSM; export type PushToServiceApiParamsSIR = ExecutorSubActionPushParamsSIR; -export interface ExternalServiceApiHandlerArgs { - externalService: ExternalService; +export interface ExternalServiceApiHandlerArgs { + externalService: T; + logger: Logger; } export type ExecutorSubActionGetIncidentParams = TypeOf< @@ -134,7 +143,6 @@ export interface PushToServiceApiHandlerArgs extends ExternalServiceApiHandlerAr params: PushToServiceApiParams; config: Record; secrets: Record; - logger: Logger; commentFieldKey: string; } @@ -162,13 +170,13 @@ export interface ExternalServiceChoices { export type GetCommonFieldsResponse = ExternalServiceFields[]; export type GetChoicesResponse = ExternalServiceChoices[]; -export interface GetCommonFieldsHandlerArgs { - externalService: ExternalService; +export interface GetCommonFieldsHandlerArgs extends ExternalServiceApiHandlerArgs { params: ExecutorSubActionCommonFieldsParams; } export interface GetChoicesHandlerArgs { - externalService: ExternalService; + externalService: Partial & { getChoices: ExternalService['getChoices'] }; + logger: Logger; params: ExecutorSubActionGetChoicesParams; } @@ -276,9 +284,36 @@ export interface ExternalServiceSIR extends ExternalService { ) => Promise; } -export type ServiceFactory = ( +export type ServiceFactory = ( credentials: ExternalServiceCredentials, logger: Logger, configurationUtilities: ActionsConfigurationUtilities, serviceConfig: SNProductsConfigValue -) => ExternalServiceSIR | ExternalService; +) => T; + +/** + * ITOM + */ + +export type ExecutorSubActionAddEventParams = TypeOf; + +export interface ExternalServiceITOM { + getChoices: ExternalService['getChoices']; + addEvent: (params: ExecutorSubActionAddEventParams) => Promise; +} + +export interface AddEventApiHandlerArgs extends ExternalServiceApiHandlerArgs { + params: ExecutorSubActionAddEventParams; +} + +export interface GetCommonFieldsHandlerArgsITOM + extends ExternalServiceApiHandlerArgs { + params: ExecutorSubActionGetChoicesParams; +} + +export interface ExternalServiceApiITOM { + getChoices: ExternalServiceAPI['getChoices']; + addEvent: (args: AddEventApiHandlerArgs) => Promise; +} + +export type ExecutorParamsITOM = TypeOf; diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/utils.test.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/utils.test.ts index 87f27da6d213f..3eaf5305d5d26 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/utils.test.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/utils.test.ts @@ -6,7 +6,17 @@ */ import { AxiosError } from 'axios'; -import { prepareIncident, createServiceError, getPushedDate } from './utils'; + +import { Logger } from '../../../../../../src/core/server'; +import { loggingSystemMock } from '../../../../../../src/core/server/mocks'; +import { + prepareIncident, + createServiceError, + getPushedDate, + throwIfSubActionIsNotSupported, +} from './utils'; + +const logger = loggingSystemMock.create().get() as jest.Mocked; /** * The purpose of this test is to @@ -15,7 +25,6 @@ import { prepareIncident, createServiceError, getPushedDate } from './utils'; * such as the scope or the import set table * of our ServiceNow application */ - describe('utils', () => { describe('prepareIncident', () => { test('it prepares the incident correctly when useOldApi=false', async () => { @@ -81,4 +90,45 @@ describe('utils', () => { expect(getPushedDate()).toBe('2021-10-04T11:15:06.000Z'); }); }); + + describe('throwIfSubActionIsNotSupported', () => { + const api = { pushToService: 'whatever' }; + + test('it throws correctly if the subAction is not supported', async () => { + expect.assertions(1); + + expect(() => + throwIfSubActionIsNotSupported({ + api, + subAction: 'addEvent', + supportedSubActions: ['getChoices'], + logger, + }) + ).toThrow('[Action][ExternalService] Unsupported subAction type addEvent'); + }); + + test('it throws correctly if the subAction is not implemented', async () => { + expect.assertions(1); + + expect(() => + throwIfSubActionIsNotSupported({ + api, + subAction: 'pushToService', + supportedSubActions: ['getChoices'], + logger, + }) + ).toThrow('[Action][ExternalService] subAction pushToService not implemented.'); + }); + + test('it does not throw if the sub action is supported and implemented', async () => { + expect(() => + throwIfSubActionIsNotSupported({ + api, + subAction: 'pushToService', + supportedSubActions: ['pushToService'], + logger, + }) + ).not.toThrow(); + }); + }); }); diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/utils.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/utils.ts index 5b7ca99ffc709..3bd4864b71e7a 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/utils.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/utils.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { Logger } from '../../../../../../src/core/server'; import { Incident, PartialIncident, ResponseError, ServiceNowError } from './types'; import { FIELD_PREFIX } from './config'; import { addTimeZoneToDate, getErrorMessage } from '../lib/axios_utils'; @@ -44,3 +45,27 @@ export const getPushedDate = (timestamp?: string) => { return new Date().toISOString(); }; + +export const throwIfSubActionIsNotSupported = ({ + api, + subAction, + supportedSubActions, + logger, +}: { + api: Record; + subAction: string; + supportedSubActions: string[]; + logger: Logger; +}) => { + if (!api[subAction]) { + const errorMessage = `[Action][ExternalService] Unsupported subAction type ${subAction}.`; + logger.error(errorMessage); + throw new Error(errorMessage); + } + + if (!supportedSubActions.includes(subAction)) { + const errorMessage = `[Action][ExternalService] subAction ${subAction} not implemented.`; + logger.error(errorMessage); + throw new Error(errorMessage); + } +}; diff --git a/x-pack/plugins/actions/server/constants/connectors.ts b/x-pack/plugins/actions/server/constants/connectors.ts index f20d499716cf0..94324e4d82bc2 100644 --- a/x-pack/plugins/actions/server/constants/connectors.ts +++ b/x-pack/plugins/actions/server/constants/connectors.ts @@ -10,3 +10,6 @@ export const ENABLE_NEW_SN_ITSM_CONNECTOR = true; // TODO: Remove when Elastic for Security Operations is published. export const ENABLE_NEW_SN_SIR_CONNECTOR = true; + +// TODO: Remove when ready +export const ENABLE_ITOM = true; diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts index 6e8d574c15860..442718e0975ee 100644 --- a/x-pack/plugins/security_solution/common/constants.ts +++ b/x-pack/plugins/security_solution/common/constants.ts @@ -5,6 +5,8 @@ * 2.0. */ +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { ENABLE_ITOM } from '../../actions/server/constants/connectors'; import type { TransformConfigSchema } from './transforms/types'; import { ENABLE_CASE_CONNECTOR } from '../../cases/common'; import { METADATA_TRANSFORMS_PATTERN } from './endpoint/constants'; @@ -312,6 +314,11 @@ if (ENABLE_CASE_CONNECTOR) { NOTIFICATION_SUPPORTED_ACTION_TYPES_IDS.push('.case'); } +// TODO: Remove when ITOM is ready +if (ENABLE_ITOM) { + NOTIFICATION_SUPPORTED_ACTION_TYPES_IDS.push('.servicenow-itom'); +} + export const NOTIFICATION_THROTTLE_NO_ACTIONS = 'no_actions'; export const NOTIFICATION_THROTTLE_RULE = 'rule'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/index.ts index 4266822bda1fc..d9bad9677c6b8 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/index.ts @@ -14,10 +14,16 @@ import { getSwimlaneActionType } from './swimlane'; import { getWebhookActionType } from './webhook'; import { TypeRegistry } from '../../type_registry'; import { ActionTypeModel } from '../../../types'; -import { getServiceNowITSMActionType, getServiceNowSIRActionType } from './servicenow'; +import { + getServiceNowITSMActionType, + getServiceNowSIRActionType, + getServiceNowITOMActionType, +} from './servicenow'; import { getJiraActionType } from './jira'; import { getResilientActionType } from './resilient'; import { getTeamsActionType } from './teams'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { ENABLE_ITOM } from '../../../../../actions/server/constants/connectors'; export function registerBuiltInActionTypes({ actionTypeRegistry, @@ -36,4 +42,9 @@ export function registerBuiltInActionTypes({ actionTypeRegistry.register(getJiraActionType()); actionTypeRegistry.register(getResilientActionType()); actionTypeRegistry.register(getTeamsActionType()); + + // TODO: Remove when ITOM is ready + if (ENABLE_ITOM) { + actionTypeRegistry.register(getServiceNowITOMActionType()); + } } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/helpers.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/helpers.test.ts index e37d8dd3b4147..e40db85bcb12d 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/helpers.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/helpers.test.ts @@ -35,6 +35,10 @@ describe('helpers', () => { expect(isFieldInvalid(undefined, ['required'])).toBeFalsy(); }); + test('should return if false the field is null', async () => { + expect(isFieldInvalid(null, ['required'])).toBeFalsy(); + }); + test('should return if false the error is not defined', async () => { // @ts-expect-error expect(isFieldInvalid('description', undefined)).toBeFalsy(); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/helpers.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/helpers.ts index 0134133645bb3..e6acb2e0976a8 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/helpers.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/helpers.ts @@ -23,9 +23,9 @@ export const isRESTApiError = (res: AppInfo | RESTApiError): res is RESTApiError (res as RESTApiError).error != null || (res as RESTApiError).status === 'failure'; export const isFieldInvalid = ( - field: string | undefined, + field: string | undefined | null, error: string | IErrorObject | string[] -): boolean => error !== undefined && error.length > 0 && field !== undefined; +): boolean => error !== undefined && error.length > 0 && field != null; // TODO: Remove when the applications are certified export const isLegacyConnector = (connector: ServiceNowActionConnector) => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/index.ts index b5b006b764e41..c313fd5d0edd6 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/index.ts @@ -5,4 +5,8 @@ * 2.0. */ -export { getServiceNowITSMActionType, getServiceNowSIRActionType } from './servicenow'; +export { + getServiceNowITSMActionType, + getServiceNowSIRActionType, + getServiceNowITOMActionType, +} from './servicenow'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow.test.tsx index b40db9c2dabda..eb3e1c01887c9 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow.test.tsx @@ -12,6 +12,7 @@ import { ServiceNowActionConnector } from './types'; const SERVICENOW_ITSM_ACTION_TYPE_ID = '.servicenow'; const SERVICENOW_SIR_ACTION_TYPE_ID = '.servicenow-sir'; +const SERVICENOW_ITOM_ACTION_TYPE_ID = '.servicenow-itom'; let actionTypeRegistry: TypeRegistry; beforeAll(() => { @@ -20,7 +21,11 @@ beforeAll(() => { }); describe('actionTypeRegistry.get() works', () => { - [SERVICENOW_ITSM_ACTION_TYPE_ID, SERVICENOW_SIR_ACTION_TYPE_ID].forEach((id) => { + [ + SERVICENOW_ITSM_ACTION_TYPE_ID, + SERVICENOW_SIR_ACTION_TYPE_ID, + SERVICENOW_ITOM_ACTION_TYPE_ID, + ].forEach((id) => { test(`${id}: action type static data is as expected`, () => { const actionTypeModel = actionTypeRegistry.get(id); expect(actionTypeModel.id).toEqual(id); @@ -29,7 +34,11 @@ describe('actionTypeRegistry.get() works', () => { }); describe('servicenow connector validation', () => { - [SERVICENOW_ITSM_ACTION_TYPE_ID, SERVICENOW_SIR_ACTION_TYPE_ID].forEach((id) => { + [ + SERVICENOW_ITSM_ACTION_TYPE_ID, + SERVICENOW_SIR_ACTION_TYPE_ID, + SERVICENOW_ITOM_ACTION_TYPE_ID, + ].forEach((id) => { test(`${id}: connector validation succeeds when connector config is valid`, async () => { const actionTypeModel = actionTypeRegistry.get(id); const actionConnector = { @@ -106,7 +115,7 @@ describe('servicenow action params validation', () => { }); }); - test(`${id}: params validation fails when body is not valid`, async () => { + test(`${id}: params validation fails when short_description is not valid`, async () => { const actionTypeModel = actionTypeRegistry.get(id); const actionParams = { subActionParams: { incident: { short_description: '' }, comments: [] }, @@ -119,4 +128,22 @@ describe('servicenow action params validation', () => { }); }); }); + + test(`${SERVICENOW_ITOM_ACTION_TYPE_ID}: action params validation succeeds when action params is valid`, async () => { + const actionTypeModel = actionTypeRegistry.get(SERVICENOW_ITOM_ACTION_TYPE_ID); + const actionParams = { subActionParams: { severity: 'Critical' } }; + + expect(await actionTypeModel.validateParams(actionParams)).toEqual({ + errors: { ['severity']: [] }, + }); + }); + + test(`${SERVICENOW_ITOM_ACTION_TYPE_ID}: params validation fails when severity is not valid`, async () => { + const actionTypeModel = actionTypeRegistry.get(SERVICENOW_ITOM_ACTION_TYPE_ID); + const actionParams = { subActionParams: { severity: null } }; + + expect(await actionTypeModel.validateParams(actionParams)).toEqual({ + errors: { ['severity']: ['Severity is required.'] }, + }); + }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow.tsx index bb4a645f10bbc..6b6d536ff303b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow.tsx @@ -18,6 +18,7 @@ import { ServiceNowSecrets, ServiceNowITSMActionParams, ServiceNowSIRActionParams, + ServiceNowITOMActionParams, } from './types'; import { isValidUrl } from '../../../lib/value_validators'; @@ -90,6 +91,20 @@ export const SERVICENOW_SIR_TITLE = i18n.translate( } ); +export const SERVICENOW_ITOM_TITLE = i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.serviceNowITOM.actionTypeTitle', + { + defaultMessage: 'ServiceNow ITOM', + } +); + +export const SERVICENOW_ITOM_DESC = i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.serviceNowITOM.selectMessageText', + { + defaultMessage: 'Create an event in ServiceNow ITOM.', + } +); + export function getServiceNowITSMActionType(): ActionTypeModel< ServiceNowConfig, ServiceNowSecrets, @@ -161,3 +176,34 @@ export function getServiceNowSIRActionType(): ActionTypeModel< actionParamsFields: lazy(() => import('./servicenow_sir_params')), }; } + +export function getServiceNowITOMActionType(): ActionTypeModel< + ServiceNowConfig, + ServiceNowSecrets, + ServiceNowITOMActionParams +> { + return { + id: '.servicenow-itom', + iconClass: lazy(() => import('./logo')), + selectMessage: SERVICENOW_ITOM_DESC, + actionTypeTitle: SERVICENOW_ITOM_TITLE, + validateConnector, + actionConnectorFields: lazy(() => import('./servicenow_connectors_no_app')), + validateParams: async ( + actionParams: ServiceNowITOMActionParams + ): Promise> => { + const translations = await import('./translations'); + const errors = { + severity: new Array(), + }; + const validationResult = { errors }; + + if (actionParams?.subActionParams?.severity == null) { + errors.severity.push(translations.SEVERITY_REQUIRED); + } + + return validationResult; + }, + actionParamsFields: lazy(() => import('./servicenow_itom_params')), + }; +} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors_no_app.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors_no_app.tsx new file mode 100644 index 0000000000000..f49c2d34d3a8d --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors_no_app.tsx @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { ActionConnectorFieldsProps } from '../../../../types'; + +import { ServiceNowActionConnector } from './types'; +import { Credentials } from './credentials'; + +const ServiceNowConnectorFieldsNoApp: React.FC< + ActionConnectorFieldsProps +> = ({ action, editActionSecrets, editActionConfig, errors, readOnly }) => { + return ( + <> + + + ); +}; + +// eslint-disable-next-line import/no-default-export +export { ServiceNowConnectorFieldsNoApp as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_itom_params.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_itom_params.test.tsx new file mode 100644 index 0000000000000..ef934d4ebacd7 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_itom_params.test.tsx @@ -0,0 +1,179 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { mount } from 'enzyme'; + +import { ActionConnector } from '../../../../types'; +import { useChoices } from './use_choices'; +import ServiceNowITOMParamsFields from './servicenow_itom_params'; + +jest.mock('./use_choices'); +jest.mock('../../../../common/lib/kibana'); + +const useChoicesMock = useChoices as jest.Mock; + +const actionParams = { + subAction: 'addEvent', + subActionParams: { + source: 'A source', + event_class: 'An event class', + resource: 'C:', + node: 'node.example.com', + metric_name: 'Percentage Logical Disk Free Space', + type: 'Disk space', + severity: '4', + description: 'desc', + additional_info: '{"alert": "test"}', + message_key: 'a key', + time_of_event: '2021-10-13T10:51:44.981Z', + }, +}; + +const connector: ActionConnector = { + secrets: {}, + config: {}, + id: 'test', + actionTypeId: '.test', + name: 'Test', + isPreconfigured: false, +}; + +const editAction = jest.fn(); +const defaultProps = { + actionConnector: connector, + actionParams, + errors: { ['subActionParams.incident.short_description']: [] }, + editAction, + index: 0, + messageVariables: [], +}; + +const choicesResponse = { + isLoading: false, + choices: { + severity: [ + { + dependent_value: '', + label: '1 - Critical', + value: '1', + element: 'severity', + }, + { + dependent_value: '', + label: '2 - Major', + value: '2', + element: 'severity', + }, + ], + }, +}; + +describe('ServiceNowITOMParamsFields renders', () => { + beforeEach(() => { + jest.clearAllMocks(); + useChoicesMock.mockImplementation((args) => { + return choicesResponse; + }); + }); + + test('all params fields is rendered', () => { + const wrapper = mount(); + expect(wrapper.find('[data-test-subj="sourceInput"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="nodeInput"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="typeInput"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="resourceInput"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="metric_nameInput"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="event_classInput"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="message_keyInput"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="severitySelect"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="descriptionTextArea"]').exists()).toBeTruthy(); + }); + + test('If severity has errors, form row is invalid', () => { + const newProps = { + ...defaultProps, + errors: { severity: ['error'] }, + }; + const wrapper = mount(); + const severity = wrapper.find('[data-test-subj="severitySelect"]').first(); + expect(severity.prop('isInvalid')).toBeTruthy(); + }); + + test('When subActionParams is undefined, set to default', () => { + const { subActionParams, ...newParams } = actionParams; + + const newProps = { + ...defaultProps, + actionParams: newParams, + }; + + mount(); + expect(editAction.mock.calls[0][1]).toEqual({ + message_key: '{{rule.id}}:{{alert.id}}', + additional_info: + '{"alert":{"id":"{{alert.id}}","actionGroup":"{{alert.actionGroup}}","actionSubgroup":"{{alert.actionSubgroup}}","actionGroupName":"{{alert.actionGroupName}}"},"rule":{"id":"{{rule.id}}","name":"{{rule.name}}","type":"{{rule.type}}"},"date":"{{date}}"}', + }); + }); + + test('When subAction is undefined, set to default', () => { + const { subAction, ...newParams } = actionParams; + + const newProps = { + ...defaultProps, + actionParams: newParams, + }; + mount(); + expect(editAction.mock.calls[0][1]).toEqual('addEvent'); + }); + + test('Resets fields when connector changes', () => { + const wrapper = mount(); + expect(editAction.mock.calls.length).toEqual(0); + wrapper.setProps({ actionConnector: { ...connector, id: '1234' } }); + expect(editAction.mock.calls.length).toEqual(1); + expect(editAction.mock.calls[0][1]).toEqual({ + message_key: '{{rule.id}}:{{alert.id}}', + additional_info: + '{"alert":{"id":"{{alert.id}}","actionGroup":"{{alert.actionGroup}}","actionSubgroup":"{{alert.actionSubgroup}}","actionGroupName":"{{alert.actionGroupName}}"},"rule":{"id":"{{rule.id}}","name":"{{rule.name}}","type":"{{rule.type}}"},"date":"{{date}}"}', + }); + }); + + test('it transforms the categories to options correctly', async () => { + const wrapper = mount(); + + wrapper.update(); + expect(wrapper.find('[data-test-subj="severitySelect"]').first().prop('options')).toEqual([ + { value: '1', text: '1 - Critical' }, + { value: '2', text: '2 - Major' }, + ]); + }); + + describe('UI updates', () => { + const changeEvent = { target: { value: 'Bug' } } as React.ChangeEvent; + const simpleFields = [ + { dataTestSubj: 'input[data-test-subj="sourceInput"]', key: 'source' }, + { dataTestSubj: 'textarea[data-test-subj="descriptionTextArea"]', key: 'description' }, + { dataTestSubj: '[data-test-subj="nodeInput"]', key: 'node' }, + { dataTestSubj: '[data-test-subj="typeInput"]', key: 'type' }, + { dataTestSubj: '[data-test-subj="resourceInput"]', key: 'resource' }, + { dataTestSubj: '[data-test-subj="metric_nameInput"]', key: 'metric_name' }, + { dataTestSubj: '[data-test-subj="event_classInput"]', key: 'event_class' }, + { dataTestSubj: '[data-test-subj="message_keyInput"]', key: 'message_key' }, + { dataTestSubj: '[data-test-subj="severitySelect"]', key: 'severity' }, + ]; + + simpleFields.forEach((field) => + test(`${field.key} update triggers editAction :D`, () => { + const wrapper = mount(); + const theField = wrapper.find(field.dataTestSubj).first(); + theField.prop('onChange')!(changeEvent); + expect(editAction.mock.calls[0][1][field.key]).toEqual(changeEvent.target.value); + }) + ); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_itom_params.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_itom_params.tsx new file mode 100644 index 0000000000000..8ad32fc0bc86b --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_itom_params.tsx @@ -0,0 +1,160 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback, useEffect, useRef, useMemo } from 'react'; +import { EuiFormRow, EuiSpacer, EuiTitle, EuiSelect } from '@elastic/eui'; +import { useKibana } from '../../../../common/lib/kibana'; +import { ActionParamsProps } from '../../../../types'; +import { TextAreaWithMessageVariables } from '../../text_area_with_message_variables'; +import { TextFieldWithMessageVariables } from '../../text_field_with_message_variables'; + +import * as i18n from './translations'; +import { useChoices } from './use_choices'; +import { ServiceNowITOMActionParams } from './types'; +import { choicesToEuiOptions, isFieldInvalid } from './helpers'; + +const choicesFields = ['severity']; + +const fields: Array<{ + label: string; + fieldKey: keyof ServiceNowITOMActionParams['subActionParams']; +}> = [ + { label: i18n.SOURCE, fieldKey: 'source' }, + { label: i18n.NODE, fieldKey: 'node' }, + { label: i18n.TYPE, fieldKey: 'type' }, + { label: i18n.RESOURCE, fieldKey: 'resource' }, + { label: i18n.METRIC_NAME, fieldKey: 'metric_name' }, + { label: i18n.EVENT_CLASS, fieldKey: 'event_class' }, + { label: i18n.MESSAGE_KEY, fieldKey: 'message_key' }, +]; + +const additionalInformation = JSON.stringify({ + alert: { + id: '{{alert.id}}', + actionGroup: '{{alert.actionGroup}}', + actionSubgroup: '{{alert.actionSubgroup}}', + actionGroupName: '{{alert.actionGroupName}}', + }, + rule: { + id: '{{rule.id}}', + name: '{{rule.name}}', + type: '{{rule.type}}', + }, + date: '{{date}}', +}); + +const ServiceNowITOMParamsFields: React.FunctionComponent< + ActionParamsProps +> = ({ actionConnector, actionParams, editAction, index, messageVariables, errors }) => { + const params = useMemo( + () => (actionParams.subActionParams ?? {}) as ServiceNowITOMActionParams['subActionParams'], + [actionParams.subActionParams] + ); + + const { description, severity } = params; + + const { + http, + notifications: { toasts }, + } = useKibana().services; + + const actionConnectorRef = useRef(actionConnector?.id ?? ''); + const { choices, isLoading: isLoadingChoices } = useChoices({ + http, + toastNotifications: toasts, + actionConnector, + fields: choicesFields, + }); + + const severityOptions = useMemo(() => choicesToEuiOptions(choices.severity), [choices.severity]); + + const editSubActionProperty = useCallback( + (key: string, value: any) => { + editAction('subActionParams', { ...params, [key]: value }, index); + }, + [editAction, index, params] + ); + + useEffect(() => { + if (actionConnector != null && actionConnectorRef.current !== actionConnector.id) { + actionConnectorRef.current = actionConnector.id; + editAction( + 'subActionParams', + { additional_info: additionalInformation, message_key: '{{rule.id}}:{{alert.id}}' }, + index + ); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [actionConnector]); + + useEffect(() => { + if (!actionParams.subAction) { + editAction('subAction', 'addEvent', index); + } + + if (!actionParams.subActionParams) { + editAction( + 'subActionParams', + { additional_info: additionalInformation, message_key: '{{rule.id}}:{{alert.id}}' }, + index + ); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [actionParams]); + + return ( + <> + +

{i18n.EVENT}

+ + + {fields.map((field) => ( + + + + + + + ))} + + editSubActionProperty('severity', e.target.value)} + isInvalid={isFieldInvalid(severity, errors.severity)} + /> + + + + ); +}; + +// eslint-disable-next-line import/no-default-export +export { ServiceNowITOMParamsFields as default }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_sir_params.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_sir_params.tsx index 72f6d7635268f..42758250408d9 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_sir_params.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_sir_params.tsx @@ -152,7 +152,7 @@ const ServiceNowSIRParamsFields: React.FunctionComponent< return ( <> -

{i18n.INCIDENT}

+

{i18n.SECURITY_INCIDENT}

; +const getChoicesMock = getChoices as jest.Mock; + +const actionConnector = { + secrets: { + username: 'user', + password: 'pass', + }, + id: 'test', + actionTypeId: '.servicenow', + name: 'ServiceNow ITSM', + isPreconfigured: false, + config: { + apiUrl: 'https://dev94428.service-now.com/', + }, +} as ActionConnector; + +const getChoicesResponse = [ + { + dependent_value: '', + label: 'Priviledge Escalation', + value: 'Priviledge Escalation', + element: 'category', + }, + { + dependent_value: '', + label: 'Criminal activity/investigation', + value: 'Criminal activity/investigation', + element: 'category', + }, + { + dependent_value: '', + label: 'Denial of Service', + value: 'Denial of Service', + element: 'category', + }, +]; + +const useChoicesResponse = { + isLoading: false, + choices: { category: getChoicesResponse }, +}; + +describe('UseChoices', () => { + const { services } = useKibanaMock(); + getChoicesMock.mockResolvedValue({ + data: getChoicesResponse, + }); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + const fields = ['category']; + + it('init', async () => { + const { result, waitForNextUpdate } = renderHook(() => + useChoices({ + http: services.http, + actionConnector, + toastNotifications: services.notifications.toasts, + fields, + }) + ); + + await waitForNextUpdate(); + + expect(result.current).toEqual(useChoicesResponse); + }); + + it('returns an empty array if the field is not in response', async () => { + const { result, waitForNextUpdate } = renderHook(() => + useChoices({ + http: services.http, + actionConnector, + toastNotifications: services.notifications.toasts, + fields: ['priority'], + }) + ); + + await waitForNextUpdate(); + + expect(result.current).toEqual({ + isLoading: false, + choices: { priority: [], category: getChoicesResponse }, + }); + }); + + it('returns an empty array when connector is not presented', async () => { + const { result } = renderHook(() => + useChoices({ + http: services.http, + actionConnector: undefined, + toastNotifications: services.notifications.toasts, + fields, + }) + ); + + expect(result.current).toEqual({ + isLoading: false, + choices: { category: [] }, + }); + }); + + it('it displays an error when service fails', async () => { + getChoicesMock.mockResolvedValue({ + status: 'error', + service_message: 'An error occurred', + }); + + const { waitForNextUpdate } = renderHook(() => + useChoices({ + http: services.http, + actionConnector, + toastNotifications: services.notifications.toasts, + fields, + }) + ); + + await waitForNextUpdate(); + + expect(services.notifications.toasts.addDanger).toHaveBeenCalledWith({ + text: 'An error occurred', + title: 'Unable to get choices', + }); + }); + + it('it displays an error when http throws an error', async () => { + getChoicesMock.mockImplementation(() => { + throw new Error('An error occurred'); + }); + + renderHook(() => + useChoices({ + http: services.http, + actionConnector, + toastNotifications: services.notifications.toasts, + fields, + }) + ); + + expect(services.notifications.toasts.addDanger).toHaveBeenCalledWith({ + text: 'An error occurred', + title: 'Unable to get choices', + }); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/use_choices.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/use_choices.tsx new file mode 100644 index 0000000000000..5493fdaee8bfa --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/use_choices.tsx @@ -0,0 +1,66 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useCallback, useMemo, useState } from 'react'; +import { HttpSetup, ToastsApi } from 'kibana/public'; + +import { ActionConnector } from '../../../../types'; +import { Choice, Fields } from './types'; +import { useGetChoices } from './use_get_choices'; + +export interface UseChoicesProps { + http: HttpSetup; + toastNotifications: Pick< + ToastsApi, + 'get$' | 'add' | 'remove' | 'addSuccess' | 'addWarning' | 'addDanger' | 'addError' + >; + actionConnector?: ActionConnector; + fields: string[]; +} + +export interface UseChoices { + choices: Fields; + isLoading: boolean; +} + +export const useChoices = ({ + http, + actionConnector, + toastNotifications, + fields, +}: UseChoicesProps): UseChoices => { + const defaultFields: Record = useMemo( + () => fields.reduce((acc, field) => ({ ...acc, [field]: [] }), {}), + [fields] + ); + const [choices, setChoices] = useState(defaultFields); + + const onChoicesSuccess = useCallback( + (values: Choice[]) => { + setChoices( + values.reduce( + (acc, value) => ({ + ...acc, + [value.element]: [...(acc[value.element] ?? []), value], + }), + defaultFields + ) + ); + }, + [defaultFields] + ); + + const { isLoading } = useGetChoices({ + http, + toastNotifications, + actionConnector, + fields, + onSuccess: onChoicesSuccess, + }); + + return { choices, isLoading }; +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/use_get_choices.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/use_get_choices.test.tsx index 01347a32e6da9..532789385e8bd 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/use_get_choices.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/use_get_choices.test.tsx @@ -121,7 +121,7 @@ describe('useGetChoices', () => { it('it displays an error when service fails', async () => { getChoicesMock.mockResolvedValue({ status: 'error', - serviceMessage: 'An error occurred', + service_message: 'An error occurred', }); const { waitForNextUpdate } = renderHook(() => @@ -162,4 +162,26 @@ describe('useGetChoices', () => { title: 'Unable to get choices', }); }); + + it('returns an empty array if the response is not an array', async () => { + getChoicesMock.mockResolvedValue({ + status: 'ok', + data: {}, + }); + + const { result } = renderHook(() => + useGetChoices({ + http: services.http, + actionConnector: undefined, + toastNotifications: services.notifications.toasts, + fields, + onSuccess, + }) + ); + + expect(result.current).toEqual({ + isLoading: false, + choices: [], + }); + }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/use_get_choices.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/use_get_choices.tsx index ef80c99955542..f4c881c633cdc 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/use_get_choices.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/use_get_choices.tsx @@ -60,15 +60,16 @@ export const useGetChoices = ({ }); if (!didCancel.current) { + const data = Array.isArray(res.data) ? res.data : []; setIsLoading(false); - setChoices(res.data ?? []); + setChoices(data); if (res.status && res.status === 'error') { toastNotifications.addDanger({ title: i18n.CHOICES_API_ERROR, - text: `${res.serviceMessage ?? res.message}`, + text: `${res.service_message ?? res.message}`, }); } else if (onSuccess) { - onSuccess(res.data ?? []); + onSuccess(data); } } } catch (error) { diff --git a/x-pack/test/alerting_api_integration/common/config.ts b/x-pack/test/alerting_api_integration/common/config.ts index 0618d379dc77d..3fe5ecb6076e2 100644 --- a/x-pack/test/alerting_api_integration/common/config.ts +++ b/x-pack/test/alerting_api_integration/common/config.ts @@ -35,6 +35,7 @@ const enabledActionTypes = [ '.server-log', '.servicenow', '.servicenow-sir', + '.servicenow-itom', '.jira', '.resilient', '.slack', diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/servicenow_simulation.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/servicenow_simulation.ts index 19a789659fd7f..f5f08bd0de246 100644 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/servicenow_simulation.ts +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/server/servicenow_simulation.ts @@ -175,6 +175,14 @@ const handler = async (request: http.IncomingMessage, response: http.ServerRespo }); } + if (pathName === '/api/global/em/jsonv2') { + return sendResponse(response, { + result: { + 'Default Bulk Endpoint': '1 events were inserted', + }, + }); + } + // Return an 400 error if endpoint is not supported response.statusCode = 400; response.setHeader('Content-Type', 'application/json'); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/jira.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/jira.ts index 7e8272b0a8afa..41a0d65b624b7 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/jira.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/jira.ts @@ -231,10 +231,6 @@ export default function jiraTest({ getService }: FtrProviderContext) { expect(Object.keys(resp.body)).to.eql(['status', 'message', 'retry', 'connector_id']); expect(resp.body.connector_id).to.eql(simulatedActionId); expect(resp.body.status).to.eql('error'); - expect(resp.body.retry).to.eql(false); - expect(resp.body.message).to.be( - `error validating action params: Cannot destructure property 'Symbol(Symbol.iterator)' of 'undefined' as it is undefined.` - ); }); }); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/resilient.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/resilient.ts index 4421c984b4aed..8e2036ce688ea 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/resilient.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/resilient.ts @@ -233,10 +233,6 @@ export default function resilientTest({ getService }: FtrProviderContext) { expect(Object.keys(resp.body)).to.eql(['status', 'message', 'retry', 'connector_id']); expect(resp.body.connector_id).to.eql(simulatedActionId); expect(resp.body.status).to.eql('error'); - expect(resp.body.retry).to.eql(false); - expect(resp.body.message).to.be( - `error validating action params: Cannot destructure property 'Symbol(Symbol.iterator)' of 'undefined' as it is undefined.` - ); }); }); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow_itom.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow_itom.ts new file mode 100644 index 0000000000000..6f1ddc6ee2748 --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow_itom.ts @@ -0,0 +1,342 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import httpProxy from 'http-proxy'; +import expect from '@kbn/expect'; +import getPort from 'get-port'; +import http from 'http'; + +import { getHttpProxyServer } from '../../../../common/lib/get_proxy_server'; +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import { getServiceNowServer } from '../../../../common/fixtures/plugins/actions_simulators/server/plugin'; + +// eslint-disable-next-line import/no-default-export +export default function serviceNowITOMTest({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const configService = getService('config'); + + const mockServiceNow = { + config: { + apiUrl: 'www.servicenowisinkibanaactions.com', + }, + secrets: { + password: 'elastic', + username: 'changeme', + }, + params: { + subAction: 'addEvent', + subActionParams: { + source: 'A source', + event_class: 'An event class', + resource: 'C:', + node: 'node.example.com', + metric_name: 'Percentage Logical Disk Free Space', + type: 'Disk space', + severity: '4', + description: 'desc', + additional_info: '{"alert": "test"}', + message_key: 'a key', + time_of_event: '2021-10-13T10:51:44.981Z', + }, + }, + }; + + describe('ServiceNow ITOM', () => { + let simulatedActionId = ''; + let serviceNowSimulatorURL: string = ''; + let serviceNowServer: http.Server; + let proxyServer: httpProxy | undefined; + let proxyHaveBeenCalled = false; + + before(async () => { + serviceNowServer = await getServiceNowServer(); + const availablePort = await getPort({ port: getPort.makeRange(9000, 9100) }); + if (!serviceNowServer.listening) { + serviceNowServer.listen(availablePort); + } + serviceNowSimulatorURL = `http://localhost:${availablePort}`; + proxyServer = await getHttpProxyServer( + serviceNowSimulatorURL, + configService.get('kbnTestServer.serverArgs'), + () => { + proxyHaveBeenCalled = true; + } + ); + }); + + after(() => { + serviceNowServer.close(); + if (proxyServer) { + proxyServer.close(); + } + }); + + describe('ServiceNow ITOM - Action Creation', () => { + it('should return 200 when creating a servicenow action successfully', async () => { + const { body: createdAction } = await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow action', + connector_type_id: '.servicenow-itom', + config: { + apiUrl: serviceNowSimulatorURL, + }, + secrets: mockServiceNow.secrets, + }) + .expect(200); + + expect(createdAction).to.eql({ + id: createdAction.id, + is_preconfigured: false, + name: 'A servicenow action', + connector_type_id: '.servicenow-itom', + is_missing_secrets: false, + config: { + apiUrl: serviceNowSimulatorURL, + }, + }); + + const { body: fetchedAction } = await supertest + .get(`/api/actions/connector/${createdAction.id}`) + .expect(200); + + expect(fetchedAction).to.eql({ + id: fetchedAction.id, + is_preconfigured: false, + name: 'A servicenow action', + connector_type_id: '.servicenow-itom', + is_missing_secrets: false, + config: { + apiUrl: serviceNowSimulatorURL, + }, + }); + }); + + it('should respond with a 400 Bad Request when creating a servicenow action with no apiUrl', async () => { + await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow action', + connector_type_id: '.servicenow-itom', + config: {}, + }) + .expect(400) + .then((resp: any) => { + expect(resp.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'error validating action type config: [apiUrl]: expected value of type [string] but got [undefined]', + }); + }); + }); + + it('should respond with a 400 Bad Request when creating a servicenow action with a not present in allowedHosts apiUrl', async () => { + await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow action', + connector_type_id: '.servicenow-itom', + config: { + apiUrl: 'http://servicenow.mynonexistent.com', + }, + secrets: mockServiceNow.secrets, + }) + .expect(400) + .then((resp: any) => { + expect(resp.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'error validating action type config: error configuring connector action: target url "http://servicenow.mynonexistent.com" is not added to the Kibana config xpack.actions.allowedHosts', + }); + }); + }); + + it('should respond with a 400 Bad Request when creating a servicenow action without secrets', async () => { + await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow action', + connector_type_id: '.servicenow-itom', + config: { + apiUrl: serviceNowSimulatorURL, + }, + }) + .expect(400) + .then((resp: any) => { + expect(resp.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'error validating action type secrets: [password]: expected value of type [string] but got [undefined]', + }); + }); + }); + }); + + describe('ServiceNow ITOM - Executor', () => { + before(async () => { + const { body } = await supertest + .post('/api/actions/connector') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow simulator', + connector_type_id: '.servicenow-itom', + config: { + apiUrl: serviceNowSimulatorURL, + }, + secrets: mockServiceNow.secrets, + }); + simulatedActionId = body.id; + }); + + describe('Validation', () => { + it('should handle failing with a simulated success without action', async () => { + await supertest + .post(`/api/actions/connector/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: {}, + }) + .then((resp: any) => { + expect(Object.keys(resp.body)).to.eql(['status', 'message', 'retry', 'connector_id']); + expect(resp.body.connector_id).to.eql(simulatedActionId); + expect(resp.body.status).to.eql('error'); + }); + }); + + it('should handle failing with a simulated success without unsupported action', async () => { + await supertest + .post(`/api/actions/connector/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { subAction: 'non-supported' }, + }) + .then((resp: any) => { + expect(resp.body).to.eql({ + connector_id: simulatedActionId, + status: 'error', + retry: false, + message: + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [addEvent]\n- [1.subAction]: expected value to equal [getChoices]', + }); + }); + }); + + it('should handle failing with a simulated success without subActionParams', async () => { + await supertest + .post(`/api/actions/connector/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { subAction: 'pushToService' }, + }) + .then((resp: any) => { + expect(resp.body).to.eql({ + connector_id: simulatedActionId, + status: 'error', + retry: false, + message: + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [addEvent]\n- [1.subAction]: expected value to equal [getChoices]', + }); + }); + }); + + describe('getChoices', () => { + it('should fail when field is not provided', async () => { + await supertest + .post(`/api/actions/connector/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { + subAction: 'getChoices', + subActionParams: {}, + }, + }) + .then((resp: any) => { + expect(resp.body).to.eql({ + connector_id: simulatedActionId, + status: 'error', + retry: false, + message: + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [addEvent]\n- [1.subActionParams.fields]: expected value of type [array] but got [undefined]', + }); + }); + }); + }); + }); + + describe('Execution', () => { + // New connectors + describe('Add event', () => { + it('should add an event ', async () => { + const { body: result } = await supertest + .post(`/api/actions/connector/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: mockServiceNow.params, + }) + .expect(200); + expect(result.status).to.eql('ok'); + }); + }); + + describe('getChoices', () => { + it('should get choices', async () => { + const { body: result } = await supertest + .post(`/api/actions/connector/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { + subAction: 'getChoices', + subActionParams: { fields: ['priority'] }, + }, + }) + .expect(200); + + expect(proxyHaveBeenCalled).to.equal(true); + expect(result).to.eql({ + status: 'ok', + connector_id: simulatedActionId, + data: [ + { + dependent_value: '', + label: '1 - Critical', + value: '1', + }, + { + dependent_value: '', + label: '2 - High', + value: '2', + }, + { + dependent_value: '', + label: '3 - Moderate', + value: '3', + }, + { + dependent_value: '', + label: '4 - Low', + value: '4', + }, + { + dependent_value: '', + label: '5 - Planning', + value: '5', + }, + ], + }); + }); + }); + }); + }); + }); +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow_itsm.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow_itsm.ts index 5ff1663975145..82f43ed4a3040 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow_itsm.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow_itsm.ts @@ -241,10 +241,6 @@ export default function serviceNowITSMTest({ getService }: FtrProviderContext) { expect(Object.keys(resp.body)).to.eql(['status', 'message', 'retry', 'connector_id']); expect(resp.body.connector_id).to.eql(simulatedActionId); expect(resp.body.status).to.eql('error'); - expect(resp.body.retry).to.eql(false); - expect(resp.body.message).to.be( - `error validating action params: Cannot destructure property 'Symbol(Symbol.iterator)' of 'undefined' as it is undefined.` - ); }); }); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow_sir.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow_sir.ts index bc4ec43fb4c7b..0cdb279ac0746 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow_sir.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow_sir.ts @@ -245,10 +245,6 @@ export default function serviceNowSIRTest({ getService }: FtrProviderContext) { expect(Object.keys(resp.body)).to.eql(['status', 'message', 'retry', 'connector_id']); expect(resp.body.connector_id).to.eql(simulatedActionId); expect(resp.body.status).to.eql('error'); - expect(resp.body.retry).to.eql(false); - expect(resp.body.message).to.be( - `error validating action params: Cannot destructure property 'Symbol(Symbol.iterator)' of 'undefined' as it is undefined.` - ); }); }); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/swimlane.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/swimlane.ts index 93d3a6c9e003f..9647d083460fd 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/swimlane.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/swimlane.ts @@ -322,10 +322,6 @@ export default function swimlaneTest({ getService }: FtrProviderContext) { expect(Object.keys(resp.body)).to.eql(['status', 'message', 'retry', 'connector_id']); expect(resp.body.connector_id).to.eql(simulatedActionId); expect(resp.body.status).to.eql('error'); - expect(resp.body.retry).to.eql(false); - expect(resp.body.message).to.be( - `error validating action params: undefined is not iterable (cannot read property Symbol(Symbol.iterator))` - ); }); }); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/index.ts index 61bd1bcad34ad..d247e066226e9 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/index.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/index.ts @@ -27,6 +27,7 @@ export default function actionsTests({ loadTestFile, getService }: FtrProviderCo loadTestFile(require.resolve('./builtin_action_types/server_log')); loadTestFile(require.resolve('./builtin_action_types/servicenow_itsm')); loadTestFile(require.resolve('./builtin_action_types/servicenow_sir')); + loadTestFile(require.resolve('./builtin_action_types/servicenow_itom')); loadTestFile(require.resolve('./builtin_action_types/jira')); loadTestFile(require.resolve('./builtin_action_types/resilient')); loadTestFile(require.resolve('./builtin_action_types/slack')); From d78fa6c7d8e3bf226c8d85e3ce8dc28d4ff3235b Mon Sep 17 00:00:00 2001 From: Orhan Toy Date: Tue, 19 Oct 2021 18:48:52 +0200 Subject: [PATCH 071/204] [App Search, Crawler] Fix validation step panel padding/whitespace (#115542) * Remove padding override * Move spacer to before action --- .../components/add_domain/validation_step_panel.tsx | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/add_domain/validation_step_panel.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/add_domain/validation_step_panel.tsx index dc19b526714a5..6e93fc1f74bb1 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/add_domain/validation_step_panel.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/crawler/components/add_domain/validation_step_panel.tsx @@ -33,14 +33,9 @@ export const ValidationStepPanel: React.FC = ({ action, }) => { const showErrorMessage = step.state === 'invalid' || step.state === 'warning'; - const styleOverride = showErrorMessage ? { paddingBottom: 0 } : {}; return ( - + @@ -59,8 +54,8 @@ export const ValidationStepPanel: React.FC = ({ {action && ( <> + {action} - )} From 9d0bf40c25dcdb3e58a489f936d7413c15c6949a Mon Sep 17 00:00:00 2001 From: Corey Robertson Date: Tue, 19 Oct 2021 12:53:24 -0400 Subject: [PATCH 072/204] Fix potential error from undefined (#115562) --- .../dashboard/public/saved_dashboards/saved_dashboard.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts b/src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts index 8772f14a6ec4c..4afb42aa841bb 100644 --- a/src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts +++ b/src/plugins/dashboard/public/saved_dashboards/saved_dashboard.ts @@ -114,14 +114,14 @@ export function createSavedDashboardClass( }, // if this is null/undefined then the SavedObject will be assigned the defaults - id: typeof arg === 'string' ? arg : arg.id, + id: typeof arg === 'object' ? arg.id : arg, // default values that will get assigned if the doc is new defaults, }); - const id: string = typeof arg === 'string' ? arg : arg.id; - const useResolve = typeof arg === 'string' ? false : arg.useResolve; + const id: string = typeof arg === 'object' ? arg.id : arg; + const useResolve = typeof arg === 'object' ? arg.useResolve : false; this.getFullPath = () => `/app/dashboards#${createDashboardEditUrl(this.aliasId || this.id)}`; From 9bc486517021b60f8c05e3c7ee178e6314f16758 Mon Sep 17 00:00:00 2001 From: Kevin Logan <56395104+kevinlog@users.noreply.github.com> Date: Tue, 19 Oct 2021 13:02:00 -0400 Subject: [PATCH 073/204] [Security Solution] Make new Add Data page more fine grained (#115016) * [Security Solution] Make new Add Data page more fine grained --- .../__snapshots__/no_data_page.test.tsx.snap | 102 ++++++++++++++++++ .../no_data_page/no_data_page.tsx | 7 +- .../app/home/template_wrapper/index.tsx | 49 +++++---- .../use_show_pages_with_empty_view.test.tsx | 51 +++++++++ .../use_show_pages_with_empty_view.tsx | 41 +++++++ .../public/hosts/pages/details/index.tsx | 2 - .../public/hosts/pages/hosts.tsx | 2 - .../public/network/pages/details/index.tsx | 2 - .../public/network/pages/network.tsx | 1 - .../components/overview_empty/index.test.tsx | 32 +++--- .../components/overview_empty/index.tsx | 12 +-- .../public/timelines/pages/timelines_page.tsx | 1 - 12 files changed, 240 insertions(+), 62 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/common/utils/empty_view/use_show_pages_with_empty_view.test.tsx create mode 100644 x-pack/plugins/security_solution/public/common/utils/empty_view/use_show_pages_with_empty_view.tsx diff --git a/src/plugins/kibana_react/public/page_template/no_data_page/__snapshots__/no_data_page.test.tsx.snap b/src/plugins/kibana_react/public/page_template/no_data_page/__snapshots__/no_data_page.test.tsx.snap index 8842a3c9f5842..0554e64c5ecb6 100644 --- a/src/plugins/kibana_react/public/page_template/no_data_page/__snapshots__/no_data_page.test.tsx.snap +++ b/src/plugins/kibana_react/public/page_template/no_data_page/__snapshots__/no_data_page.test.tsx.snap @@ -3,6 +3,108 @@ exports[`NoDataPage render 1`] = `
& { export type NoDataPageActionsProps = Record; -export interface NoDataPageProps { +export interface NoDataPageProps extends CommonProps { /** * Single name for the current solution, used to auto-generate the title, logo, description, and button label */ @@ -94,6 +96,7 @@ export const NoDataPage: FunctionComponent = ({ actions, docsLink, pageTitle, + ...rest }) => { // Convert obj data into an iterable array const entries = Object.entries(actions); @@ -131,7 +134,7 @@ export const NoDataPage: FunctionComponent = ({ }, [actions, sortedData, actionsKeys]); return ( -
+
getTimelineShowStatus(state, TimelineId.active) ); - const endpointMetadataIndex = useMemo(() => { - return [ENDPOINT_METADATA_INDEX]; - }, []); - const [, { indexExists: metadataIndexExists }] = useFetchIndex(endpointMetadataIndex, true); - const { indicesExist } = useSourcererScope(); - const securityIndicesExist = indicesExist || metadataIndexExists; + const showEmptyState = useShowPagesWithEmptyView(); + const emptyStateProps = showEmptyState ? NO_DATA_PAGE_TEMPLATE_PROPS : {}; // StyledKibanaPageTemplate is a styled EuiPageTemplate. Security solution currently passes the header and page content as the children of StyledKibanaPageTemplate, as opposed to using the pageHeader prop, which may account for any style discrepancies, such as the bottom border not extending the full width of the page, between EuiPageTemplate and the security solution pages. - return securityIndicesExist ? ( + return ( - - - {children} - + {showEmptyState ? ( + children + ) : ( + <> + + + {children} + + + )} - ) : ( - ); }); diff --git a/x-pack/plugins/security_solution/public/common/utils/empty_view/use_show_pages_with_empty_view.test.tsx b/x-pack/plugins/security_solution/public/common/utils/empty_view/use_show_pages_with_empty_view.test.tsx new file mode 100644 index 0000000000000..981f7e9e876ea --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/utils/empty_view/use_show_pages_with_empty_view.test.tsx @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook, act } from '@testing-library/react-hooks'; +import { useShowPagesWithEmptyView } from './use_show_pages_with_empty_view'; + +jest.mock('../route/use_route_spy', () => ({ + useRouteSpy: jest + .fn() + .mockImplementationOnce(() => [{ pageName: 'hosts' }]) + .mockImplementationOnce(() => [{ pageName: 'rules' }]) + .mockImplementationOnce(() => [{ pageName: 'network' }]), +})); +jest.mock('../../../common/containers/sourcerer', () => ({ + useSourcererScope: jest + .fn() + .mockImplementationOnce(() => [{ indicesExist: false }]) + .mockImplementationOnce(() => [{ indicesExist: false }]) + .mockImplementationOnce(() => [{ indicesExist: true }]), +})); + +describe('use show pages with empty view', () => { + it('shows empty view when on an elligible page and indices do not exist', async () => { + await act(async () => { + const { result, waitForNextUpdate } = renderHook(() => useShowPagesWithEmptyView()); + await waitForNextUpdate(); + const emptyResult = result.current; + expect(emptyResult).toEqual(true); + }); + }); + it('does not show empty view when on an inelligible page and indices do not exist', async () => { + await act(async () => { + const { result, waitForNextUpdate } = renderHook(() => useShowPagesWithEmptyView()); + await waitForNextUpdate(); + const emptyResult = result.current; + expect(emptyResult).toEqual(false); + }); + }); + it('shows empty view when on an elligible page and indices do exist', async () => { + await act(async () => { + const { result, waitForNextUpdate } = renderHook(() => useShowPagesWithEmptyView()); + await waitForNextUpdate(); + const emptyResult = result.current; + expect(emptyResult).toEqual(true); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/utils/empty_view/use_show_pages_with_empty_view.tsx b/x-pack/plugins/security_solution/public/common/utils/empty_view/use_show_pages_with_empty_view.tsx new file mode 100644 index 0000000000000..10cc4be10a61b --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/utils/empty_view/use_show_pages_with_empty_view.tsx @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useState, useEffect } from 'react'; +import { useRouteSpy } from '../route/use_route_spy'; +import { SecurityPageName } from '../../../app/types'; +import { useSourcererScope } from '../../../common/containers/sourcerer'; + +// Used to detect if we're on a top level page that is empty and set page background color to match the subdued Empty State +const isPageNameWithEmptyView = (currentName: string) => { + const pageNamesWithEmptyView: string[] = [ + SecurityPageName.hosts, + SecurityPageName.network, + SecurityPageName.timelines, + SecurityPageName.overview, + ]; + return pageNamesWithEmptyView.includes(currentName); +}; + +export const useShowPagesWithEmptyView = () => { + const [{ pageName }] = useRouteSpy(); + const { indicesExist } = useSourcererScope(); + + const shouldShowEmptyState = isPageNameWithEmptyView(pageName) && !indicesExist; + + const [showEmptyState, setShowEmptyState] = useState(shouldShowEmptyState); + + useEffect(() => { + if (shouldShowEmptyState) { + setShowEmptyState(true); + } else { + setShowEmptyState(false); + } + }, [shouldShowEmptyState]); + + return showEmptyState; +}; diff --git a/x-pack/plugins/security_solution/public/hosts/pages/details/index.tsx b/x-pack/plugins/security_solution/public/hosts/pages/details/index.tsx index 627f5e5aef507..9bf046f40edb4 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/details/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/details/index.tsx @@ -219,8 +219,6 @@ const HostDetailsComponent: React.FC = ({ detailName, hostDeta ) : ( - - )} diff --git a/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx b/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx index f647819798ab7..8e6a635624bfe 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx @@ -216,8 +216,6 @@ const HostsComponent = () => { ) : ( - - )} diff --git a/x-pack/plugins/security_solution/public/network/pages/details/index.tsx b/x-pack/plugins/security_solution/public/network/pages/details/index.tsx index a3f953fc24fe2..5aee4022a7f30 100644 --- a/x-pack/plugins/security_solution/public/network/pages/details/index.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/details/index.tsx @@ -302,8 +302,6 @@ const NetworkDetailsComponent: React.FC = () => { ) : ( - - )} diff --git a/x-pack/plugins/security_solution/public/network/pages/network.tsx b/x-pack/plugins/security_solution/public/network/pages/network.tsx index 928570417c524..fe8a9a93f97c7 100644 --- a/x-pack/plugins/security_solution/public/network/pages/network.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/network.tsx @@ -221,7 +221,6 @@ const NetworkComponent = React.memo( ) : ( - )} diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.test.tsx index 61e9e66f1bb87..36ecc3371c056 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.test.tsx @@ -42,17 +42,13 @@ describe('OverviewEmpty', () => { }); it('render with correct actions ', () => { - expect(wrapper.find('[data-test-subj="empty-page"]').prop('noDataConfig')).toEqual({ - actions: { - elasticAgent: { - category: 'security', - description: - 'Use Elastic Agent to collect security events and protect your endpoints from threats.', - title: 'Add a Security integration', - }, + expect(wrapper.find('[data-test-subj="empty-page"]').prop('actions')).toEqual({ + elasticAgent: { + category: 'security', + description: + 'Use Elastic Agent to collect security events and protect your endpoints from threats.', + title: 'Add a Security integration', }, - docsLink: 'https://www.elastic.co/guide/en/security/mocked-test-branch/index.html', - solution: 'Security', }); }); }); @@ -67,17 +63,13 @@ describe('OverviewEmpty', () => { }); it('render with correct actions ', () => { - expect(wrapper.find('[data-test-subj="empty-page"]').prop('noDataConfig')).toEqual({ - actions: { - elasticAgent: { - category: 'security', - description: - 'Use Elastic Agent to collect security events and protect your endpoints from threats.', - title: 'Add a Security integration', - }, + expect(wrapper.find('[data-test-subj="empty-page"]').prop('actions')).toEqual({ + elasticAgent: { + category: 'security', + description: + 'Use Elastic Agent to collect security events and protect your endpoints from threats.', + title: 'Add a Security integration', }, - docsLink: 'https://www.elastic.co/guide/en/security/mocked-test-branch/index.html', - solution: 'Security', }); }); }); diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.tsx index 9b20c079002e6..023d010ec9a9b 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_empty/index.tsx @@ -11,7 +11,7 @@ import { useKibana } from '../../../common/lib/kibana'; import { SOLUTION_NAME } from '../../../../public/common/translations'; import { - KibanaPageTemplate, + NoDataPage, NoDataPageActionsProps, } from '../../../../../../../src/plugins/kibana_react/public'; @@ -32,13 +32,11 @@ const OverviewEmptyComponent: React.FC = () => { }; return ( - ); }; diff --git a/x-pack/plugins/security_solution/public/timelines/pages/timelines_page.tsx b/x-pack/plugins/security_solution/public/timelines/pages/timelines_page.tsx index 728153f47abd7..f79d513380349 100644 --- a/x-pack/plugins/security_solution/public/timelines/pages/timelines_page.tsx +++ b/x-pack/plugins/security_solution/public/timelines/pages/timelines_page.tsx @@ -93,7 +93,6 @@ export const TimelinesPageComponent: React.FC = () => { ) : ( - )} From ffeadc52414b2b0b13d17b9db22de4b1f04f5449 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ester=20Mart=C3=AD=20Vilaseca?= Date: Tue, 19 Oct 2021 19:03:34 +0200 Subject: [PATCH 074/204] [Stack monitoring] Remove react migration feature flag (#115575) --- x-pack/plugins/monitoring/public/plugin.ts | 1 - x-pack/plugins/monitoring/server/config.test.ts | 1 - x-pack/plugins/monitoring/server/config.ts | 1 - 3 files changed, 3 deletions(-) diff --git a/x-pack/plugins/monitoring/public/plugin.ts b/x-pack/plugins/monitoring/public/plugin.ts index f75b76871f58c..78da3c57db534 100644 --- a/x-pack/plugins/monitoring/public/plugin.ts +++ b/x-pack/plugins/monitoring/public/plugin.ts @@ -133,7 +133,6 @@ export class MonitoringPlugin ['showLicenseExpiration', monitoring.ui.show_license_expiration], ['showCgroupMetricsElasticsearch', monitoring.ui.container.elasticsearch.enabled], ['showCgroupMetricsLogstash', monitoring.ui.container.logstash.enabled], - ['renderReactApp', monitoring.ui.render_react_app], ]; } diff --git a/x-pack/plugins/monitoring/server/config.test.ts b/x-pack/plugins/monitoring/server/config.test.ts index 8dd1866c274c9..036ade38607e9 100644 --- a/x-pack/plugins/monitoring/server/config.test.ts +++ b/x-pack/plugins/monitoring/server/config.test.ts @@ -106,7 +106,6 @@ describe('config schema', () => { "index": "metricbeat-*", }, "min_interval_seconds": 10, - "render_react_app": true, "show_license_expiration": true, }, } diff --git a/x-pack/plugins/monitoring/server/config.ts b/x-pack/plugins/monitoring/server/config.ts index 7b66b35d658cc..835ad30de7cbe 100644 --- a/x-pack/plugins/monitoring/server/config.ts +++ b/x-pack/plugins/monitoring/server/config.ts @@ -50,7 +50,6 @@ export const configSchema = schema.object({ }), min_interval_seconds: schema.number({ defaultValue: 10 }), show_license_expiration: schema.boolean({ defaultValue: true }), - render_react_app: schema.boolean({ defaultValue: true }), }), kibana: schema.object({ collection: schema.object({ From 65f3a8458a6651d70114e94298d998c0b4052617 Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Tue, 19 Oct 2021 12:08:53 -0500 Subject: [PATCH 075/204] Remove Angular (#115464) Co-authored-by: Tim Roes Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- package.json | 6 ---- .../src/worker/webpack.config.ts | 2 +- packages/kbn-ui-shared-deps-npm/BUILD.bazel | 2 -- .../kbn-ui-shared-deps-npm/webpack.config.js | 1 - packages/kbn-ui-shared-deps-src/src/entry.js | 1 - packages/kbn-ui-shared-deps-src/src/index.js | 1 - .../canvas/storybook/canvas.webpack.ts | 1 - yarn.lock | 30 ------------------- 8 files changed, 1 insertion(+), 43 deletions(-) diff --git a/package.json b/package.json index b3b9b96e75566..f4706b5afe7cd 100644 --- a/package.json +++ b/package.json @@ -184,11 +184,6 @@ "JSONStream": "1.3.5", "abort-controller": "^3.0.0", "abortcontroller-polyfill": "^1.7.3", - "angular": "^1.8.0", - "angular-aria": "^1.8.0", - "angular-recursion": "^1.0.5", - "angular-route": "^1.8.0", - "angular-sanitize": "^1.8.0", "antlr4ts": "^0.5.0-alpha.3", "archiver": "^5.2.0", "axios": "^0.21.1", @@ -488,7 +483,6 @@ "@testing-library/react": "^11.2.6", "@testing-library/react-hooks": "^5.1.1", "@testing-library/user-event": "^13.1.1", - "@types/angular": "^1.6.56", "@types/apidoc": "^0.22.3", "@types/archiver": "^5.1.0", "@types/babel__core": "^7.1.16", diff --git a/packages/kbn-optimizer/src/worker/webpack.config.ts b/packages/kbn-optimizer/src/worker/webpack.config.ts index 9a0863237fab1..1c16d2f1f77da 100644 --- a/packages/kbn-optimizer/src/worker/webpack.config.ts +++ b/packages/kbn-optimizer/src/worker/webpack.config.ts @@ -277,7 +277,7 @@ export function getWebpackConfig(bundle: Bundle, bundleRefs: BundleRefs, worker: terserOptions: { compress: true, keep_classnames: true, - mangle: !['kibanaLegacy', 'monitoring'].includes(bundle.id), + mangle: true, }, }), ], diff --git a/packages/kbn-ui-shared-deps-npm/BUILD.bazel b/packages/kbn-ui-shared-deps-npm/BUILD.bazel index 2ca08a34d761f..bbad873429b2b 100644 --- a/packages/kbn-ui-shared-deps-npm/BUILD.bazel +++ b/packages/kbn-ui-shared-deps-npm/BUILD.bazel @@ -36,7 +36,6 @@ RUNTIME_DEPS = [ "@npm//@elastic/numeral", "@npm//@emotion/react", "@npm//abortcontroller-polyfill", - "@npm//angular", "@npm//babel-loader", "@npm//babel-plugin-transform-react-remove-prop-types", "@npm//core-js", @@ -76,7 +75,6 @@ TYPES_DEPS = [ "@npm//@elastic/numeral", "@npm//@emotion/react", "@npm//abortcontroller-polyfill", - "@npm//angular", "@npm//babel-loader", "@npm//core-js", "@npm//css-loader", diff --git a/packages/kbn-ui-shared-deps-npm/webpack.config.js b/packages/kbn-ui-shared-deps-npm/webpack.config.js index c06ce6704f451..6b572dc3ea208 100644 --- a/packages/kbn-ui-shared-deps-npm/webpack.config.js +++ b/packages/kbn-ui-shared-deps-npm/webpack.config.js @@ -50,7 +50,6 @@ module.exports = (_, argv) => { '@elastic/eui/dist/eui_theme_amsterdam_dark.json', '@elastic/numeral', '@emotion/react', - 'angular', 'classnames', 'fflate', 'history', diff --git a/packages/kbn-ui-shared-deps-src/src/entry.js b/packages/kbn-ui-shared-deps-src/src/entry.js index d19aed4440e7c..c111cbbe60b97 100644 --- a/packages/kbn-ui-shared-deps-src/src/entry.js +++ b/packages/kbn-ui-shared-deps-src/src/entry.js @@ -16,7 +16,6 @@ require('./flot_charts'); // stateful deps export const KbnI18n = require('@kbn/i18n'); export const KbnI18nReact = require('@kbn/i18n/react'); -export const Angular = require('angular'); export const EmotionReact = require('@emotion/react'); export const Moment = require('moment'); export const MomentTimezone = require('moment-timezone/moment-timezone'); diff --git a/packages/kbn-ui-shared-deps-src/src/index.js b/packages/kbn-ui-shared-deps-src/src/index.js index fc5825c6d4777..3e3643d3e2988 100644 --- a/packages/kbn-ui-shared-deps-src/src/index.js +++ b/packages/kbn-ui-shared-deps-src/src/index.js @@ -30,7 +30,6 @@ exports.externals = { /** * stateful deps */ - angular: '__kbnSharedDeps__.Angular', '@kbn/i18n': '__kbnSharedDeps__.KbnI18n', '@kbn/i18n/react': '__kbnSharedDeps__.KbnI18nReact', '@emotion/react': '__kbnSharedDeps__.EmotionReact', diff --git a/x-pack/plugins/canvas/storybook/canvas.webpack.ts b/x-pack/plugins/canvas/storybook/canvas.webpack.ts index 29dd6cf3dda65..f4980741cc3e2 100644 --- a/x-pack/plugins/canvas/storybook/canvas.webpack.ts +++ b/x-pack/plugins/canvas/storybook/canvas.webpack.ts @@ -45,7 +45,6 @@ export const canvasWebpack = { test: [ resolve(KIBANA_ROOT, 'x-pack/plugins/canvas/public/components/embeddable_flyout'), resolve(KIBANA_ROOT, 'x-pack/plugins/reporting/public'), - resolve(KIBANA_ROOT, 'src/plugins/kibana_legacy/public/angular'), resolve(KIBANA_ROOT, 'src/plugins/kibana_legacy/public/paginate'), ], use: 'null-loader', diff --git a/yarn.lock b/yarn.lock index d715897b57e28..375e748564283 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5782,11 +5782,6 @@ dependencies: "@turf/helpers" "6.x" -"@types/angular@^1.6.56": - version "1.6.56" - resolved "https://registry.yarnpkg.com/@types/angular/-/angular-1.6.56.tgz#20124077bd44061e018c7283c0bb83f4b00322dd" - integrity sha512-HxtqilvklZ7i6XOaiP7uIJIrFXEVEhfbSY45nfv2DeBRngncI58Y4ZOUMiUkcT8sqgLL1ablmbfylChUg7A3GA== - "@types/anymatch@*": version "1.3.1" resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.1.tgz#336badc1beecb9dacc38bea2cf32adf627a8421a" @@ -7854,31 +7849,6 @@ amdefine@>=0.0.4: resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU= -angular-aria@^1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/angular-aria/-/angular-aria-1.8.0.tgz#97aec9b1e8bafd07d5fab30f98d8ec832e18e25d" - integrity sha512-eCQI6EwgY6bYHdzIUfDABHnZjoZ3bNYpCsnceQF4bLfbq1QtZ7raRPNca45sj6C9Pfjde6PNcEDvuLozFPYnrQ== - -angular-recursion@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/angular-recursion/-/angular-recursion-1.0.5.tgz#cd405428a0bf55faf52eaa7988c1fe69cd930543" - integrity sha1-zUBUKKC/Vfr1Lqp5iMH+ac2TBUM= - -angular-route@^1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/angular-route/-/angular-route-1.8.0.tgz#cb8066c5d34284ffd6a15ac7be1b3d51c5ad7bb2" - integrity sha512-ORvXAdVfCCA6XFwyjSkVQFFGufj0mNGiCvBR93Qsii8+7t/6Ioy6wheUoCj1x4NWUv7hAq3nYYGCVO6QEKb1BQ== - -angular-sanitize@^1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/angular-sanitize/-/angular-sanitize-1.8.0.tgz#9f80782d3afeec3bcc0bb92b3ca6f1f421cfbca6" - integrity sha512-j5GiOPCvfcDWK5svEOVoPb11X3UDVy/mdHPRWuy14Iyw86xaq+Bb+x/em2sAOa5MQQeY5ciLXbF3RRp8iCKcNg== - -angular@^1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/angular/-/angular-1.8.0.tgz#b1ec179887869215cab6dfd0df2e42caa65b1b51" - integrity sha512-VdaMx+Qk0Skla7B5gw77a8hzlcOakwF8mjlW13DpIWIDlfqwAbSSLfd8N/qZnzEmQF4jC4iofInd3gE7vL8ZZg== - ansi-align@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.0.tgz#b536b371cf687caaef236c18d3e21fe3797467cb" From 9ee377979c4f1f5822ff5314e93ebef71e26218d Mon Sep 17 00:00:00 2001 From: Poff Poffenberger Date: Tue, 19 Oct 2021 12:09:23 -0500 Subject: [PATCH 076/204] Fix Canvas double render by preventing workpad and assets from being reloaded if it's already available (#114712) * Fix Canvas double render by preventing workpad and assets from being reloaded if it's already available * Better fix * Another fix * updating tests * updating tests again * remove unused async --- .../routes/workpad/hooks/use_workpad.test.tsx | 61 ++++++++++++------- .../routes/workpad/hooks/use_workpad.ts | 26 ++++++-- 2 files changed, 58 insertions(+), 29 deletions(-) diff --git a/x-pack/plugins/canvas/public/routes/workpad/hooks/use_workpad.test.tsx b/x-pack/plugins/canvas/public/routes/workpad/hooks/use_workpad.test.tsx index 846af8a891434..3ad696b2b207b 100644 --- a/x-pack/plugins/canvas/public/routes/workpad/hooks/use_workpad.test.tsx +++ b/x-pack/plugins/canvas/public/routes/workpad/hooks/use_workpad.test.tsx @@ -5,7 +5,6 @@ * 2.0. */ -import { waitFor } from '@testing-library/react'; import { renderHook } from '@testing-library/react-hooks'; import { useWorkpad } from './use_workpad'; @@ -61,14 +60,20 @@ describe('useWorkpad', () => { workpad: workpadResponse, }); - renderHook(() => useWorkpad(workpadId, true, getRedirectPath)); + const { waitFor, unmount } = renderHook(() => useWorkpad(workpadId, true, getRedirectPath)); - await waitFor(() => expect(mockResolveWorkpad).toHaveBeenCalledWith(workpadId)); + try { + await waitFor(() => expect(mockDispatch).toHaveBeenCalledTimes(3)); - expect(mockResolveWorkpad).toHaveBeenCalledWith(workpadId); - expect(mockDispatch).toHaveBeenCalledWith({ type: 'setAssets', payload: assets }); - expect(mockDispatch).toHaveBeenCalledWith({ type: 'setWorkpad', payload: workpad }); - expect(mockDispatch).toHaveBeenCalledWith({ type: 'setZoomScale', payload: 1 }); + expect(mockResolveWorkpad).toHaveBeenCalledWith(workpadId); + expect(mockDispatch).toHaveBeenCalledWith({ type: 'setAssets', payload: assets }); + expect(mockDispatch).toHaveBeenCalledWith({ type: 'setWorkpad', payload: workpad }); + expect(mockDispatch).toHaveBeenCalledWith({ type: 'setZoomScale', payload: 1 }); + } catch (e) { + throw e; + } finally { + unmount(); + } }); test('sets alias id of workpad on a conflict', async () => { @@ -81,17 +86,23 @@ describe('useWorkpad', () => { aliasId, }); - renderHook(() => useWorkpad(workpadId, true, getRedirectPath)); - - await waitFor(() => expect(mockResolveWorkpad).toHaveBeenCalledWith(workpadId)); - - expect(mockResolveWorkpad).toHaveBeenCalledWith(workpadId); - expect(mockDispatch).toHaveBeenCalledWith({ type: 'setAssets', payload: assets }); - expect(mockDispatch).toHaveBeenCalledWith({ - type: 'setWorkpad', - payload: { ...workpad, aliasId }, - }); - expect(mockDispatch).toHaveBeenCalledWith({ type: 'setZoomScale', payload: 1 }); + const { waitFor, unmount } = renderHook(() => useWorkpad(workpadId, true, getRedirectPath)); + + try { + await waitFor(() => expect(mockDispatch).toHaveBeenCalledTimes(3)); + + expect(mockResolveWorkpad).toHaveBeenCalledWith(workpadId); + expect(mockDispatch).toHaveBeenCalledWith({ type: 'setAssets', payload: assets }); + expect(mockDispatch).toHaveBeenCalledWith({ + type: 'setWorkpad', + payload: { ...workpad, aliasId }, + }); + expect(mockDispatch).toHaveBeenCalledWith({ type: 'setZoomScale', payload: 1 }); + } catch (e) { + throw e; + } finally { + unmount(); + } }); test('redirects on alias match', async () => { @@ -104,10 +115,14 @@ describe('useWorkpad', () => { aliasId, }); - renderHook(() => useWorkpad(workpadId, true, getRedirectPath)); - - await waitFor(() => expect(mockResolveWorkpad).toHaveBeenCalledWith(workpadId)); - - expect(mockRedirectLegacyUrl).toBeCalledWith(`#${aliasId}`, 'Workpad'); + const { waitFor, unmount } = renderHook(() => useWorkpad(workpadId, true, getRedirectPath)); + try { + await waitFor(() => expect(mockRedirectLegacyUrl).toHaveBeenCalled()); + expect(mockRedirectLegacyUrl).toBeCalledWith(`#${aliasId}`, 'Workpad'); + } catch (e) { + throw e; + } finally { + unmount(); + } }); }); diff --git a/x-pack/plugins/canvas/public/routes/workpad/hooks/use_workpad.ts b/x-pack/plugins/canvas/public/routes/workpad/hooks/use_workpad.ts index f8ddd769aac43..35e79b442a15d 100644 --- a/x-pack/plugins/canvas/public/routes/workpad/hooks/use_workpad.ts +++ b/x-pack/plugins/canvas/public/routes/workpad/hooks/use_workpad.ts @@ -28,11 +28,16 @@ export const useWorkpad = ( getRedirectPath: (workpadId: string) => string ): [CanvasWorkpad | undefined, string | Error | undefined] => { const workpadService = useWorkpadService(); + const workpadResolve = workpadService.resolve; const platformService = usePlatformService(); const dispatch = useDispatch(); const storedWorkpad = useSelector(getWorkpad); const [error, setError] = useState(undefined); + const [resolveInfo, setResolveInfo] = useState< + { aliasId: string | undefined; outcome: string } | undefined + >(undefined); + useEffect(() => { (async () => { try { @@ -40,7 +45,9 @@ export const useWorkpad = ( outcome, aliasId, workpad: { assets, ...workpad }, - } = await workpadService.resolve(workpadId); + } = await workpadResolve(workpadId); + + setResolveInfo({ aliasId, outcome }); if (outcome === 'conflict') { workpad.aliasId = aliasId; @@ -49,15 +56,22 @@ export const useWorkpad = ( dispatch(setAssets(assets)); dispatch(setWorkpad(workpad, { loadPages })); dispatch(setZoomScale(1)); - - if (outcome === 'aliasMatch' && platformService.redirectLegacyUrl && aliasId) { - platformService.redirectLegacyUrl(`#${getRedirectPath(aliasId)}`, getWorkpadLabel()); - } } catch (e) { setError(e as Error | string); } })(); - }, [workpadId, dispatch, setError, loadPages, workpadService, getRedirectPath, platformService]); + }, [workpadId, dispatch, setError, loadPages, workpadResolve]); + + useEffect(() => { + (() => { + if (!resolveInfo) return; + + const { aliasId, outcome } = resolveInfo; + if (outcome === 'aliasMatch' && platformService.redirectLegacyUrl && aliasId) { + platformService.redirectLegacyUrl(`#${getRedirectPath(aliasId)}`, getWorkpadLabel()); + } + })(); + }, [resolveInfo, getRedirectPath, platformService]); return [storedWorkpad.id === workpadId ? storedWorkpad : undefined, error]; }; From d5b214a340951fe73b0b590819fab9ef816d63b3 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Tue, 19 Oct 2021 20:40:22 +0300 Subject: [PATCH 077/204] [TSVB] Weird state update on missing field (#115017) * Remove condition for index string mode when we reset field * Fix lint * Fix types * Update field_select.tsx Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../components/aggs/field_select.tsx | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/src/plugins/vis_types/timeseries/public/application/components/aggs/field_select.tsx b/src/plugins/vis_types/timeseries/public/application/components/aggs/field_select.tsx index 610b4a91cfd14..8e1880a7a14d8 100644 --- a/src/plugins/vis_types/timeseries/public/application/components/aggs/field_select.tsx +++ b/src/plugins/vis_types/timeseries/public/application/components/aggs/field_select.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ import { i18n } from '@kbn/i18n'; -import React, { ReactNode, useContext } from 'react'; +import React, { ReactNode } from 'react'; import { EuiComboBox, EuiComboBoxProps, @@ -20,8 +20,6 @@ import type { TimeseriesUIRestrictions } from '../../../../common/ui_restriction // @ts-ignore import { isFieldEnabled } from '../../lib/check_ui_restrictions'; -import { PanelModelContext } from '../../contexts/panel_model_context'; -import { USE_KIBANA_INDEXES_KEY } from '../../../../common/constants'; interface FieldSelectProps { label: string | ReactNode; @@ -64,7 +62,6 @@ export function FieldSelect({ uiRestrictions, 'data-test-subj': dataTestSubj = 'metricsIndexPatternFieldsSelect', }: FieldSelectProps) { - const panelModel = useContext(PanelModelContext); const htmlId = htmlIdGenerator(); let selectedOptions: Array> = []; @@ -119,18 +116,10 @@ export function FieldSelect({ } }); - let isInvalid; + const isInvalid = Boolean(value && fields[fieldsSelector] && !selectedOptions.length); - if (Boolean(panelModel?.[USE_KIBANA_INDEXES_KEY])) { - isInvalid = Boolean(value && fields[fieldsSelector] && !selectedOptions.length); - - if (value && !selectedOptions.length) { - selectedOptions = [{ label: value!, id: 'INVALID_FIELD' }]; - } - } else { - if (value && fields[fieldsSelector] && !selectedOptions.length) { - onChange([]); - } + if (value && !selectedOptions.length) { + selectedOptions = [{ label: value, id: 'INVALID_FIELD' }]; } return ( From 0447557c06ed9117ed6f4057155d050de6cd13a6 Mon Sep 17 00:00:00 2001 From: Maja Grubic Date: Tue, 19 Oct 2021 19:53:15 +0200 Subject: [PATCH 078/204] [Discover] Fix field filters test (#115062) * [Discover] Fix field filters test * Change the fixture for the test * Fix discover fixture * Remove empty line * Restore after Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../apps/discover/_source_filters.ts | 33 ++++++++++--------- test/functional/page_objects/settings_page.ts | 14 ++++++++ 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/test/functional/apps/discover/_source_filters.ts b/test/functional/apps/discover/_source_filters.ts index 912ffd8d552b9..134e7cca923b9 100644 --- a/test/functional/apps/discover/_source_filters.ts +++ b/test/functional/apps/discover/_source_filters.ts @@ -14,39 +14,40 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const log = getService('log'); const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); - const PageObjects = getPageObjects(['common', 'timePicker', 'discover']); + const retry = getService('retry'); + const PageObjects = getPageObjects(['common', 'timePicker', 'discover', 'header', 'settings']); - // FLAKY: https://github.com/elastic/kibana/issues/113130 - describe.skip('source filters', function describeIndexTests() { + describe('source filters', function () { before(async function () { - // delete .kibana index and update configDoc - await kibanaServer.uiSettings.replace({ - defaultIndex: 'logstash-*', - }); - await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/visualize.json'); - - // and load a set of makelogs data await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); - - await kibanaServer.uiSettings.update({ + await kibanaServer.uiSettings.replace({ + defaultIndex: 'logstash-*', 'discover:searchFieldsFromSource': false, }); + log.debug('management'); + await PageObjects.common.navigateToApp('settings'); + await PageObjects.settings.clickKibanaIndexPatterns(); + await PageObjects.settings.clickIndexPatternLogstash(); + await PageObjects.settings.addFieldFilter('referer'); + await PageObjects.settings.addFieldFilter('relatedContent*'); + log.debug('discover'); await PageObjects.common.navigateToApp('discover'); - await PageObjects.timePicker.setDefaultAbsoluteRange(); + await PageObjects.discover.waitUntilSearchingHasFinished(); - // After hiding the time picker, we need to wait for - // the refresh button to hide before clicking the share button - await PageObjects.common.sleep(1000); + await retry.try(async function () { + expect(await PageObjects.discover.getDocHeader()).to.have.string('Document'); + }); }); after(async () => { await kibanaServer.importExport.unload( 'test/functional/fixtures/kbn_archiver/visualize.json' ); + await kibanaServer.uiSettings.unset('defaultIndex'); }); it('should not get the field referer', async function () { diff --git a/test/functional/page_objects/settings_page.ts b/test/functional/page_objects/settings_page.ts index b929a78072868..633040221ed36 100644 --- a/test/functional/page_objects/settings_page.ts +++ b/test/functional/page_objects/settings_page.ts @@ -558,6 +558,20 @@ export class SettingsPageObject extends FtrService { } } + async addFieldFilter(name: string) { + await this.testSubjects.click('tab-sourceFilters'); + await this.find.setValue('.euiFieldText', name); + await this.find.clickByButtonText('Add'); + const table = await this.find.byClassName('euiTable'); + await this.retry.waitFor('field filter to be added', async () => { + const tableCells = await table.findAllByCssSelector('td'); + const fieldNames = await mapAsync(tableCells, async (cell) => { + return (await cell.getVisibleText()).trim(); + }); + return fieldNames.includes(name); + }); + } + public async confirmSave() { await this.testSubjects.setValue('saveModalConfirmText', 'change'); await this.testSubjects.click('confirmModalConfirmButton'); From 36d128c3e7167bbb93468bb3e60318890507fd44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Marcondes?= <55978943+cauemarcondes@users.noreply.github.com> Date: Tue, 19 Oct 2021 13:57:51 -0400 Subject: [PATCH 079/204] [APM] Default time range overrides fetched range for link-to/trace URLs without range parameters (#115449) * removing default time range from link trace url * adding test --- ...ect_to_transaction_detail_page_url.test.ts | 36 +++++++++++++------ .../components/routing/apm_route_config.tsx | 6 ---- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/TraceLink/get_redirect_to_transaction_detail_page_url.test.ts b/x-pack/plugins/apm/public/components/app/TraceLink/get_redirect_to_transaction_detail_page_url.test.ts index 21b2f487fba91..56a4facd20496 100644 --- a/x-pack/plugins/apm/public/components/app/TraceLink/get_redirect_to_transaction_detail_page_url.test.ts +++ b/x-pack/plugins/apm/public/components/app/TraceLink/get_redirect_to_transaction_detail_page_url.test.ts @@ -21,19 +21,35 @@ describe('getRedirectToTransactionDetailPageUrl', () => { }, } as unknown as any; - const url = getRedirectToTransactionDetailPageUrl({ transaction }); + describe('without time range', () => { + const url = getRedirectToTransactionDetailPageUrl({ transaction }); - it('rounds the start time down', () => { - expect(parse(url, true).query.rangeFrom).toBe('2020-01-01T00:00:00.000Z'); - }); + it('rounds the start time down', () => { + expect(parse(url, true).query.rangeFrom).toBe('2020-01-01T00:00:00.000Z'); + }); + + it('rounds the end time up', () => { + expect(parse(url, true).query.rangeTo).toBe('2020-01-01T00:05:00.000Z'); + }); - it('rounds the end time up', () => { - expect(parse(url, true).query.rangeTo).toBe('2020-01-01T00:05:00.000Z'); + it('formats url correctly', () => { + expect(url).toBe( + '/services/opbeans-node/transactions/view?traceId=trace_id&transactionId=transaction_id&transactionName=transaction_name&transactionType=request&rangeFrom=2020-01-01T00%3A00%3A00.000Z&rangeTo=2020-01-01T00%3A05%3A00.000Z' + ); + }); }); - it('formats url correctly', () => { - expect(url).toBe( - '/services/opbeans-node/transactions/view?traceId=trace_id&transactionId=transaction_id&transactionName=transaction_name&transactionType=request&rangeFrom=2020-01-01T00%3A00%3A00.000Z&rangeTo=2020-01-01T00%3A05%3A00.000Z' - ); + describe('with time range', () => { + const url = getRedirectToTransactionDetailPageUrl({ + transaction, + rangeFrom: '2020-01-01T00:02:00.000Z', + rangeTo: '2020-01-01T00:17:59.999Z', + }); + + it('uses timerange provided', () => { + expect(url).toBe( + '/services/opbeans-node/transactions/view?traceId=trace_id&transactionId=transaction_id&transactionName=transaction_name&transactionType=request&rangeFrom=2020-01-01T00%3A02%3A00.000Z&rangeTo=2020-01-01T00%3A17%3A59.999Z' + ); + }); }); }); diff --git a/x-pack/plugins/apm/public/components/routing/apm_route_config.tsx b/x-pack/plugins/apm/public/components/routing/apm_route_config.tsx index 5377cb81b372e..7e6ee849fdb0b 100644 --- a/x-pack/plugins/apm/public/components/routing/apm_route_config.tsx +++ b/x-pack/plugins/apm/public/components/routing/apm_route_config.tsx @@ -53,12 +53,6 @@ const apmRoutes = route([ }), }), ]), - defaults: { - query: { - rangeFrom: 'now-15m', - rangeTo: 'now', - }, - }, }, { path: '/', From 975beb125a6317d7ec65fa7a66d4857bfed279f7 Mon Sep 17 00:00:00 2001 From: Walter Rafelsberger Date: Tue, 19 Oct 2021 19:59:01 +0200 Subject: [PATCH 080/204] [ML] APM Correlations: Log log chart enhancements. (#113039) - By clicking on a row in the analysis tables, the row gets selected/pinned as the one highlighted in the chart, allowing the user to investigate this particular result via hovering in the chart. - A subtitle is added to the charts to clarify the chart type "Log-log plot for latency (x) by transactions (y) with overlapping bands" and lists the areas. Allows the user to see the full name of the highlighted entity because it could be cut off in the chart's native legend for longer field/value combinations. - The area palette has been tweaked for higher contrasts, the error area color now matched the orange used in other charts for errors/failed transactions. - Some visual tweaks like adding the non-transparent upper line for the areas to make sure there's a color in the chart itself that matches the legend colors: - The trace samples tab now also shows an area with all failed transactions. --- .../app/correlations/correlations_table.tsx | 7 + .../failed_transactions_correlations.tsx | 85 +++++++- .../app/correlations/latency_correlations.tsx | 64 ++++-- .../correlations/use_transaction_colors.ts | 17 ++ .../distribution/index.tsx | 139 +++++-------- ...use_transaction_distribution_chart_data.ts | 187 ++++++++++++++++++ .../index.test.tsx | 4 +- .../transaction_distribution_chart/index.tsx | 25 +-- .../get_overall_latency_distribution.ts | 8 +- .../plugins/apm/server/lib/latency/types.ts | 10 +- .../search_strategies/queries/get_filters.ts | 13 +- .../queries/query_failure_correlation.ts | 5 +- .../apm/server/routes/latency_distribution.ts | 14 +- .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - .../latency_overall_distribution.ts | 6 +- 16 files changed, 429 insertions(+), 157 deletions(-) create mode 100644 x-pack/plugins/apm/public/components/app/correlations/use_transaction_colors.ts create mode 100644 x-pack/plugins/apm/public/components/app/transaction_details/distribution/use_transaction_distribution_chart_data.ts diff --git a/x-pack/plugins/apm/public/components/app/correlations/correlations_table.tsx b/x-pack/plugins/apm/public/components/app/correlations/correlations_table.tsx index adf1805e0b9ae..eda3b64c309cc 100644 --- a/x-pack/plugins/apm/public/components/app/correlations/correlations_table.tsx +++ b/x-pack/plugins/apm/public/components/app/correlations/correlations_table.tsx @@ -22,6 +22,7 @@ interface CorrelationsTableProps { significantTerms?: T[]; status: FETCH_STATUS; percentageColumnName?: string; + setPinnedSignificantTerm?: (term: T | null) => void; setSelectedSignificantTerm: (term: T | null) => void; selectedTerm?: FieldValuePair; onFilter?: () => void; @@ -33,6 +34,7 @@ interface CorrelationsTableProps { export function CorrelationsTable({ significantTerms, status, + setPinnedSignificantTerm, setSelectedSignificantTerm, columns, selectedTerm, @@ -91,6 +93,11 @@ export function CorrelationsTable({ columns={columns} rowProps={(term) => { return { + onClick: () => { + if (setPinnedSignificantTerm) { + setPinnedSignificantTerm(term); + } + }, onMouseEnter: () => { setSelectedSignificantTerm(term); trackSelectSignificantCorrelationTerm(); diff --git a/x-pack/plugins/apm/public/components/app/correlations/failed_transactions_correlations.tsx b/x-pack/plugins/apm/public/components/app/correlations/failed_transactions_correlations.tsx index a177733b3ecaf..838671cbae7d9 100644 --- a/x-pack/plugins/apm/public/components/app/correlations/failed_transactions_correlations.tsx +++ b/x-pack/plugins/apm/public/components/app/correlations/failed_transactions_correlations.tsx @@ -18,6 +18,7 @@ import { EuiTitle, EuiBetaBadge, EuiBadge, + EuiText, EuiToolTip, EuiSwitch, EuiIconTip, @@ -26,6 +27,8 @@ import type { EuiTableSortingType } from '@elastic/eui/src/components/basic_tabl import type { Direction } from '@elastic/eui/src/services/sort/sort_direction'; import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; + import { enableInspectEsQueries, useUiTracker, @@ -37,10 +40,13 @@ import { APM_SEARCH_STRATEGIES, DEFAULT_PERCENTILE_THRESHOLD, } from '../../../../common/search_strategies/constants'; +import { FieldStats } from '../../../../common/search_strategies/field_stats_types'; import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context'; +import { useLocalStorage } from '../../../hooks/useLocalStorage'; import { FETCH_STATUS } from '../../../hooks/use_fetcher'; import { useSearchStrategy } from '../../../hooks/use_search_strategy'; +import { useTheme } from '../../../hooks/use_theme'; import { ImpactBar } from '../../shared/ImpactBar'; import { push } from '../../shared/Links/url_helpers'; @@ -58,10 +64,8 @@ import { CorrelationsLog } from './correlations_log'; import { CorrelationsEmptyStatePrompt } from './empty_state_prompt'; import { CrossClusterSearchCompatibilityWarning } from './cross_cluster_search_warning'; import { CorrelationsProgressControls } from './progress_controls'; -import { useLocalStorage } from '../../../hooks/useLocalStorage'; -import { useTheme } from '../../../hooks/use_theme'; +import { useTransactionColors } from './use_transaction_colors'; import { CorrelationsContextPopover } from './context_popover'; -import { FieldStats } from '../../../../common/search_strategies/field_stats_types'; import { OnAddFilter } from './context_popover/top_values'; export function FailedTransactionsCorrelations({ @@ -69,6 +73,9 @@ export function FailedTransactionsCorrelations({ }: { onFilter: () => void; }) { + const euiTheme = useTheme(); + const transactionColors = useTransactionColors(); + const { core: { notifications, uiSettings }, } = useApmPluginContext(); @@ -96,18 +103,11 @@ export function FailedTransactionsCorrelations({ progress.isRunning ); - const [selectedSignificantTerm, setSelectedSignificantTerm] = - useState(null); - - const selectedTerm = - selectedSignificantTerm ?? response.failedTransactionsCorrelations?.[0]; - const history = useHistory(); const [showStats, setShowStats] = useLocalStorage( 'apmFailedTransactionsShowAdvancedStats', false ); - const euiTheme = useTheme(); const toggleShowStats = useCallback(() => { setShowStats(!showStats); @@ -410,6 +410,30 @@ export function FailedTransactionsCorrelations({ [response.failedTransactionsCorrelations, sortField, sortDirection] ); + const [pinnedSignificantTerm, setPinnedSignificantTerm] = + useState(null); + const [selectedSignificantTerm, setSelectedSignificantTerm] = + useState(null); + + const selectedTerm = useMemo(() => { + if (!correlationTerms) { + return; + } else if (selectedSignificantTerm) { + return correlationTerms?.find( + (h) => + h.fieldName === selectedSignificantTerm.fieldName && + h.fieldValue === selectedSignificantTerm.fieldValue + ); + } else if (pinnedSignificantTerm) { + return correlationTerms.find( + (h) => + h.fieldName === pinnedSignificantTerm.fieldName && + h.fieldValue === pinnedSignificantTerm.fieldValue + ); + } + return correlationTerms[0]; + }, [correlationTerms, pinnedSignificantTerm, selectedSignificantTerm]); + const showCorrelationsTable = progress.isRunning || correlationTerms.length > 0; @@ -497,6 +521,41 @@ export function FailedTransactionsCorrelations({ + {selectedTerm && ( + + , + allTransactions: ( + + + + ), + allFailedTransactions: ( + + + + ), + focusTransaction: ( + + {selectedTerm?.fieldName}:{selectedTerm?.fieldValue} + + ), + }} + /> + + )} + @@ -581,6 +645,7 @@ export function FailedTransactionsCorrelations({ status={ progress.isRunning ? FETCH_STATUS.LOADING : FETCH_STATUS.SUCCESS } + setPinnedSignificantTerm={setPinnedSignificantTerm} setSelectedSignificantTerm={setSelectedSignificantTerm} selectedTerm={selectedTerm} onTableChange={onTableChange} diff --git a/x-pack/plugins/apm/public/components/app/correlations/latency_correlations.tsx b/x-pack/plugins/apm/public/components/app/correlations/latency_correlations.tsx index 75af7fae4ce12..db6f3ad63f00d 100644 --- a/x-pack/plugins/apm/public/components/app/correlations/latency_correlations.tsx +++ b/x-pack/plugins/apm/public/components/app/correlations/latency_correlations.tsx @@ -15,6 +15,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiSpacer, + EuiText, EuiTitle, EuiToolTip, } from '@elastic/eui'; @@ -22,6 +23,7 @@ import { Direction } from '@elastic/eui/src/services/sort/sort_direction'; import { EuiTableSortingType } from '@elastic/eui/src/components/basic_table/table_types'; import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; import { enableInspectEsQueries, @@ -34,6 +36,7 @@ import { DEFAULT_PERCENTILE_THRESHOLD, } from '../../../../common/search_strategies/constants'; import { LatencyCorrelation } from '../../../../common/search_strategies/latency_correlations/types'; +import { FieldStats } from '../../../../common/search_strategies/field_stats_types'; import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context'; import { FETCH_STATUS } from '../../../hooks/use_fetcher'; @@ -53,11 +56,13 @@ import { CorrelationsLog } from './correlations_log'; import { CorrelationsEmptyStatePrompt } from './empty_state_prompt'; import { CrossClusterSearchCompatibilityWarning } from './cross_cluster_search_warning'; import { CorrelationsProgressControls } from './progress_controls'; -import { FieldStats } from '../../../../common/search_strategies/field_stats_types'; +import { useTransactionColors } from './use_transaction_colors'; import { CorrelationsContextPopover } from './context_popover'; import { OnAddFilter } from './context_popover/top_values'; export function LatencyCorrelations({ onFilter }: { onFilter: () => void }) { + const transactionColors = useTransactionColors(); + const { core: { notifications, uiSettings }, } = useApmPluginContext(); @@ -98,19 +103,11 @@ export function LatencyCorrelations({ onFilter }: { onFilter: () => void }) { } }, [progress.error, notifications.toasts]); + const [pinnedSignificantTerm, setPinnedSignificantTerm] = + useState(null); const [selectedSignificantTerm, setSelectedSignificantTerm] = useState(null); - const selectedHistogram = useMemo( - () => - response.latencyCorrelations?.find( - (h) => - h.fieldName === selectedSignificantTerm?.fieldName && - h.fieldValue === selectedSignificantTerm?.fieldValue - ) ?? response.latencyCorrelations?.[0], - [response.latencyCorrelations, selectedSignificantTerm] - ); - const history = useHistory(); const trackApmEvent = useUiTracker({ app: 'apm' }); @@ -270,6 +267,25 @@ export function LatencyCorrelations({ onFilter }: { onFilter: () => void }) { [response.latencyCorrelations, sortField, sortDirection] ); + const selectedHistogram = useMemo(() => { + if (!histogramTerms) { + return; + } else if (selectedSignificantTerm) { + return histogramTerms?.find( + (h) => + h.fieldName === selectedSignificantTerm.fieldName && + h.fieldValue === selectedSignificantTerm.fieldValue + ); + } else if (pinnedSignificantTerm) { + return histogramTerms.find( + (h) => + h.fieldName === pinnedSignificantTerm.fieldName && + h.fieldValue === pinnedSignificantTerm.fieldValue + ); + } + return histogramTerms[0]; + }, [histogramTerms, pinnedSignificantTerm, selectedSignificantTerm]); + const showCorrelationsTable = progress.isRunning || histogramTerms.length > 0; const showCorrelationsEmptyStatePrompt = histogramTerms.length < 1 && @@ -315,6 +331,31 @@ export function LatencyCorrelations({ onFilter }: { onFilter: () => void }) { + {selectedHistogram && ( + + , + allTransactions: ( + + + + ), + focusTransaction: ( + + {selectedHistogram?.fieldName}:{selectedHistogram?.fieldValue} + + ), + }} + /> + + )} + void }) { status={ progress.isRunning ? FETCH_STATUS.LOADING : FETCH_STATUS.SUCCESS } + setPinnedSignificantTerm={setPinnedSignificantTerm} setSelectedSignificantTerm={setSelectedSignificantTerm} selectedTerm={selectedHistogram} onTableChange={onTableChange} diff --git a/x-pack/plugins/apm/public/components/app/correlations/use_transaction_colors.ts b/x-pack/plugins/apm/public/components/app/correlations/use_transaction_colors.ts new file mode 100644 index 0000000000000..445640209cd29 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/correlations/use_transaction_colors.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useTheme } from '../../../hooks/use_theme'; + +export const useTransactionColors = () => { + const euiTheme = useTheme(); + return { + ALL_TRANSACTIONS: euiTheme.eui.euiColorVis1, + ALL_FAILED_TRANSACTIONS: euiTheme.eui.euiColorVis7, + FOCUS_TRANSACTION: euiTheme.eui.euiColorVis2, + }; +}; diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/distribution/index.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/distribution/index.tsx index 109e52a84350f..8862596fd6d2a 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/distribution/index.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/distribution/index.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useEffect } from 'react'; +import React from 'react'; import { BrushEndListener, XYBrushEvent } from '@elastic/charts'; import { EuiBadge, @@ -16,30 +16,27 @@ import { EuiText, EuiTitle, } from '@elastic/eui'; + import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; import { useUiTracker } from '../../../../../../observability/public'; import { getDurationFormatter } from '../../../../../common/utils/formatters'; import { DEFAULT_PERCENTILE_THRESHOLD } from '../../../../../common/search_strategies/constants'; -import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; -import { useApmServiceContext } from '../../../../context/apm_service/use_apm_service_context'; import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; -import { useApmParams } from '../../../../hooks/use_apm_params'; -import { useFetcher, FETCH_STATUS } from '../../../../hooks/use_fetcher'; -import { useTimeRange } from '../../../../hooks/use_time_range'; +import { FETCH_STATUS } from '../../../../hooks/use_fetcher'; -import { - TransactionDistributionChart, - TransactionDistributionChartData, -} from '../../../shared/charts/transaction_distribution_chart'; -import { isErrorMessage } from '../../correlations/utils/is_error_message'; +import { TransactionDistributionChart } from '../../../shared/charts/transaction_distribution_chart'; +import { useTransactionColors } from '../../correlations/use_transaction_colors'; import type { TabContentProps } from '../types'; import { useWaterfallFetcher } from '../use_waterfall_fetcher'; import { WaterfallWithSummary } from '../waterfall_with_summary'; +import { useTransactionDistributionChartData } from './use_transaction_distribution_chart_data'; + // Enforce min height so it's consistent across all tabs on the same level // to prevent "flickering" behavior const MIN_TAB_TITLE_HEIGHT = 56; @@ -71,15 +68,8 @@ export function TransactionDistribution({ selection, traceSamples, }: TransactionDistributionProps) { - const { serviceName, transactionType } = useApmServiceContext(); - - const { - core: { notifications }, - } = useApmPluginContext(); - + const transactionColors = useTransactionColors(); const { urlParams } = useUrlParams(); - const { transactionName } = urlParams; - const { waterfall, status: waterfallStatus } = useWaterfallFetcher(); const markerCurrentTransaction = @@ -99,68 +89,6 @@ export function TransactionDistribution({ } ); - const { - query: { kuery, environment, rangeFrom, rangeTo }, - } = useApmParams('/services/{serviceName}/transactions/view'); - - const { start, end } = useTimeRange({ rangeFrom, rangeTo }); - - const { - data = { log: [] }, - status, - error, - } = useFetcher( - (callApmApi) => { - if (serviceName && environment && start && end) { - return callApmApi({ - endpoint: 'GET /internal/apm/latency/overall_distribution', - params: { - query: { - serviceName, - transactionName, - transactionType, - kuery, - environment, - start, - end, - percentileThreshold: DEFAULT_PERCENTILE_THRESHOLD, - }, - }, - }); - } - }, - [ - serviceName, - transactionName, - transactionType, - kuery, - environment, - start, - end, - ] - ); - - const overallHistogram = - data.overallHistogram === undefined && status !== FETCH_STATUS.LOADING - ? [] - : data.overallHistogram; - const hasData = - Array.isArray(overallHistogram) && overallHistogram.length > 0; - - useEffect(() => { - if (isErrorMessage(error)) { - notifications.toasts.addDanger({ - title: i18n.translate( - 'xpack.apm.transactionDetails.distribution.errorTitle', - { - defaultMessage: 'An error occurred fetching the distribution', - } - ), - text: error.toString(), - }); - } - }, [error, notifications.toasts]); - const trackApmEvent = useUiTracker({ app: 'apm' }); const onTrackedChartSelection = (brushEvent: XYBrushEvent) => { @@ -173,18 +101,8 @@ export function TransactionDistribution({ trackApmEvent({ metric: 'transaction_distribution_chart_clear_selection' }); }; - const transactionDistributionChartData: TransactionDistributionChartData[] = - []; - - if (Array.isArray(overallHistogram)) { - transactionDistributionChartData.push({ - id: i18n.translate( - 'xpack.apm.transactionDistribution.chart.allTransactionsLabel', - { defaultMessage: 'All transactions' } - ), - histogram: overallHistogram, - }); - } + const { chartData, hasData, percentileThresholdValue, status } = + useTransactionDistributionChartData(); return (
@@ -244,15 +162,46 @@ export function TransactionDistribution({ )} + + + + + ), + allFailedTransactions: ( + + + + ), + }} + /> + + diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/distribution/use_transaction_distribution_chart_data.ts b/x-pack/plugins/apm/public/components/app/transaction_details/distribution/use_transaction_distribution_chart_data.ts new file mode 100644 index 0000000000000..0edf5f648a980 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/transaction_details/distribution/use_transaction_distribution_chart_data.ts @@ -0,0 +1,187 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useEffect, useMemo } from 'react'; + +import { i18n } from '@kbn/i18n'; + +import { DEFAULT_PERCENTILE_THRESHOLD } from '../../../../../common/search_strategies/constants'; +import { RawSearchStrategyClientParams } from '../../../../../common/search_strategies/types'; +import { EVENT_OUTCOME } from '../../../../../common/elasticsearch_fieldnames'; +import { EventOutcome } from '../../../../../common/event_outcome'; + +import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; +import { useApmServiceContext } from '../../../../context/apm_service/use_apm_service_context'; +import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; +import { useApmParams } from '../../../../hooks/use_apm_params'; +import { useFetcher, FETCH_STATUS } from '../../../../hooks/use_fetcher'; +import { useTimeRange } from '../../../../hooks/use_time_range'; + +import type { TransactionDistributionChartData } from '../../../shared/charts/transaction_distribution_chart'; + +import { isErrorMessage } from '../../correlations/utils/is_error_message'; + +function hasRequiredParams(params: RawSearchStrategyClientParams) { + const { serviceName, environment, start, end } = params; + return serviceName && environment && start && end; +} + +export const useTransactionDistributionChartData = () => { + const { serviceName, transactionType } = useApmServiceContext(); + + const { + core: { notifications }, + } = useApmPluginContext(); + + const { urlParams } = useUrlParams(); + const { transactionName } = urlParams; + + const { + query: { kuery, environment, rangeFrom, rangeTo }, + } = useApmParams('/services/{serviceName}/transactions/view'); + + const { start, end } = useTimeRange({ rangeFrom, rangeTo }); + + const params = useMemo( + () => ({ + serviceName, + transactionName, + transactionType, + kuery, + environment, + start, + end, + }), + [ + serviceName, + transactionName, + transactionType, + kuery, + environment, + start, + end, + ] + ); + + const { + // TODO The default object has `log: []` to retain compatibility with the shared search strategies code. + // Remove once the other tabs are migrated away from search strategies. + data: overallLatencyData = { log: [] }, + status: overallLatencyStatus, + error: overallLatencyError, + } = useFetcher( + (callApmApi) => { + if (hasRequiredParams(params)) { + return callApmApi({ + endpoint: 'POST /internal/apm/latency/overall_distribution', + params: { + body: { + ...params, + percentileThreshold: DEFAULT_PERCENTILE_THRESHOLD, + }, + }, + }); + } + }, + [params] + ); + + useEffect(() => { + if (isErrorMessage(overallLatencyError)) { + notifications.toasts.addDanger({ + title: i18n.translate( + 'xpack.apm.transactionDetails.distribution.latencyDistributionErrorTitle', + { + defaultMessage: + 'An error occurred fetching the overall latency distribution.', + } + ), + text: overallLatencyError.toString(), + }); + } + }, [overallLatencyError, notifications.toasts]); + + const overallLatencyHistogram = + overallLatencyData.overallHistogram === undefined && + overallLatencyStatus !== FETCH_STATUS.LOADING + ? [] + : overallLatencyData.overallHistogram; + const hasData = + Array.isArray(overallLatencyHistogram) && + overallLatencyHistogram.length > 0; + + // TODO The default object has `log: []` to retain compatibility with the shared search strategies code. + // Remove once the other tabs are migrated away from search strategies. + const { data: errorHistogramData = { log: [] }, error: errorHistogramError } = + useFetcher( + (callApmApi) => { + if (hasRequiredParams(params)) { + return callApmApi({ + endpoint: 'POST /internal/apm/latency/overall_distribution', + params: { + body: { + ...params, + percentileThreshold: DEFAULT_PERCENTILE_THRESHOLD, + termFilters: [ + { + fieldName: EVENT_OUTCOME, + fieldValue: EventOutcome.failure, + }, + ], + }, + }, + }); + } + }, + [params] + ); + + useEffect(() => { + if (isErrorMessage(errorHistogramError)) { + notifications.toasts.addDanger({ + title: i18n.translate( + 'xpack.apm.transactionDetails.distribution.failedTransactionsLatencyDistributionErrorTitle', + { + defaultMessage: + 'An error occurred fetching the failed transactions latency distribution.', + } + ), + text: errorHistogramError.toString(), + }); + } + }, [errorHistogramError, notifications.toasts]); + + const transactionDistributionChartData: TransactionDistributionChartData[] = + []; + + if (Array.isArray(overallLatencyHistogram)) { + transactionDistributionChartData.push({ + id: i18n.translate( + 'xpack.apm.transactionDistribution.chart.allTransactionsLabel', + { defaultMessage: 'All transactions' } + ), + histogram: overallLatencyHistogram, + }); + } + + if (Array.isArray(errorHistogramData.overallHistogram)) { + transactionDistributionChartData.push({ + id: i18n.translate( + 'xpack.apm.transactionDistribution.chart.allFailedTransactionsLabel', + { defaultMessage: 'All failed transactions' } + ), + histogram: errorHistogramData.overallHistogram, + }); + } + + return { + chartData: transactionDistributionChartData, + hasData, + percentileThresholdValue: overallLatencyData.percentileThresholdValue, + status: overallLatencyStatus, + }; +}; diff --git a/x-pack/plugins/apm/public/components/shared/charts/transaction_distribution_chart/index.test.tsx b/x-pack/plugins/apm/public/components/shared/charts/transaction_distribution_chart/index.test.tsx index 48bd992952c30..8a57063ac4d45 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/transaction_distribution_chart/index.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/transaction_distribution_chart/index.test.tsx @@ -29,8 +29,8 @@ describe('TransactionDistributionChart', () => { { doc_count: 10 }, { doc_count: 10 }, { doc_count: 0.0001 }, - { doc_count: 0 }, - { doc_count: 0 }, + { doc_count: 0.0001 }, + { doc_count: 0.0001 }, { doc_count: 10 }, { doc_count: 10 }, { doc_count: 0.0001 }, diff --git a/x-pack/plugins/apm/public/components/shared/charts/transaction_distribution_chart/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/transaction_distribution_chart/index.tsx index 535fb777166bb..1a1ea13fa45ec 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/transaction_distribution_chart/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/transaction_distribution_chart/index.tsx @@ -51,6 +51,7 @@ interface TransactionDistributionChartProps { markerValue: number; markerPercentile: number; onChartSelection?: BrushEndListener; + palette?: string[]; selection?: [number, number]; status: FETCH_STATUS; } @@ -77,13 +78,10 @@ const CHART_PLACEHOLDER_VALUE = 0.0001; // Elastic charts will show any lone bin (i.e. a populated bin followed by empty bin) // as a circular marker instead of a bar // This provides a workaround by making the next bin not empty +// TODO Find a way to get rid of this workaround since it alters original values of the data. export const replaceHistogramDotsWithBars = (histogramItems: HistogramItem[]) => histogramItems.reduce((histogramItem, _, i) => { - if ( - histogramItem[i - 1]?.doc_count > 0 && - histogramItem[i - 1]?.doc_count !== CHART_PLACEHOLDER_VALUE && - histogramItem[i].doc_count === 0 - ) { + if (histogramItem[i].doc_count === 0) { histogramItem[i].doc_count = CHART_PLACEHOLDER_VALUE; } return histogramItem; @@ -102,16 +100,16 @@ export function TransactionDistributionChart({ markerValue, markerPercentile, onChartSelection, + palette, selection, status, }: TransactionDistributionChartProps) { const chartTheme = useChartTheme(); const euiTheme = useTheme(); - const areaSeriesColors = [ + const areaSeriesColors = palette ?? [ euiTheme.eui.euiColorVis1, euiTheme.eui.euiColorVis2, - euiTheme.eui.euiColorVis5, ]; const annotationsDataValues: LineAnnotationDatum[] = [ @@ -136,7 +134,7 @@ export function TransactionDistributionChart({ ) ?? 0; const yTicks = Math.ceil(Math.log10(yMax)); const yAxisDomain = { - min: 0.9, + min: 0.5, max: Math.pow(10, yTicks), }; @@ -171,7 +169,7 @@ export function TransactionDistributionChart({ }, areaSeriesStyle: { line: { - visible: false, + visible: true, }, }, axes: { @@ -186,7 +184,7 @@ export function TransactionDistributionChart({ }, }, }} - showLegend + showLegend={true} legendPosition={Position.Bottom} onBrushEnd={onChartSelection} /> @@ -238,7 +236,10 @@ export function TransactionDistributionChart({ /> ({ params: { - query: { + body: { environment: 'ENVIRONMENT_ALL', start: '2020', end: '2021', kuery: '', - percentileThreshold: '95', + percentileThreshold: 95, }, }, }); From c1e9fc9d0d28c7c08350f42bfad94704ebc215ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20C=C3=B4t=C3=A9?= Date: Tue, 19 Oct 2021 14:07:22 -0400 Subject: [PATCH 081/204] Remove TLS requirement for alerting when security is enabled (#115234) * Initial commit * Fix CI failures * Fix test label * Update messages * Cleanup translations * Fix type check Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- ...-plugin-core-public.doclinksstart.links.md | 1 + docs/user/alerting/alerting-setup.asciidoc | 3 +- .../public/doc_links/doc_links_service.ts | 2 + src/core/public/public.api.md | 1 + .../translations/translations/ja-JP.json | 7 -- .../translations/translations/zh-CN.json | 7 -- .../components/health_check.test.tsx | 27 ++++---- .../application/components/health_check.tsx | 65 ++++++++++--------- 8 files changed, 53 insertions(+), 60 deletions(-) diff --git a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md index 73efed79324fe..01e7beae61ce8 100644 --- a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md +++ b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md @@ -211,6 +211,7 @@ readonly links: { clusterPrivileges: string; elasticsearchSettings: string; elasticsearchEnableSecurity: string; + elasticsearchEnableApiKeys: string; indicesPrivileges: string; kibanaTLS: string; kibanaPrivileges: string; diff --git a/docs/user/alerting/alerting-setup.asciidoc b/docs/user/alerting/alerting-setup.asciidoc index 3b9868178fa8d..9aae3a27cf373 100644 --- a/docs/user/alerting/alerting-setup.asciidoc +++ b/docs/user/alerting/alerting-setup.asciidoc @@ -17,8 +17,7 @@ If you are using an *on-premises* Elastic Stack deployment: If you are using an *on-premises* Elastic Stack deployment with <>: -* You must enable Transport Layer Security (TLS) for communication <>. {kib} alerting uses <> to secure background rule checks and actions, and API keys require {ref}/configuring-tls.html#tls-http[TLS on the HTTP interface]. A proxy will not suffice. -* If you have enabled TLS and are still unable to access Alerting, ensure that you have not {ref}/security-settings.html#api-key-service-settings[explicitly disabled API keys]. +* If you are unable to access Alerting, ensure that you have not {ref}/security-settings.html#api-key-service-settings[explicitly disabled API keys]. The Alerting framework uses queries that require the `search.allow_expensive_queries` setting to be `true`. See the scripts {ref}/query-dsl-script-query.html#_allow_expensive_queries_4[documentation]. diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts index 91ad185447986..87b05eeafc568 100644 --- a/src/core/public/doc_links/doc_links_service.ts +++ b/src/core/public/doc_links/doc_links_service.ts @@ -357,6 +357,7 @@ export class DocLinksService { clusterPrivileges: `${ELASTICSEARCH_DOCS}security-privileges.html#privileges-list-cluster`, elasticsearchSettings: `${ELASTICSEARCH_DOCS}security-settings.html`, elasticsearchEnableSecurity: `${ELASTICSEARCH_DOCS}configuring-stack-security.html`, + elasticsearchEnableApiKeys: `${ELASTICSEARCH_DOCS}security-settings.html#api-key-service-settings`, indicesPrivileges: `${ELASTICSEARCH_DOCS}security-privileges.html#privileges-list-indices`, kibanaTLS: `${ELASTICSEARCH_DOCS}security-basic-setup.html#encrypt-internode-communication`, kibanaPrivileges: `${KIBANA_DOCS}kibana-privileges.html`, @@ -715,6 +716,7 @@ export interface DocLinksStart { clusterPrivileges: string; elasticsearchSettings: string; elasticsearchEnableSecurity: string; + elasticsearchEnableApiKeys: string; indicesPrivileges: string; kibanaTLS: string; kibanaPrivileges: string; diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index 508299686b0d9..353e5aa4607e4 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -680,6 +680,7 @@ export interface DocLinksStart { clusterPrivileges: string; elasticsearchSettings: string; elasticsearchEnableSecurity: string; + elasticsearchEnableApiKeys: string; indicesPrivileges: string; kibanaTLS: string; kibanaPrivileges: string; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index fc7b3ad2141ca..b9313ccfa46a4 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -24979,15 +24979,8 @@ "xpack.triggersActionsUI.components.healthCheck.alertsErrorAction": "方法を確認してください。", "xpack.triggersActionsUI.components.healthCheck.alertsErrorTitle": "アラートとアクションを有効にする必要があります", "xpack.triggersActionsUI.components.healthCheck.encryptionErrorAction": "方法を確認してください。", - "xpack.triggersActionsUI.components.healthCheck.encryptionErrorAfterKey": " kibana.ymlファイルで、暗号化された保存されたプラグインが有効になっていることを確認してください。", "xpack.triggersActionsUI.components.healthCheck.encryptionErrorBeforeKey": "ルールを作成するには、値を設定します ", "xpack.triggersActionsUI.components.healthCheck.encryptionErrorTitle": "暗号化された保存されたオブジェクトがありません", - "xpack.triggersActionsUI.components.healthCheck.tlsAndEncryptionError": "KibanaとElasticsearchの間でトランスポートレイヤーセキュリティを有効にし、kibana.ymlファイルで暗号化鍵を構成する必要があります。", - "xpack.triggersActionsUI.components.healthCheck.tlsAndEncryptionErrorAction": "方法を確認してください。", - "xpack.triggersActionsUI.components.healthCheck.tlsAndEncryptionErrorTitle": "追加の設定が必要です", - "xpack.triggersActionsUI.components.healthCheck.tlsError": "アラートはAPIキーに依存し、キーを使用するにはElasticsearchとKibanaの間にTLSが必要です。", - "xpack.triggersActionsUI.components.healthCheck.tlsErrorAction": "TLSを有効にする方法をご覧ください。", - "xpack.triggersActionsUI.components.healthCheck.tlsErrorTitle": "トランスポートレイヤーセキュリティとAPIキーを有効にする必要があります", "xpack.triggersActionsUI.connectors.breadcrumbTitle": "コネクター", "xpack.triggersActionsUI.data.coreQueryParams.aggTypeRequiredErrorMessage": "[aggType]が「{aggType}」のときには[aggField]に値が必要です", "xpack.triggersActionsUI.data.coreQueryParams.dateStartGTdateEndErrorMessage": "[dateStart]が[dateEnd]よりも大です", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 3d2866ac100d6..b9739d4bd991e 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -25405,15 +25405,8 @@ "xpack.triggersActionsUI.components.healthCheck.alertsErrorAction": "了解操作方法。", "xpack.triggersActionsUI.components.healthCheck.alertsErrorTitle": "必须启用“告警和操作”", "xpack.triggersActionsUI.components.healthCheck.encryptionErrorAction": "了解操作方法。", - "xpack.triggersActionsUI.components.healthCheck.encryptionErrorAfterKey": " 设置值,并确保启用加密已保存对象插件。", "xpack.triggersActionsUI.components.healthCheck.encryptionErrorBeforeKey": "要创建规则,请在 kibana.yml 文件中为: ", "xpack.triggersActionsUI.components.healthCheck.encryptionErrorTitle": "加密已保存对象不可用", - "xpack.triggersActionsUI.components.healthCheck.tlsAndEncryptionError": "必须在 Kibana 和 Elasticsearch 之间启用传输层安全并在 kibana.yml 文件中配置加密密钥。", - "xpack.triggersActionsUI.components.healthCheck.tlsAndEncryptionErrorAction": "了解操作方法。", - "xpack.triggersActionsUI.components.healthCheck.tlsAndEncryptionErrorTitle": "需要其他设置", - "xpack.triggersActionsUI.components.healthCheck.tlsError": "Alerting 功能依赖于 API 密钥,这需要在 Elasticsearch 与 Kibana 之间启用 TLS。", - "xpack.triggersActionsUI.components.healthCheck.tlsErrorAction": "了解如何启用 TLS。", - "xpack.triggersActionsUI.components.healthCheck.tlsErrorTitle": "必须启用传输层安全和 API 密钥", "xpack.triggersActionsUI.connectors.breadcrumbTitle": "连接器", "xpack.triggersActionsUI.data.coreQueryParams.aggTypeRequiredErrorMessage": "[aggField]:当 [aggType] 为“{aggType}”时必须有值", "xpack.triggersActionsUI.data.coreQueryParams.dateStartGTdateEndErrorMessage": "[dateStart]:晚于 [dateEnd]", diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/health_check.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/health_check.test.tsx index ff5992a6542b7..4192ee1a75a8f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/health_check.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/health_check.test.tsx @@ -81,7 +81,7 @@ describe('health check', () => { expect(queryByText('should render')).toBeInTheDocument(); }); - test('renders warning if TLS is required', async () => { + test('renders warning if API keys are disabled', async () => { useKibanaMock().services.http.get = jest.fn().mockImplementation(async () => ({ is_sufficiently_secure: false, has_permanent_encryption_key: true, @@ -103,18 +103,17 @@ describe('health check', () => { // wait for useEffect to run }); - const [description, action] = queryAllByText(/TLS/i); + const [description] = queryAllByText(/API keys/i); + const [action] = queryAllByText(/Learn more/i); expect(description.textContent).toMatchInlineSnapshot( - `"Alerting relies on API keys, which require TLS between Elasticsearch and Kibana. Learn how to enable TLS.(opens in a new tab or window)"` + `"You must enable API keys to use Alerting. Learn more.(opens in a new tab or window)"` ); - expect(action.textContent).toMatchInlineSnapshot( - `"Learn how to enable TLS.(opens in a new tab or window)"` - ); + expect(action.textContent).toMatchInlineSnapshot(`"Learn more.(opens in a new tab or window)"`); expect(action.getAttribute('href')).toMatchInlineSnapshot( - `"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-basic-setup.html#encrypt-internode-communication"` + `"https://www.elastic.co/guide/en/elasticsearch/reference/mocked-test-branch/security-settings.html#api-key-service-settings"` ); }); @@ -142,11 +141,13 @@ describe('health check', () => { const description = queryByRole(/banner/i); expect(description!.textContent).toMatchInlineSnapshot( - `"To create a rule, set a value for xpack.encryptedSavedObjects.encryptionKey in your kibana.yml file and ensure the Encrypted Saved Objects plugin is enabled. Learn how.(opens in a new tab or window)"` + `"You must configure an encryption key to use Alerting. Learn more.(opens in a new tab or window)"` ); const action = queryByText(/Learn/i); - expect(action!.textContent).toMatchInlineSnapshot(`"Learn how.(opens in a new tab or window)"`); + expect(action!.textContent).toMatchInlineSnapshot( + `"Learn more.(opens in a new tab or window)"` + ); expect(action!.getAttribute('href')).toMatchInlineSnapshot( `"https://www.elastic.co/guide/en/kibana/mocked-test-branch/alert-action-settings-kb.html#general-alert-action-settings"` ); @@ -175,14 +176,16 @@ describe('health check', () => { // wait for useEffect to run }); - const description = queryByText(/Transport Layer Security/i); + const description = queryByText(/You must enable/i); expect(description!.textContent).toMatchInlineSnapshot( - `"You must enable Transport Layer Security between Kibana and Elasticsearch and configure an encryption key in your kibana.yml file. Learn how.(opens in a new tab or window)"` + `"You must enable API keys and configure an encryption key to use Alerting. Learn more.(opens in a new tab or window)"` ); const action = queryByText(/Learn/i); - expect(action!.textContent).toMatchInlineSnapshot(`"Learn how.(opens in a new tab or window)"`); + expect(action!.textContent).toMatchInlineSnapshot( + `"Learn more.(opens in a new tab or window)"` + ); expect(action!.getAttribute('href')).toMatchInlineSnapshot( `"https://www.elastic.co/guide/en/kibana/mocked-test-branch/alerting-setup.html#alerting-prerequisites"` ); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/health_check.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/health_check.tsx index f990e12ed76e5..835c64ee0a05b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/health_check.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/health_check.tsx @@ -13,7 +13,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { EuiLink, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { EuiEmptyPrompt, EuiCode } from '@elastic/eui'; +import { EuiEmptyPrompt } from '@elastic/eui'; import { DocLinksStart } from 'kibana/public'; import './health_check.scss'; import { useHealthContext } from '../context/health_context'; @@ -82,11 +82,11 @@ export const HealthCheck: React.FunctionComponent = ({ ) : !healthCheck.isAlertsAvailable ? ( ) : !healthCheck.isSufficientlySecure && !healthCheck.hasPermanentEncryptionKey ? ( - + ) : !healthCheck.hasPermanentEncryptionKey ? ( ) : ( - + ); } ) @@ -108,7 +108,7 @@ const EncryptionError = ({ docLinks, className }: PromptErrorProps) => (

} @@ -118,22 +118,14 @@ const EncryptionError = ({ docLinks, className }: PromptErrorProps) => ( {i18n.translate( 'xpack.triggersActionsUI.components.healthCheck.encryptionErrorBeforeKey', { - defaultMessage: 'To create a rule, set a value for ', - } - )} - {'xpack.encryptedSavedObjects.encryptionKey'} - {i18n.translate( - 'xpack.triggersActionsUI.components.healthCheck.encryptionErrorAfterKey', - { - defaultMessage: - ' in your kibana.yml file and ensure the Encrypted Saved Objects plugin is enabled. ', + defaultMessage: 'You must configure an encryption key to use Alerting. ', } )} {i18n.translate( 'xpack.triggersActionsUI.components.healthCheck.encryptionErrorAction', { - defaultMessage: 'Learn how.', + defaultMessage: 'Learn more.', } )} @@ -143,7 +135,7 @@ const EncryptionError = ({ docLinks, className }: PromptErrorProps) => ( /> ); -const TlsError = ({ docLinks, className }: PromptErrorProps) => ( +const ApiKeysDisabledError = ({ docLinks, className }: PromptErrorProps) => ( ( title={

} body={

- {i18n.translate('xpack.triggersActionsUI.components.healthCheck.tlsError', { - defaultMessage: - 'Alerting relies on API keys, which require TLS between Elasticsearch and Kibana. ', + {i18n.translate('xpack.triggersActionsUI.components.healthCheck.apiKeysDisabledError', { + defaultMessage: 'You must enable API keys to use Alerting. ', })} - - {i18n.translate('xpack.triggersActionsUI.components.healthCheck.tlsErrorAction', { - defaultMessage: 'Learn how to enable TLS.', - })} + + {i18n.translate( + 'xpack.triggersActionsUI.components.healthCheck.apiKeysDisabledErrorAction', + { + defaultMessage: 'Learn more.', + } + )}

@@ -206,7 +204,7 @@ const AlertsError = ({ docLinks, className }: PromptErrorProps) => ( /> ); -const TlsAndEncryptionError = ({ docLinks, className }: PromptErrorProps) => ( +const ApiKeysAndEncryptionError = ({ docLinks, className }: PromptErrorProps) => ( ( title={

@@ -223,15 +221,18 @@ const TlsAndEncryptionError = ({ docLinks, className }: PromptErrorProps) => ( body={

- {i18n.translate('xpack.triggersActionsUI.components.healthCheck.tlsAndEncryptionError', { - defaultMessage: - 'You must enable Transport Layer Security between Kibana and Elasticsearch and configure an encryption key in your kibana.yml file. ', - })} + {i18n.translate( + 'xpack.triggersActionsUI.components.healthCheck.apiKeysAndEncryptionError', + { + defaultMessage: + 'You must enable API keys and configure an encryption key to use Alerting. ', + } + )} {i18n.translate( - 'xpack.triggersActionsUI.components.healthCheck.tlsAndEncryptionErrorAction', + 'xpack.triggersActionsUI.components.healthCheck.apiKeysAndEncryptionErrorAction', { - defaultMessage: 'Learn how.', + defaultMessage: 'Learn more.', } )} From 9dada56a2046af4a19a42b45c6087ab94b3fc35a Mon Sep 17 00:00:00 2001 From: Yuliia Naumenko Date: Tue, 19 Oct 2021 11:22:37 -0700 Subject: [PATCH 082/204] [Alerting] Telemetry required for 7.16 (#114690) * [Alerting] Telemetry required for 7.16 * rules namespaces * rules fix * failed task fix * fixed due to the discussion about namespaces keys * added missing telemetry props mappings * added missing telemetry props mappings * added missing telemetry props mappings * fixed tests * changed actions active structure under parent key for alerts * fixed tests * fixed tests * fixed test data * added preconfig connectors for email services count * fixed if statement * fixed default namespace * fixed test * fixed tests * fixed task * removed filtering by referenceType Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../server/usage/actions_telemetry.test.ts | 145 +++++++++++++++- .../actions/server/usage/actions_telemetry.ts | 163 ++++++++++++------ .../server/usage/actions_usage_collector.ts | 29 +--- x-pack/plugins/actions/server/usage/task.ts | 4 +- x-pack/plugins/actions/server/usage/types.ts | 35 +++- .../server/usage/alerts_telemetry.test.ts | 10 +- .../alerting/server/usage/alerts_telemetry.ts | 39 +++-- .../server/usage/alerts_usage_collector.ts | 2 + x-pack/plugins/alerting/server/usage/task.ts | 1 + x-pack/plugins/alerting/server/usage/types.ts | 1 + .../usage/task_manager_usage_collector.ts | 10 ++ .../task_manager/server/usage/types.ts | 1 + .../schema/xpack_plugins.json | 52 +++++- 13 files changed, 395 insertions(+), 97 deletions(-) diff --git a/x-pack/plugins/actions/server/usage/actions_telemetry.test.ts b/x-pack/plugins/actions/server/usage/actions_telemetry.test.ts index 0e6b7fff04451..229f06f2e47fa 100644 --- a/x-pack/plugins/actions/server/usage/actions_telemetry.test.ts +++ b/x-pack/plugins/actions/server/usage/actions_telemetry.test.ts @@ -146,6 +146,7 @@ Object { id: '1', actionTypeId: '.server-log', }, + namespaces: ['default'], }, }, { @@ -154,6 +155,7 @@ Object { id: '2', actionTypeId: '.slack', }, + namespaces: ['default'], }, }, ], @@ -170,6 +172,8 @@ Object { "__server-log": 1, "__slack": 1, }, + "countEmailByService": Object {}, + "countNamespaces": 1, "countTotal": 2, } `); @@ -220,6 +224,7 @@ Object { id: '1', actionTypeId: '.server-log', }, + namespaces: ['default'], }, }, { @@ -228,13 +233,35 @@ Object { id: '2', actionTypeId: '.slack', }, + namespaces: ['default'], }, }, ], }, }) ); - const telemetry = await getInUseTotalCount(mockEsClient, 'test'); + const telemetry = await getInUseTotalCount(mockEsClient, 'test', undefined, [ + { + id: 'test', + actionTypeId: '.email', + name: 'test', + isPreconfigured: true, + config: { + tenantId: 'sdsd', + clientId: 'sdfsdf', + }, + secrets: { + clientSecret: 'sdfsdf', + }, + }, + { + id: 'anotherServerLog', + actionTypeId: '.server-log', + name: 'test', + isPreconfigured: true, + secrets: {}, + }, + ]); expect(mockEsClient.search).toHaveBeenCalledTimes(2); expect(telemetry).toMatchInlineSnapshot(` @@ -245,6 +272,8 @@ Object { "__server-log": 1, "__slack": 1, }, + "countEmailByService": Object {}, + "countNamespaces": 1, "countTotal": 4, } `); @@ -423,6 +452,114 @@ Object { id: '1', actionTypeId: '.server-log', }, + namespaces: ['default'], + }, + }, + { + _source: { + action: { + id: '2', + actionTypeId: '.slack', + }, + namespaces: ['default'], + }, + }, + { + _source: { + action: { + id: '3', + actionTypeId: '.email', + }, + namespaces: ['default'], + }, + }, + ], + }, + }) + ); + const telemetry = await getInUseTotalCount(mockEsClient, 'test', undefined, [ + { + id: 'anotherServerLog', + actionTypeId: '.server-log', + name: 'test', + isPreconfigured: true, + secrets: {}, + }, + ]); + + expect(mockEsClient.search).toHaveBeenCalledTimes(2); + expect(telemetry).toMatchInlineSnapshot(` +Object { + "countByAlertHistoryConnectorType": 1, + "countByType": Object { + "__email": 3, + "__index": 1, + "__server-log": 1, + "__slack": 1, + }, + "countEmailByService": Object { + "other": 3, + }, + "countNamespaces": 1, + "countTotal": 6, +} +`); + }); + + test('getInUseTotalCount() accounts for actions namespaces', async () => { + const mockEsClient = elasticsearchClientMock.createClusterClient().asScoped().asInternalUser; + mockEsClient.search.mockReturnValueOnce( + // @ts-expect-error not full search response + elasticsearchClientMock.createSuccessTransportRequestPromise({ + aggregations: { + refs: { + actionRefIds: { + value: { + connectorIds: { + '1': 'action-0', + '123': 'action-1', + '456': 'action-2', + }, + total: 3, + }, + }, + }, + preconfigured_actions: { + preconfiguredActionRefIds: { + value: { + total: 3, + actionRefs: { + 'preconfigured:preconfigured-alert-history-es-index': { + actionRef: 'preconfigured:preconfigured-alert-history-es-index', + actionTypeId: '.index', + }, + 'preconfigured:cloud_email': { + actionRef: 'preconfigured:cloud_email', + actionTypeId: '.email', + }, + 'preconfigured:cloud_email2': { + actionRef: 'preconfigured:cloud_email2', + actionTypeId: '.email', + }, + }, + }, + }, + }, + }, + }) + ); + mockEsClient.search.mockReturnValueOnce( + // @ts-expect-error not full search response + elasticsearchClientMock.createSuccessTransportRequestPromise({ + hits: { + hits: [ + { + _source: { + action: { + id: '1', + actionTypeId: '.server-log', + }, + namespaces: ['test'], }, }, { @@ -431,6 +568,7 @@ Object { id: '2', actionTypeId: '.slack', }, + namespaces: ['default'], }, }, { @@ -439,6 +577,7 @@ Object { id: '3', actionTypeId: '.email', }, + namespaces: ['test2'], }, }, ], @@ -457,6 +596,10 @@ Object { "__server-log": 1, "__slack": 1, }, + "countEmailByService": Object { + "other": 1, + }, + "countNamespaces": 3, "countTotal": 6, } `); diff --git a/x-pack/plugins/actions/server/usage/actions_telemetry.ts b/x-pack/plugins/actions/server/usage/actions_telemetry.ts index 4a3d0c70e535a..1cb6bf8bfc74c 100644 --- a/x-pack/plugins/actions/server/usage/actions_telemetry.ts +++ b/x-pack/plugins/actions/server/usage/actions_telemetry.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { QueryDslQueryContainer } from '@elastic/elasticsearch/api/types'; import { ElasticsearchClient } from 'kibana/server'; import { AlertHistoryEsIndexConnectorId } from '../../common'; import { ActionResult, PreConfiguredAction } from '../types'; @@ -81,11 +82,15 @@ export async function getTotalCount( export async function getInUseTotalCount( esClient: ElasticsearchClient, - kibanaIndex: string + kibanaIndex: string, + referenceType?: string, + preconfiguredActions?: PreConfiguredAction[] ): Promise<{ countTotal: number; countByType: Record; countByAlertHistoryConnectorType: number; + countEmailByService: Record; + countNamespaces: number; }> { const scriptedMetric = { scripted_metric: { @@ -160,6 +165,63 @@ export async function getInUseTotalCount( }, }; + const mustQuery = [ + { + bool: { + should: [ + { + nested: { + path: 'references', + query: { + bool: { + filter: { + bool: { + must: [ + { + term: { + 'references.type': 'action', + }, + }, + ], + }, + }, + }, + }, + }, + }, + { + nested: { + path: 'alert.actions', + query: { + bool: { + filter: { + bool: { + must: [ + { + prefix: { + 'alert.actions.actionRef': { + value: 'preconfigured:', + }, + }, + }, + ], + }, + }, + }, + }, + }, + }, + ], + }, + }, + ] as QueryDslQueryContainer[]; + + if (!!referenceType) { + mustQuery.push({ + term: { type: referenceType }, + }); + } + const { body: actionResults } = await esClient.search({ index: kibanaIndex, body: { @@ -172,54 +234,7 @@ export async function getInUseTotalCount( type: 'action_task_params', }, }, - must: { - bool: { - should: [ - { - nested: { - path: 'references', - query: { - bool: { - filter: { - bool: { - must: [ - { - term: { - 'references.type': 'action', - }, - }, - ], - }, - }, - }, - }, - }, - }, - { - nested: { - path: 'alert.actions', - query: { - bool: { - filter: { - bool: { - must: [ - { - prefix: { - 'alert.actions.actionRef': { - value: 'preconfigured:', - }, - }, - }, - ], - }, - }, - }, - }, - }, - }, - ], - }, - }, + must: mustQuery, }, }, }, @@ -250,13 +265,15 @@ export async function getInUseTotalCount( const preconfiguredActionsAggs = // @ts-expect-error aggegation type is not specified actionResults.aggregations.preconfigured_actions?.preconfiguredActionRefIds.value; + const { body: { hits: actions }, } = await esClient.search<{ action: ActionResult; + namespaces: string[]; }>({ index: kibanaIndex, - _source_includes: ['action'], + _source_includes: ['action', 'namespaces'], body: { query: { bool: { @@ -274,6 +291,7 @@ export async function getInUseTotalCount( }, }, }); + const countByActionTypeId = actions.hits.reduce( (actionTypeCount: Record, action) => { const actionSource = action._source!; @@ -286,6 +304,26 @@ export async function getInUseTotalCount( {} ); + const namespacesList = actions.hits.reduce((_namespaces: Set, action) => { + const namespaces = action._source?.namespaces ?? ['default']; + namespaces.forEach((namespace) => { + if (!_namespaces.has(namespace)) { + _namespaces.add(namespace); + } + }); + return _namespaces; + }, new Set()); + + const countEmailByService = actions.hits + .filter((action) => action._source!.action.actionTypeId === '.email') + .reduce((emailServiceCount: Record, action) => { + const service = (action._source!.action.config?.service ?? 'other') as string; + const currentCount = + emailServiceCount[service] !== undefined ? emailServiceCount[service] : 0; + emailServiceCount[service] = currentCount + 1; + return emailServiceCount; + }, {}); + let preconfiguredAlertHistoryConnectors = 0; const preconfiguredActionsRefs: Array<{ actionTypeId: string; @@ -298,15 +336,40 @@ export async function getInUseTotalCount( if (actionRef === `preconfigured:${AlertHistoryEsIndexConnectorId}`) { preconfiguredAlertHistoryConnectors++; } + if (preconfiguredActions && actionTypeId === '__email') { + const preconfiguredConnectorId = actionRef.split(':')[1]; + const service = (preconfiguredActions.find( + (preconfConnector) => preconfConnector.id === preconfiguredConnectorId + )?.config?.service ?? 'other') as string; + const currentCount = + countEmailByService[service] !== undefined ? countEmailByService[service] : 0; + countEmailByService[service] = currentCount + 1; + } } return { countTotal: aggs.total + (preconfiguredActionsAggs?.total ?? 0), countByType: countByActionTypeId, countByAlertHistoryConnectorType: preconfiguredAlertHistoryConnectors, + countEmailByService, + countNamespaces: namespacesList.size, }; } +export async function getInUseByAlertingTotalCounts( + esClient: ElasticsearchClient, + kibanaIndex: string, + preconfiguredActions?: PreConfiguredAction[] +): Promise<{ + countTotal: number; + countByType: Record; + countByAlertHistoryConnectorType: number; + countEmailByService: Record; + countNamespaces: number; +}> { + return await getInUseTotalCount(esClient, kibanaIndex, 'alert', preconfiguredActions); +} + function replaceFirstAndLastDotSymbols(strToReplace: string) { const hasFirstSymbolDot = strToReplace.startsWith('.'); const appliedString = hasFirstSymbolDot ? strToReplace.replace('.', '__') : strToReplace; diff --git a/x-pack/plugins/actions/server/usage/actions_usage_collector.ts b/x-pack/plugins/actions/server/usage/actions_usage_collector.ts index 80e0c19092c78..9ba9d7390a7b6 100644 --- a/x-pack/plugins/actions/server/usage/actions_usage_collector.ts +++ b/x-pack/plugins/actions/server/usage/actions_usage_collector.ts @@ -5,29 +5,12 @@ * 2.0. */ -import { MakeSchemaFrom, UsageCollectionSetup } from 'src/plugins/usage_collection/server'; +import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; import { get } from 'lodash'; import { TaskManagerStartContract } from '../../../task_manager/server'; -import { ActionsUsage } from './types'; +import { ActionsUsage, byServiceProviderTypeSchema, byTypeSchema } from './types'; import { ActionsConfig } from '../config'; -const byTypeSchema: MakeSchemaFrom['count_by_type'] = { - // TODO: Find out an automated way to populate the keys or reformat these into an array (and change the Remote Telemetry indexer accordingly) - DYNAMIC_KEY: { type: 'long' }, - // Known actions: - __email: { type: 'long' }, - __index: { type: 'long' }, - __pagerduty: { type: 'long' }, - __swimlane: { type: 'long' }, - '__server-log': { type: 'long' }, - __slack: { type: 'long' }, - __webhook: { type: 'long' }, - __servicenow: { type: 'long' }, - __jira: { type: 'long' }, - __resilient: { type: 'long' }, - __teams: { type: 'long' }, -}; - export function createActionsUsageCollector( usageCollection: UsageCollectionSetup, config: ActionsConfig, @@ -45,6 +28,7 @@ export function createActionsUsageCollector( _meta: { description: 'Indicates if preconfigured alert history connector is enabled.' }, }, count_total: { type: 'long' }, + count_by_type: byTypeSchema, count_active_total: { type: 'long' }, count_active_alert_history_connectors: { type: 'long', @@ -52,8 +36,9 @@ export function createActionsUsageCollector( description: 'The total number of preconfigured alert history connectors used by rules.', }, }, - count_by_type: byTypeSchema, count_active_by_type: byTypeSchema, + count_active_email_connectors_by_service_type: byServiceProviderTypeSchema, + count_actions_namespaces: { type: 'long' }, }, fetch: async () => { try { @@ -69,10 +54,12 @@ export function createActionsUsageCollector( return { alert_history_connector_enabled: false, count_total: 0, + count_by_type: {}, count_active_total: 0, count_active_alert_history_connectors: 0, count_active_by_type: {}, - count_by_type: {}, + count_active_email_connectors_by_service_type: {}, + count_actions_namespaces: 0, }; } }, diff --git a/x-pack/plugins/actions/server/usage/task.ts b/x-pack/plugins/actions/server/usage/task.ts index 7cbfb87dedda6..bacb9e5f72571 100644 --- a/x-pack/plugins/actions/server/usage/task.ts +++ b/x-pack/plugins/actions/server/usage/task.ts @@ -83,7 +83,7 @@ export function telemetryTaskRunner( const esClient = await getEsClient(); return Promise.all([ getTotalCount(esClient, kibanaIndex, preconfiguredActions), - getInUseTotalCount(esClient, kibanaIndex), + getInUseTotalCount(esClient, kibanaIndex, undefined, preconfiguredActions), ]) .then(([totalAggegations, totalInUse]) => { return { @@ -94,6 +94,8 @@ export function telemetryTaskRunner( count_active_total: totalInUse.countTotal, count_active_by_type: totalInUse.countByType, count_active_alert_history_connectors: totalInUse.countByAlertHistoryConnectorType, + count_active_email_connectors_by_service_type: totalInUse.countEmailByService, + count_actions_namespaces: totalInUse.countNamespaces, }, runAt: getNextMidnight(), }; diff --git a/x-pack/plugins/actions/server/usage/types.ts b/x-pack/plugins/actions/server/usage/types.ts index 9221ba8ea5688..52677b35ac75b 100644 --- a/x-pack/plugins/actions/server/usage/types.ts +++ b/x-pack/plugins/actions/server/usage/types.ts @@ -5,14 +5,47 @@ * 2.0. */ +import { MakeSchemaFrom } from 'src/plugins/usage_collection/server'; + export interface ActionsUsage { alert_history_connector_enabled: boolean; count_total: number; + count_by_type: Record; count_active_total: number; count_active_alert_history_connectors: number; - count_by_type: Record; count_active_by_type: Record; + count_active_email_connectors_by_service_type: Record; + count_actions_namespaces: number; // TODO: Implement executions count telemetry with eventLog, when it will write to index // executions_by_type: Record; // executions_total: number; } + +export const byTypeSchema: MakeSchemaFrom['count_by_type'] = { + // TODO: Find out an automated way to populate the keys or reformat these into an array (and change the Remote Telemetry indexer accordingly) + DYNAMIC_KEY: { type: 'long' }, + // Known actions: + __email: { type: 'long' }, + __index: { type: 'long' }, + __pagerduty: { type: 'long' }, + __swimlane: { type: 'long' }, + '__server-log': { type: 'long' }, + __slack: { type: 'long' }, + __webhook: { type: 'long' }, + __servicenow: { type: 'long' }, + __jira: { type: 'long' }, + __resilient: { type: 'long' }, + __teams: { type: 'long' }, +}; + +export const byServiceProviderTypeSchema: MakeSchemaFrom['count_active_email_connectors_by_service_type'] = + { + DYNAMIC_KEY: { type: 'long' }, + // Known services: + exchange_server: { type: 'long' }, + gmail: { type: 'long' }, + outlook365: { type: 'long' }, + elastic_cloud: { type: 'long' }, + other: { type: 'long' }, + ses: { type: 'long' }, + }; diff --git a/x-pack/plugins/alerting/server/usage/alerts_telemetry.test.ts b/x-pack/plugins/alerting/server/usage/alerts_telemetry.test.ts index cce394d70ed6f..15fa6e63ac561 100644 --- a/x-pack/plugins/alerting/server/usage/alerts_telemetry.test.ts +++ b/x-pack/plugins/alerting/server/usage/alerts_telemetry.test.ts @@ -18,7 +18,14 @@ describe('alerts telemetry', () => { aggregations: { byAlertTypeId: { value: { - types: { '.index-threshold': 2, 'logs.alert.document.count': 1, 'document.test.': 1 }, + ruleTypes: { + '.index-threshold': 2, + 'logs.alert.document.count': 1, + 'document.test.': 1, + }, + namespaces: { + default: 1, + }, }, }, }, @@ -39,6 +46,7 @@ Object { "document.test__": 1, "logs.alert.document.count": 1, }, + "countNamespaces": 1, "countTotal": 4, } `); diff --git a/x-pack/plugins/alerting/server/usage/alerts_telemetry.ts b/x-pack/plugins/alerting/server/usage/alerts_telemetry.ts index 7d8c1593f533d..18fa9b590b4e1 100644 --- a/x-pack/plugins/alerting/server/usage/alerts_telemetry.ts +++ b/x-pack/plugins/alerting/server/usage/alerts_telemetry.ts @@ -10,10 +10,14 @@ import { AlertsUsage } from './types'; const alertTypeMetric = { scripted_metric: { - init_script: 'state.types = [:]', + init_script: 'state.ruleTypes = [:]; state.namespaces = [:]', map_script: ` String alertType = doc['alert.alertTypeId'].value; - state.types.put(alertType, state.types.containsKey(alertType) ? state.types.get(alertType) + 1 : 1); + String namespace = doc['namespaces'] !== null ? doc['namespaces'].value : 'default'; + state.ruleTypes.put(alertType, state.ruleTypes.containsKey(alertType) ? state.ruleTypes.get(alertType) + 1 : 1); + if (state.namespaces.containsKey(namespace) === false) { + state.namespaces.put(namespace, 1); + } `, // Combine script is executed per cluster, but we already have a key-value pair per cluster. // Despite docs that say this is optional, this script can't be blank. @@ -40,7 +44,12 @@ export async function getTotalCountAggregations( ): Promise< Pick< AlertsUsage, - 'count_total' | 'count_by_type' | 'throttle_time' | 'schedule_time' | 'connectors_per_alert' + | 'count_total' + | 'count_by_type' + | 'throttle_time' + | 'schedule_time' + | 'connectors_per_alert' + | 'count_rules_namespaces' > > { const throttleTimeMetric = { @@ -247,7 +256,7 @@ export async function getTotalCountAggregations( }); const aggregations = results.aggregations as { - byAlertTypeId: { value: { types: Record } }; + byAlertTypeId: { value: { ruleTypes: Record } }; throttleTime: { value: { min: number; max: number; totalCount: number; totalSum: number } }; intervalTime: { value: { min: number; max: number; totalCount: number; totalSum: number } }; connectorsAgg: { @@ -257,20 +266,20 @@ export async function getTotalCountAggregations( }; }; - const totalAlertsCount = Object.keys(aggregations.byAlertTypeId.value.types).reduce( + const totalAlertsCount = Object.keys(aggregations.byAlertTypeId.value.ruleTypes).reduce( (total: number, key: string) => - parseInt(aggregations.byAlertTypeId.value.types[key], 10) + total, + parseInt(aggregations.byAlertTypeId.value.ruleTypes[key], 10) + total, 0 ); return { count_total: totalAlertsCount, - count_by_type: Object.keys(aggregations.byAlertTypeId.value.types).reduce( + count_by_type: Object.keys(aggregations.byAlertTypeId.value.ruleTypes).reduce( // ES DSL aggregations are returned as `any` by esClient.search // eslint-disable-next-line @typescript-eslint/no-explicit-any (obj: any, key: string) => ({ ...obj, - [replaceFirstAndLastDotSymbols(key)]: aggregations.byAlertTypeId.value.types[key], + [replaceFirstAndLastDotSymbols(key)]: aggregations.byAlertTypeId.value.ruleTypes[key], }), {} ), @@ -300,6 +309,7 @@ export async function getTotalCountAggregations( : 0, max: aggregations.connectorsAgg.connectors.value.max, }, + count_rules_namespaces: 0, }; } @@ -319,24 +329,27 @@ export async function getTotalCountInUse(esClient: ElasticsearchClient, kibanaIn }); const aggregations = searchResult.aggregations as { - byAlertTypeId: { value: { types: Record } }; + byAlertTypeId: { + value: { ruleTypes: Record; namespaces: Record }; + }; }; return { - countTotal: Object.keys(aggregations.byAlertTypeId.value.types).reduce( + countTotal: Object.keys(aggregations.byAlertTypeId.value.ruleTypes).reduce( (total: number, key: string) => - parseInt(aggregations.byAlertTypeId.value.types[key], 10) + total, + parseInt(aggregations.byAlertTypeId.value.ruleTypes[key], 10) + total, 0 ), - countByType: Object.keys(aggregations.byAlertTypeId.value.types).reduce( + countByType: Object.keys(aggregations.byAlertTypeId.value.ruleTypes).reduce( // ES DSL aggregations are returned as `any` by esClient.search // eslint-disable-next-line @typescript-eslint/no-explicit-any (obj: any, key: string) => ({ ...obj, - [replaceFirstAndLastDotSymbols(key)]: aggregations.byAlertTypeId.value.types[key], + [replaceFirstAndLastDotSymbols(key)]: aggregations.byAlertTypeId.value.ruleTypes[key], }), {} ), + countNamespaces: Object.keys(aggregations.byAlertTypeId.value.namespaces).length, }; } diff --git a/x-pack/plugins/alerting/server/usage/alerts_usage_collector.ts b/x-pack/plugins/alerting/server/usage/alerts_usage_collector.ts index 453a29b5884e6..ecea721dfad92 100644 --- a/x-pack/plugins/alerting/server/usage/alerts_usage_collector.ts +++ b/x-pack/plugins/alerting/server/usage/alerts_usage_collector.ts @@ -91,6 +91,7 @@ export function createAlertsUsageCollector( }, count_active_by_type: {}, count_by_type: {}, + count_rules_namespaces: 0, }; } }, @@ -115,6 +116,7 @@ export function createAlertsUsageCollector( }, count_active_by_type: byTypeSchema, count_by_type: byTypeSchema, + count_rules_namespaces: { type: 'long' }, }, }); } diff --git a/x-pack/plugins/alerting/server/usage/task.ts b/x-pack/plugins/alerting/server/usage/task.ts index 043d970ddd231..9d39b3765cb5d 100644 --- a/x-pack/plugins/alerting/server/usage/task.ts +++ b/x-pack/plugins/alerting/server/usage/task.ts @@ -89,6 +89,7 @@ export function telemetryTaskRunner(logger: Logger, core: CoreSetup, kibanaIndex count_active_by_type: totalInUse.countByType, count_active_total: totalInUse.countTotal, count_disabled_total: totalCountAggregations.count_total - totalInUse.countTotal, + count_rules_namespaces: totalInUse.countNamespaces, }, runAt: getNextMidnight(), }; diff --git a/x-pack/plugins/alerting/server/usage/types.ts b/x-pack/plugins/alerting/server/usage/types.ts index c3c750da73a7f..5e420b54e37cb 100644 --- a/x-pack/plugins/alerting/server/usage/types.ts +++ b/x-pack/plugins/alerting/server/usage/types.ts @@ -11,6 +11,7 @@ export interface AlertsUsage { count_disabled_total: number; count_by_type: Record; count_active_by_type: Record; + count_rules_namespaces: number; throttle_time: { min: string; avg: string; diff --git a/x-pack/plugins/task_manager/server/usage/task_manager_usage_collector.ts b/x-pack/plugins/task_manager/server/usage/task_manager_usage_collector.ts index 6e9891ecd6d65..d6eb92b944ce1 100644 --- a/x-pack/plugins/task_manager/server/usage/task_manager_usage_collector.ts +++ b/x-pack/plugins/task_manager/server/usage/task_manager_usage_collector.ts @@ -52,6 +52,15 @@ export function createTaskManagerUsageCollector( }, }, task_type_exclusion: excludeTaskTypes, + failed_tasks: Object.entries(lastMonitoredHealth?.stats.workload?.value.task_types!).reduce( + (numb, [key, val]) => { + if (val.status.failed !== undefined) { + numb += val.status.failed; + } + return numb; + }, + 0 + ), }; }, schema: { @@ -79,6 +88,7 @@ export function createTaskManagerUsageCollector( }, }, task_type_exclusion: { type: 'array', items: { type: 'keyword' } }, + failed_tasks: { type: 'long' }, }, }); } diff --git a/x-pack/plugins/task_manager/server/usage/types.ts b/x-pack/plugins/task_manager/server/usage/types.ts index 0acbfd1d4fab9..f9ac823a58124 100644 --- a/x-pack/plugins/task_manager/server/usage/types.ts +++ b/x-pack/plugins/task_manager/server/usage/types.ts @@ -30,4 +30,5 @@ export interface TaskManagerUsage { p99: number; }; }; + failed_tasks: number; } diff --git a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json index 5bb559c137390..12763e4e26e31 100644 --- a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json +++ b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json @@ -11,15 +11,6 @@ "count_total": { "type": "long" }, - "count_active_total": { - "type": "long" - }, - "count_active_alert_history_connectors": { - "type": "long", - "_meta": { - "description": "The total number of preconfigured alert history connectors used by rules." - } - }, "count_by_type": { "properties": { "DYNAMIC_KEY": { @@ -60,6 +51,15 @@ } } }, + "count_active_total": { + "type": "long" + }, + "count_active_alert_history_connectors": { + "type": "long", + "_meta": { + "description": "The total number of preconfigured alert history connectors used by rules." + } + }, "count_active_by_type": { "properties": { "DYNAMIC_KEY": { @@ -99,6 +99,34 @@ "type": "long" } } + }, + "count_active_email_connectors_by_service_type": { + "properties": { + "DYNAMIC_KEY": { + "type": "long" + }, + "exchange_server": { + "type": "long" + }, + "gmail": { + "type": "long" + }, + "outlook365": { + "type": "long" + }, + "elastic_cloud": { + "type": "long" + }, + "other": { + "type": "long" + }, + "ses": { + "type": "long" + } + } + }, + "count_actions_namespaces": { + "type": "long" } } }, @@ -321,6 +349,9 @@ "type": "long" } } + }, + "count_rules_namespaces": { + "type": "long" } } }, @@ -7277,6 +7308,9 @@ "items": { "type": "keyword" } + }, + "failed_tasks": { + "type": "long" } } }, From fcb340fdb9220b5894f3da289428e02d9368364f Mon Sep 17 00:00:00 2001 From: Scotty Bollinger Date: Tue, 19 Oct 2021 13:39:47 -0500 Subject: [PATCH 083/204] Handle edge case when estimates are empty (#115476) There is an edge case where a connector does not sync and there is no estimate of next syncs in the UI. In this case, we simply omit the estimates from the UI. --- .../public/applications/workplace_search/types.ts | 2 +- .../synchronization/frequency_item.test.tsx | 6 ++++++ .../components/synchronization/frequency_item.tsx | 14 ++++++++++---- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/types.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/types.ts index ab45c54cc5c57..2a982c2be1cfc 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/types.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/types.ts @@ -130,7 +130,7 @@ interface SourceActivity { export interface SyncEstimate { duration?: string; - nextStart: string; + nextStart?: string; lastRun?: string; } diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/frequency_item.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/frequency_item.test.tsx index 7498a185a80ec..12cd07ac23b7c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/frequency_item.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/frequency_item.test.tsx @@ -122,4 +122,10 @@ describe('FrequencyItem', () => { expect(setSyncFrequency).toHaveBeenCalledWith('full', '3', 'minutes'); }); }); + + it('handles edge case where estimate is empty', () => { + const wrapper = shallow(); + + expect(wrapper.find('[data-test-subj="SyncEstimates"]')).toHaveLength(0); + }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/frequency_item.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/frequency_item.tsx index a51500e3076a8..f0066f06466a7 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/frequency_item.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/synchronization/frequency_item.tsx @@ -53,6 +53,7 @@ export const FrequencyItem: React.FC = ({ const estimateDisplay = durationEstimate && moment.duration(durationEstimate).humanize(); const nextStartIsPast = moment().isAfter(nextStart); const nextStartTime = nextStartIsPast ? NEXT_SYNC_RUNNING_MESSAGE : moment(nextStart).fromNow(); + const showEstimates = lastRun || nextStart || durationEstimate; const frequencyItemLabel = ( = ({ - - - {lastRun && lastRunSummary} {nextStartSummary} {estimateDisplay && estimateSummary} - + {showEstimates && ( + <> + + + {lastRun && lastRunSummary} {nextStart && nextStartSummary}{' '} + {estimateDisplay && estimateSummary} + + + )} ); From 6f56668cc5a0dbddc984e8982d004afec4f1d403 Mon Sep 17 00:00:00 2001 From: Jason Stoltzfus Date: Tue, 19 Oct 2021 14:49:55 -0400 Subject: [PATCH 084/204] Fix style (#115439) --- .../views/curation_suggestion/curation_result_panel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_result_panel.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_result_panel.tsx index bbd61804ad341..b8a659118e736 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_result_panel.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_result_panel.tsx @@ -73,7 +73,7 @@ export const CurationResultPanel: React.FC = ({ variant, results }) => { > {results.length > 0 ? ( results.map((result, index) => ( - + Date: Tue, 19 Oct 2021 11:50:23 -0700 Subject: [PATCH 085/204] skip flaky suite (#115614) --- x-pack/test/accessibility/apps/lens.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/accessibility/apps/lens.ts b/x-pack/test/accessibility/apps/lens.ts index ff769ddd29bfc..b8ddd774741b6 100644 --- a/x-pack/test/accessibility/apps/lens.ts +++ b/x-pack/test/accessibility/apps/lens.ts @@ -14,7 +14,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const listingTable = getService('listingTable'); - describe('Lens', () => { + // Failing: See https://github.com/elastic/kibana/issues/115614 + describe.skip('Lens', () => { const lensChartName = 'MyLensChart'; before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/logstash_functional'); From 30ce299589603523d05783a5d309eed23fe8f125 Mon Sep 17 00:00:00 2001 From: Georgii Gorbachev Date: Tue, 19 Oct 2021 20:57:55 +0200 Subject: [PATCH 086/204] [Security Solution][Detections] Enable writing rule execution events to Event Log by default (#115394) * Enable writing rule execution events to Event Log by default * Update event log provider name according to the RFC * Fix SavedObjectClient find method arguments Co-authored-by: Dmitry Shevchenko --- .../detection_alerts/building_block_alerts.spec.ts | 8 ++++++-- x-pack/plugins/security_solution/server/config.ts | 2 +- .../rule_execution_log/event_log_adapter/constants.ts | 2 +- .../rule_status_saved_objects_client.ts | 7 ++++++- .../saved_objects_adapter/saved_objects_adapter.ts | 3 +-- 5 files changed, 15 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/building_block_alerts.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/building_block_alerts.spec.ts index 262ffe8163e57..94418e61b4053 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/building_block_alerts.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/building_block_alerts.spec.ts @@ -8,19 +8,23 @@ import { getBuildingBlockRule } from '../../objects/rule'; import { OVERVIEW_ALERTS_HISTOGRAM } from '../../screens/overview'; import { OVERVIEW } from '../../screens/security_header'; +import { waitForAlertsIndexToBeCreated, waitForAlertsPanelToBeLoaded } from '../../tasks/alerts'; import { goToRuleDetails } from '../../tasks/alerts_detection_rules'; import { createCustomRuleActivated } from '../../tasks/api_calls/rules'; import { cleanKibana } from '../../tasks/common'; import { waitForAlertsToPopulate, waitForTheRuleToBeExecuted } from '../../tasks/create_new_rule'; -import { loginAndWaitForPage } from '../../tasks/login'; +import { loginAndWaitForPage, loginAndWaitForPageWithoutDateRange } from '../../tasks/login'; import { navigateFromHeaderTo } from '../../tasks/security_header'; -import { DETECTIONS_RULE_MANAGEMENT_URL } from '../../urls/navigation'; +import { ALERTS_URL, DETECTIONS_RULE_MANAGEMENT_URL } from '../../urls/navigation'; const EXPECTED_NUMBER_OF_ALERTS = 16; describe('Alerts generated by building block rules', () => { beforeEach(() => { cleanKibana(); + loginAndWaitForPageWithoutDateRange(ALERTS_URL); + waitForAlertsPanelToBeLoaded(); + waitForAlertsIndexToBeCreated(); }); it('Alerts should be visible on the Rule Detail page and not visible on the Overview page', () => { diff --git a/x-pack/plugins/security_solution/server/config.ts b/x-pack/plugins/security_solution/server/config.ts index e0b8ad883f4a2..61cbb5641c5f6 100644 --- a/x-pack/plugins/security_solution/server/config.ts +++ b/x-pack/plugins/security_solution/server/config.ts @@ -112,7 +112,7 @@ export const configSchema = schema.object({ schema.literal(UnderlyingLogClient.eventLog), schema.literal(UnderlyingLogClient.savedObjects), ], - { defaultValue: UnderlyingLogClient.savedObjects } + { defaultValue: UnderlyingLogClient.eventLog } ), }), diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log_adapter/constants.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log_adapter/constants.ts index f09eb43bf15f1..55624b56e39a0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log_adapter/constants.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log_adapter/constants.ts @@ -5,7 +5,7 @@ * 2.0. */ -export const RULE_EXECUTION_LOG_PROVIDER = 'rule-execution.security'; +export const RULE_EXECUTION_LOG_PROVIDER = 'securitySolution.ruleExecution'; export const ALERT_SAVED_OBJECT_TYPE = 'alert'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/saved_objects_adapter/rule_status_saved_objects_client.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/saved_objects_adapter/rule_status_saved_objects_client.ts index 66b646e96ea53..0026bba24eebe 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/saved_objects_adapter/rule_status_saved_objects_client.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/saved_objects_adapter/rule_status_saved_objects_client.ts @@ -21,7 +21,7 @@ import { IRuleStatusSOAttributes } from '../../rules/types'; export interface RuleStatusSavedObjectsClient { find: ( - options?: Omit + options: Omit & { ruleId: string } ) => Promise>>; findBulk: (ids: string[], statusesPerId: number) => Promise; create: ( @@ -47,9 +47,14 @@ export const ruleStatusSavedObjectsClientFactory = ( savedObjectsClient: SavedObjectsClientContract ): RuleStatusSavedObjectsClient => ({ find: async (options) => { + const references = { + id: options.ruleId, + type: 'alert', + }; const result = await savedObjectsClient.find({ ...options, type: legacyRuleStatusSavedObjectType, + hasReference: references, }); return result.saved_objects; }, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/saved_objects_adapter/saved_objects_adapter.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/saved_objects_adapter/saved_objects_adapter.ts index 9db7afce62ee4..70db3a768fdb1 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/saved_objects_adapter/saved_objects_adapter.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/saved_objects_adapter/saved_objects_adapter.ts @@ -53,8 +53,7 @@ export class SavedObjectsAdapter implements IRuleExecutionLogClient { perPage: logsCount, sortField: 'statusDate', sortOrder: 'desc', - search: ruleId, - searchFields: ['references.id'], + ruleId, }); } From f2ef8b396440275bf996264ee701a60e76effc13 Mon Sep 17 00:00:00 2001 From: Tyler Smalley Date: Tue, 19 Oct 2021 12:19:26 -0700 Subject: [PATCH 087/204] skip flaky suite (#92528) --- .../public/ui/query_string_input/query_bar_top_row.test.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/data/public/ui/query_string_input/query_bar_top_row.test.tsx b/src/plugins/data/public/ui/query_string_input/query_bar_top_row.test.tsx index b7ec5a1f0c286..0541e12cf8172 100644 --- a/src/plugins/data/public/ui/query_string_input/query_bar_top_row.test.tsx +++ b/src/plugins/data/public/ui/query_string_input/query_bar_top_row.test.tsx @@ -103,7 +103,8 @@ function wrapQueryBarTopRowInContext(testProps: any) { ); } -describe('QueryBarTopRowTopRow', () => { +// Failing: See https://github.com/elastic/kibana/issues/92528 +describe.skip('QueryBarTopRowTopRow', () => { const QUERY_INPUT_SELECTOR = 'QueryStringInputUI'; const TIMEPICKER_SELECTOR = 'EuiSuperDatePicker'; const TIMEPICKER_DURATION = '[data-shared-timefilter-duration]'; From ea00e608dc9fdac12f3045f3b526489f0f5eccc8 Mon Sep 17 00:00:00 2001 From: Byron Hulcher Date: Tue, 19 Oct 2021 15:39:21 -0400 Subject: [PATCH 088/204] [App Search] Add EntSearchLogStream components to Curations History panels (#115467) --- ...automated_curations_history_panel.test.tsx | 35 ++++++++++ .../automated_curations_history_panel.tsx | 68 +++++++++++++++++++ .../curation_changes_panel.test.tsx | 22 ------ .../components/curation_changes_panel.tsx | 23 ------- .../curations_history/components/index.ts | 4 +- .../rejected_curations_history_panel.test.tsx | 35 ++++++++++ .../rejected_curations_history_panel.tsx | 68 +++++++++++++++++++ .../rejected_curations_panel.test.tsx | 22 ------ .../components/rejected_curations_panel.tsx | 23 ------- .../curations_history.test.tsx | 10 ++- .../curations_history/curations_history.tsx | 10 ++- 11 files changed, 222 insertions(+), 98 deletions(-) create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/automated_curations_history_panel.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/automated_curations_history_panel.tsx delete mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/curation_changes_panel.test.tsx delete mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/curation_changes_panel.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/rejected_curations_history_panel.test.tsx create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/rejected_curations_history_panel.tsx delete mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/rejected_curations_panel.test.tsx delete mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/rejected_curations_panel.tsx diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/automated_curations_history_panel.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/automated_curations_history_panel.test.tsx new file mode 100644 index 0000000000000..044637ff1c823 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/automated_curations_history_panel.test.tsx @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +// TODO use engine_logic.mock instead of setMockValues({ engineName: 'some-engine' }), currently that breaks the test +import { setMockValues } from '../../../../../../__mocks__/kea_logic'; +// import '../../../../../__mocks__/engine_logic.mock'; + +import React from 'react'; + +import { shallow } from 'enzyme'; + +import { EntSearchLogStream } from '../../../../../../shared/log_stream'; +import { DataPanel } from '../../../../data_panel'; + +import { AutomatedCurationsHistoryPanel } from './automated_curations_history_panel'; + +describe('AutomatedCurationsHistoryPanel', () => { + beforeEach(() => { + jest.clearAllMocks(); + setMockValues({ engineName: 'some-engine' }); + }); + + it('renders', () => { + const wrapper = shallow(); + + expect(wrapper.is(DataPanel)).toBe(true); + expect(wrapper.find(EntSearchLogStream).prop('query')).toEqual( + 'event.kind: event and event.dataset: search-relevance-suggestions and appsearch.search_relevance_suggestions.engine: some-engine and event.action: curation_suggestion and appsearch.search_relevance_suggestions.suggestion.new_status: automated' + ); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/automated_curations_history_panel.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/automated_curations_history_panel.tsx new file mode 100644 index 0000000000000..c3c5747b29954 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/automated_curations_history_panel.tsx @@ -0,0 +1,68 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { useValues } from 'kea'; + +import { i18n } from '@kbn/i18n'; + +import { EntSearchLogStream } from '../../../../../../shared/log_stream'; +import { DataPanel } from '../../../../data_panel'; +import { EngineLogic } from '../../../../engine'; + +export const AutomatedCurationsHistoryPanel: React.FC = () => { + const { engineName } = useValues(EngineLogic); + + const filters = [ + 'event.kind: event', + 'event.dataset: search-relevance-suggestions', + `appsearch.search_relevance_suggestions.engine: ${engineName}`, + 'event.action: curation_suggestion', + 'appsearch.search_relevance_suggestions.suggestion.new_status: automated', + ]; + + return ( + + {i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.curations.automatedCurationsHistoryPanel.tableTitle', + { + defaultMessage: 'Automated curation changes', + } + )} + + } + subtitle={i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.curations.automatedCurationsHistoryPanel.tableDecription', + { + defaultMessage: 'A detailed log of recent changes to your automated curations.', + } + )} + hasBorder + > + + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/curation_changes_panel.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/curation_changes_panel.test.tsx deleted file mode 100644 index 7fc06beaa86a9..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/curation_changes_panel.test.tsx +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; - -import { shallow } from 'enzyme'; - -import { DataPanel } from '../../../../data_panel'; - -import { CurationChangesPanel } from './curation_changes_panel'; - -describe('CurationChangesPanel', () => { - it('renders', () => { - const wrapper = shallow(); - - expect(wrapper.is(DataPanel)).toBe(true); - }); -}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/curation_changes_panel.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/curation_changes_panel.tsx deleted file mode 100644 index 0aaf20485966e..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/curation_changes_panel.tsx +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; - -import { DataPanel } from '../../../../data_panel'; - -export const CurationChangesPanel: React.FC = () => { - return ( - Automated curation changes} - subtitle={A detailed log of recent changes to your automated curations} - iconType="visTable" - hasBorder - > - Embedded logs view goes here... - - ); -}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/index.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/index.ts index 43651e613364e..2f2c2aeb63503 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/index.ts @@ -5,6 +5,6 @@ * 2.0. */ -export { CurationChangesPanel } from './curation_changes_panel'; +export { AutomatedCurationsHistoryPanel } from './automated_curations_history_panel'; export { IgnoredQueriesPanel } from './ignored_queries_panel'; -export { RejectedCurationsPanel } from './rejected_curations_panel'; +export { RejectedCurationsHistoryPanel } from './rejected_curations_history_panel'; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/rejected_curations_history_panel.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/rejected_curations_history_panel.test.tsx new file mode 100644 index 0000000000000..28bb317941e1c --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/rejected_curations_history_panel.test.tsx @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +// TODO use engine_logic.mock instead of setMockValues({ engineName: 'some-engine' }), currently that breaks the test +import { setMockValues } from '../../../../../../__mocks__/kea_logic'; +// import '../../../../../__mocks__/engine_logic.mock'; + +import React from 'react'; + +import { shallow } from 'enzyme'; + +import { EntSearchLogStream } from '../../../../../../shared/log_stream'; +import { DataPanel } from '../../../../data_panel'; + +import { RejectedCurationsHistoryPanel } from './rejected_curations_history_panel'; + +describe('RejectedCurationsHistoryPanel', () => { + beforeEach(() => { + jest.clearAllMocks(); + setMockValues({ engineName: 'some-engine' }); + }); + + it('renders', () => { + const wrapper = shallow(); + + expect(wrapper.is(DataPanel)).toBe(true); + expect(wrapper.find(EntSearchLogStream).prop('query')).toEqual( + 'event.kind: event and event.dataset: search-relevance-suggestions and appsearch.search_relevance_suggestions.engine: some-engine and event.action: curation_suggestion and appsearch.search_relevance_suggestions.suggestion.new_status: rejected' + ); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/rejected_curations_history_panel.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/rejected_curations_history_panel.tsx new file mode 100644 index 0000000000000..275083f91c0fb --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/rejected_curations_history_panel.tsx @@ -0,0 +1,68 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { useValues } from 'kea'; + +import { i18n } from '@kbn/i18n'; + +import { EntSearchLogStream } from '../../../../../../shared/log_stream'; +import { DataPanel } from '../../../../data_panel'; +import { EngineLogic } from '../../../../engine'; + +export const RejectedCurationsHistoryPanel: React.FC = () => { + const { engineName } = useValues(EngineLogic); + + const filters = [ + 'event.kind: event', + 'event.dataset: search-relevance-suggestions', + `appsearch.search_relevance_suggestions.engine: ${engineName}`, + 'event.action: curation_suggestion', + 'appsearch.search_relevance_suggestions.suggestion.new_status: rejected', + ]; + + return ( + + {i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.curations.rejectedCurationsHistoryPanel.tableTitle', + { + defaultMessage: 'Recently rejected suggestions', + } + )} + + } + subtitle={i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.curations.rejectedCurationsHistoryPanel.tableDescription', + { + defaultMessage: 'View suggestions you’ve previously rejected.', + } + )} + hasBorder + > + + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/rejected_curations_panel.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/rejected_curations_panel.test.tsx deleted file mode 100644 index a40eb8895ad69..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/rejected_curations_panel.test.tsx +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; - -import { shallow } from 'enzyme'; - -import { DataPanel } from '../../../../data_panel'; - -import { RejectedCurationsPanel } from './rejected_curations_panel'; - -describe('RejectedCurationsPanel', () => { - it('renders', () => { - const wrapper = shallow(); - - expect(wrapper.is(DataPanel)).toBe(true); - }); -}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/rejected_curations_panel.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/rejected_curations_panel.tsx deleted file mode 100644 index 51719b4eebbd7..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/components/rejected_curations_panel.tsx +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; - -import { DataPanel } from '../../../../data_panel'; - -export const RejectedCurationsPanel: React.FC = () => { - return ( - Rececntly rejected sugggestions} - subtitle={Recent suggestions that are still valid can be re-enabled from here} - iconType="crossInACircleFilled" - hasBorder - > - Embedded logs view goes here... - - ); -}; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/curations_history.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/curations_history.test.tsx index 407454922ef05..c5dc20024b62f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/curations_history.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/curations_history.test.tsx @@ -9,15 +9,19 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { CurationChangesPanel, IgnoredQueriesPanel, RejectedCurationsPanel } from './components'; +import { + AutomatedCurationsHistoryPanel, + IgnoredQueriesPanel, + RejectedCurationsHistoryPanel, +} from './components'; import { CurationsHistory } from './curations_history'; describe('CurationsHistory', () => { it('renders', () => { const wrapper = shallow(); - expect(wrapper.find(CurationChangesPanel)).toHaveLength(1); - expect(wrapper.find(RejectedCurationsPanel)).toHaveLength(1); + expect(wrapper.find(AutomatedCurationsHistoryPanel)).toHaveLength(1); + expect(wrapper.find(RejectedCurationsHistoryPanel)).toHaveLength(1); expect(wrapper.find(IgnoredQueriesPanel)).toHaveLength(1); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/curations_history.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/curations_history.tsx index 5f857087e05ef..41250d8f67dfd 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/curations_history.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_history/curations_history.tsx @@ -9,7 +9,11 @@ import React from 'react'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { CurationChangesPanel, IgnoredQueriesPanel, RejectedCurationsPanel } from './components'; +import { + AutomatedCurationsHistoryPanel, + IgnoredQueriesPanel, + RejectedCurationsHistoryPanel, +} from './components'; export const CurationsHistory: React.FC = () => { return ( @@ -17,10 +21,10 @@ export const CurationsHistory: React.FC = () => { - + - + From 5477f609de6d32b82b096e5611e8379937383f9b Mon Sep 17 00:00:00 2001 From: Shahzad Date: Tue, 19 Oct 2021 21:49:34 +0200 Subject: [PATCH 089/204] [Uptime] Added trends chart and deep link to exp view on waterfall step metric markers (#114068) * added trend chart and link to exp view * Added step level filtering/breakdowns * fix type * make step and monitor name single select * fix test * added step filters * pr feedback * hide step filter for non step metrics * pr feedback * remove step def when it's not applicable * ^^also for step name breakdown * Update x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/breakdown/breakdowns.tsx Co-authored-by: Dominique Clarke * use side effct * update monitor filter * update all_value usage * refactor * fix type * use last value operation * use last 48 intervals Co-authored-by: Dominique Clarke --- .../configurations/lens_attributes.ts | 50 ++++++++- .../synthetics/kpi_over_time_config.ts | 20 ++-- .../embeddable/embeddable.tsx | 23 +++- .../columns/operation_type_select.tsx | 6 + .../common/runtime_types/ping/synthetics.ts | 4 + .../step_detail/step_detail_container.tsx | 6 +- .../waterfall/waterfall_chart_container.tsx | 5 +- .../waterfall/waterfall_chart_wrapper.tsx | 10 +- .../components/waterfall_marker_icon.tsx | 38 +++++++ .../components/waterfall_marker_trend.tsx | 106 ++++++++++++++++++ .../components/waterfall_markers.tsx | 44 ++++++-- .../waterfall/context/waterfall_chart.tsx | 5 + .../uptime_startup_plugins_context.tsx | 4 +- 13 files changed, 293 insertions(+), 28 deletions(-) create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_marker_icon.tsx create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_marker_trend.tsx diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts index a31bef7c9c214..5e769882a2793 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts @@ -14,6 +14,7 @@ import { AvgIndexPatternColumn, MedianIndexPatternColumn, PercentileIndexPatternColumn, + LastValueIndexPatternColumn, OperationType, PersistedIndexPatternLayer, RangeIndexPatternColumn, @@ -219,6 +220,15 @@ export class LensAttributes { columnFilter, }); } + if (operationType === 'last_value') { + return this.getLastValueOperationColumn({ + sourceField, + operationType, + label, + seriesConfig, + columnFilter, + }); + } if (operationType?.includes('th')) { return this.getPercentileNumberColumn(sourceField, operationType, seriesConfig!); } @@ -226,6 +236,36 @@ export class LensAttributes { return this.getNumberRangeColumn(sourceField, seriesConfig!, label); } + getLastValueOperationColumn({ + sourceField, + label, + seriesConfig, + operationType, + columnFilter, + }: { + sourceField: string; + operationType: 'last_value'; + label?: string; + seriesConfig: SeriesConfig; + columnFilter?: ColumnFilter; + }): LastValueIndexPatternColumn { + return { + ...buildNumberColumn(sourceField), + operationType, + label: i18n.translate('xpack.observability.expView.columns.operation.label', { + defaultMessage: '{operationType} of {sourceField}', + values: { + sourceField: label || seriesConfig.labels[sourceField], + operationType: capitalize(operationType), + }, + }), + filter: columnFilter, + params: { + sortField: '@timestamp', + }, + }; + } + getNumberOperationColumn({ sourceField, label, @@ -622,6 +662,12 @@ export class LensAttributes { const label = timeShift ? `${mainYAxis.label}(${timeShift})` : mainYAxis.label; + let filterQuery = columnFilter || mainYAxis.filter?.query; + + if (columnFilter && mainYAxis.filter?.query) { + filterQuery = `${columnFilter} and ${mainYAxis.filter.query}`; + } + layers[layerId] = { columnOrder: [ `x-axis-column-${layerId}`, @@ -637,9 +683,7 @@ export class LensAttributes { ...mainYAxis, label, filter: { - query: mainYAxis.filter - ? `${columnFilter} and ${mainYAxis.filter.query}` - : columnFilter, + query: filterQuery ?? '', language: 'kuery', }, ...(timeShift ? { timeShift } : {}), diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/synthetics/kpi_over_time_config.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/synthetics/kpi_over_time_config.ts index 8951ffcda63d8..e548ec2714e14 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/synthetics/kpi_over_time_config.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/synthetics/kpi_over_time_config.ts @@ -111,44 +111,48 @@ export function getSyntheticsKPIConfig({ indexPattern }: ConfigProps): SeriesCon field: SYNTHETICS_LCP, id: SYNTHETICS_LCP, columnType: OPERATION_COLUMN, - columnFilters: [STEP_METRIC_FILTER], + columnFilters: getStepMetricColumnFilter(SYNTHETICS_LCP), }, { label: FCP_LABEL, field: SYNTHETICS_FCP, id: SYNTHETICS_FCP, columnType: OPERATION_COLUMN, - columnFilters: [STEP_METRIC_FILTER], + columnFilters: getStepMetricColumnFilter(SYNTHETICS_FCP), }, { label: DCL_LABEL, field: SYNTHETICS_DCL, id: SYNTHETICS_DCL, columnType: OPERATION_COLUMN, - columnFilters: [STEP_METRIC_FILTER], + columnFilters: getStepMetricColumnFilter(SYNTHETICS_DCL), }, { label: DOCUMENT_ONLOAD_LABEL, field: SYNTHETICS_DOCUMENT_ONLOAD, id: SYNTHETICS_DOCUMENT_ONLOAD, columnType: OPERATION_COLUMN, - columnFilters: [STEP_METRIC_FILTER], + columnFilters: getStepMetricColumnFilter(SYNTHETICS_DOCUMENT_ONLOAD), }, { label: CLS_LABEL, field: SYNTHETICS_CLS, id: SYNTHETICS_CLS, columnType: OPERATION_COLUMN, - columnFilters: [STEP_METRIC_FILTER], + columnFilters: getStepMetricColumnFilter(SYNTHETICS_CLS), }, ], labels: { ...FieldLabels, [SUMMARY_UP]: UP_LABEL, [SUMMARY_DOWN]: DOWN_LABEL }, }; } -const STEP_METRIC_FILTER: ColumnFilter = { - language: 'kuery', - query: `synthetics.type: step/metrics`, +const getStepMetricColumnFilter = (field: string): ColumnFilter[] => { + return [ + { + language: 'kuery', + query: `synthetics.type: step/metrics and ${field}: *`, + }, + ]; }; const STEP_END_FILTER: ColumnFilter = { diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.tsx index 10ec4075a8155..35ce2fc6c1a47 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.tsx @@ -12,7 +12,7 @@ import { AllSeries, useTheme } from '../../../..'; import { LayerConfig, LensAttributes } from '../configurations/lens_attributes'; import { ReportViewType } from '../types'; import { getLayerConfigs } from '../hooks/use_lens_attributes'; -import { LensPublicStart } from '../../../../../../lens/public'; +import { LensPublicStart, XYState } from '../../../../../../lens/public'; import { OperationTypeComponent } from '../series_editor/columns/operation_type_select'; import { IndexPatternState } from '../hooks/use_app_index_pattern'; @@ -22,6 +22,8 @@ export interface ExploratoryEmbeddableProps { appendTitle?: JSX.Element; title: string | JSX.Element; showCalculationMethod?: boolean; + axisTitlesVisibility?: XYState['axisTitlesVisibilitySettings']; + legendIsVisible?: boolean; } export interface ExploratoryEmbeddableComponentProps extends ExploratoryEmbeddableProps { @@ -37,6 +39,8 @@ export default function Embeddable({ appendTitle, indexPatterns, lens, + axisTitlesVisibility, + legendIsVisible, showCalculationMethod = false, }: ExploratoryEmbeddableComponentProps) { const LensComponent = lens?.EmbeddableComponent; @@ -57,11 +61,20 @@ export default function Embeddable({ return No lens component; } + const attributesJSON = lensAttributes.getJSON(); + + (attributesJSON.state.visualization as XYState).axisTitlesVisibilitySettings = + axisTitlesVisibility; + + if (typeof legendIsVisible !== 'undefined') { + (attributesJSON.state.visualization as XYState).legend.isVisible = legendIsVisible; + } + return ( - + - +

{title}

@@ -81,7 +94,7 @@ export default function Embeddable({ id="exploratoryView" style={{ height: '100%' }} timeRange={series?.time} - attributes={lensAttributes.getJSON()} + attributes={attributesJSON} onBrushEnd={({ range }) => {}} /> @@ -92,7 +105,7 @@ const Wrapper = styled.div` height: 100%; &&& { > :nth-child(2) { - height: calc(100% - 56px); + height: calc(100% - 32px); } } `; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/operation_type_select.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/operation_type_select.tsx index 6d83e25cc96e3..a223a74d74aea 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/operation_type_select.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/operation_type_select.tsx @@ -73,6 +73,12 @@ export function OperationTypeComponent({ defaultMessage: 'Sum', }), }, + { + value: 'last_value' as OperationType, + inputDisplay: i18n.translate('xpack.observability.expView.operationType.lastValue', { + defaultMessage: 'Last value', + }), + }, { value: '75th' as OperationType, inputDisplay: i18n.translate('xpack.observability.expView.operationType.75thPercentile', { diff --git a/x-pack/plugins/uptime/common/runtime_types/ping/synthetics.ts b/x-pack/plugins/uptime/common/runtime_types/ping/synthetics.ts index e7948f4ad532c..7b181ac2cf50c 100644 --- a/x-pack/plugins/uptime/common/runtime_types/ping/synthetics.ts +++ b/x-pack/plugins/uptime/common/runtime_types/ping/synthetics.ts @@ -22,6 +22,10 @@ export const JourneyStepType = t.intersection([ name: t.string, status: t.string, type: t.string, + timespan: t.type({ + gte: t.string, + lt: t.string, + }), }), synthetics: t.partial({ error: t.partial({ diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/step_detail_container.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/step_detail_container.tsx index 777491c503fd9..1e493ad21b4d3 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/step_detail_container.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/step_detail_container.tsx @@ -45,7 +45,11 @@ export const StepDetailContainer: React.FC = ({ checkGroup, stepIndex }) )} {journey && activeStep && !journey.loading && ( - + )} ); diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_container.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_container.tsx index d249c23c44d75..95da0ea0a45f6 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_container.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_container.tsx @@ -15,6 +15,7 @@ import { networkEventsSelector } from '../../../../../state/selectors'; import { WaterfallChartWrapper } from './waterfall_chart_wrapper'; import { extractItems } from './data_formatting'; import { useStepWaterfallMetrics } from '../use_step_waterfall_metrics'; +import { JourneyStep } from '../../../../../../common/runtime_types'; export const NO_DATA_TEXT = i18n.translate('xpack.uptime.synthetics.stepDetail.waterfallNoData', { defaultMessage: 'No waterfall data could be found for this step', @@ -22,10 +23,11 @@ export const NO_DATA_TEXT = i18n.translate('xpack.uptime.synthetics.stepDetail.w interface Props { checkGroup: string; + activeStep?: JourneyStep; stepIndex: number; } -export const WaterfallChartContainer: React.FC = ({ checkGroup, stepIndex }) => { +export const WaterfallChartContainer: React.FC = ({ checkGroup, stepIndex, activeStep }) => { const dispatch = useDispatch(); useEffect(() => { @@ -79,6 +81,7 @@ export const WaterfallChartContainer: React.FC = ({ checkGroup, stepIndex data={extractItems(networkEvents.events)} markerItems={metrics} total={networkEvents.total} + activeStep={activeStep} /> )} {waterfallLoaded && hasEvents && !isWaterfallSupported && ( diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_wrapper.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_wrapper.tsx index 8071fd1e3c4d3..aaa3d5c1cc23c 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_wrapper.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_wrapper.tsx @@ -15,6 +15,7 @@ import { WaterfallFilter } from './waterfall_filter'; import { WaterfallFlyout } from './waterfall_flyout'; import { WaterfallSidebarItem } from './waterfall_sidebar_item'; import { MarkerItems } from '../../waterfall/context/waterfall_chart'; +import { JourneyStep } from '../../../../../../common/runtime_types'; export const renderLegendItem: RenderItem = (item) => { return ( @@ -26,11 +27,17 @@ export const renderLegendItem: RenderItem = (item) => { interface Props { total: number; + activeStep?: JourneyStep; data: NetworkItems; markerItems?: MarkerItems; } -export const WaterfallChartWrapper: React.FC = ({ data, total, markerItems }) => { +export const WaterfallChartWrapper: React.FC = ({ + data, + total, + markerItems, + activeStep, +}) => { const [query, setQuery] = useState(''); const [activeFilters, setActiveFilters] = useState([]); const [onlyHighlighted, setOnlyHighlighted] = useState(false); @@ -109,6 +116,7 @@ export const WaterfallChartWrapper: React.FC = ({ data, total, markerItem return ( ; + } + + return ( + setIsOpen(false)} + anchorPosition="downLeft" + panelStyle={{ paddingBottom: 0, paddingLeft: 4 }} + zIndex={100} + button={ + setIsOpen((prevState) => !prevState)} + /> + } + > + + + ); +} diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_marker_trend.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_marker_trend.tsx new file mode 100644 index 0000000000000..6ff7835633914 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_marker_trend.tsx @@ -0,0 +1,106 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; + +import { EuiButton } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import moment from 'moment'; +import { useUptimeStartPlugins } from '../../../../../contexts/uptime_startup_plugins_context'; +import { useUptimeSettingsContext } from '../../../../../contexts/uptime_settings_context'; +import { AllSeries, createExploratoryViewUrl } from '../../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common'; +import { useWaterfallContext } from '../context/waterfall_chart'; +import { JourneyStep } from '../../../../../../common/runtime_types'; + +const getLast48Intervals = (activeStep: JourneyStep) => { + const { lt, gte } = activeStep.monitor.timespan!; + const inDays = moment(lt).diff(moment(gte), 'days'); + if (inDays > 0) { + return { to: 'now', from: `now-${inDays * 48}d` }; + } + + const inHours = moment(lt).diff(moment(gte), 'hours'); + if (inHours > 0) { + return { to: 'now', from: `now-${inHours * 48}h` }; + } + + const inMinutes = moment(lt).diff(moment(gte), 'minutes'); + if (inMinutes > 0) { + return { to: 'now', from: `now-${inMinutes * 48}m` }; + } + + const inSeconds = moment(lt).diff(moment(gte), 'seconds'); + return { to: 'now', from: `now-${inSeconds * 48}s` }; +}; + +export function WaterfallMarkerTrend({ title, field }: { title: string; field: string }) { + const { observability } = useUptimeStartPlugins(); + + const EmbeddableExpVIew = observability!.ExploratoryViewEmbeddable; + + const { basePath } = useUptimeSettingsContext(); + + const { activeStep } = useWaterfallContext(); + + if (!activeStep) { + return null; + } + + const allSeries: AllSeries = [ + { + name: `${title}(${activeStep.synthetics.step?.name!})`, + selectedMetricField: field, + time: getLast48Intervals(activeStep), + seriesType: 'area', + dataType: 'synthetics', + reportDefinitions: { + 'monitor.name': [activeStep.monitor.name!], + 'synthetics.step.name.keyword': [activeStep.synthetics.step?.name!], + }, + operationType: 'last_value', + }, + ]; + + const href = createExploratoryViewUrl( + { + reportType: 'kpi-over-time', + allSeries, + }, + basePath + ); + + return ( + + + {EXPLORE_LABEL} + + } + reportType={'kpi-over-time'} + attributes={allSeries} + axisTitlesVisibility={{ x: false, yLeft: false, yRight: false }} + legendIsVisible={false} + /> + + ); +} + +export const EXPLORE_LABEL = i18n.translate('xpack.uptime.synthetics.markers.explore', { + defaultMessage: 'Explore', +}); + +const Wrapper = euiStyled.div` + height: 200px; + width: 400px; + &&& { + .expExpressionRenderer__expression { + padding-bottom: 0 !important; + } + } +`; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_markers.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_markers.tsx index b341b052e0102..d8f6468015ede 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_markers.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_markers.tsx @@ -7,11 +7,11 @@ import React from 'react'; import { AnnotationDomainType, LineAnnotation } from '@elastic/charts'; -import { EuiIcon } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { useWaterfallContext } from '..'; import { useTheme } from '../../../../../../../observability/public'; import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common'; +import { WaterfallMarkerIcon } from './waterfall_marker_icon'; export const FCP_LABEL = i18n.translate('xpack.uptime.synthetics.waterfall.fcpLabel', { defaultMessage: 'First contentful paint', @@ -39,6 +39,12 @@ export const DOCUMENT_CONTENT_LOADED_LABEL = i18n.translate( } ); +export const SYNTHETICS_CLS = 'browser.experience.cls'; +export const SYNTHETICS_LCP = 'browser.experience.lcp.us'; +export const SYNTHETICS_FCP = 'browser.experience.fcp.us'; +export const SYNTHETICS_DOCUMENT_ONLOAD = 'browser.experience.load.us'; +export const SYNTHETICS_DCL = 'browser.experience.dcl.us'; + export function WaterfallChartMarkers() { const { markerItems } = useWaterfallContext(); @@ -48,12 +54,32 @@ export function WaterfallChartMarkers() { return null; } - const markersInfo: Record = { - domContentLoaded: { label: DOCUMENT_CONTENT_LOADED_LABEL, color: theme.eui.euiColorVis0 }, - firstContentfulPaint: { label: FCP_LABEL, color: theme.eui.euiColorVis1 }, - largestContentfulPaint: { label: LCP_LABEL, color: theme.eui.euiColorVis2 }, - layoutShift: { label: LAYOUT_SHIFT_LABEL, color: theme.eui.euiColorVis3 }, - loadEvent: { label: LOAD_EVENT_LABEL, color: theme.eui.euiColorVis9 }, + const markersInfo: Record = { + domContentLoaded: { + label: DOCUMENT_CONTENT_LOADED_LABEL, + color: theme.eui.euiColorVis0, + field: SYNTHETICS_DCL, + }, + firstContentfulPaint: { + label: FCP_LABEL, + color: theme.eui.euiColorVis1, + field: SYNTHETICS_FCP, + }, + largestContentfulPaint: { + label: LCP_LABEL, + color: theme.eui.euiColorVis2, + field: SYNTHETICS_LCP, + }, + layoutShift: { + label: LAYOUT_SHIFT_LABEL, + color: theme.eui.euiColorVis3, + field: SYNTHETICS_CLS, + }, + loadEvent: { + label: LOAD_EVENT_LABEL, + color: theme.eui.euiColorVis9, + field: SYNTHETICS_DOCUMENT_ONLOAD, + }, }; return ( @@ -73,7 +99,9 @@ export function WaterfallChartMarkers() { }), }, ]} - marker={} + marker={ + + } style={{ line: { strokeWidth: 2, diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/context/waterfall_chart.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/context/waterfall_chart.tsx index cce0533293e07..d495b7432bce7 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/context/waterfall_chart.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/context/waterfall_chart.tsx @@ -9,6 +9,7 @@ import React, { createContext, useContext, Context } from 'react'; import { WaterfallData, WaterfallDataEntry, WaterfallMetadata } from '../types'; import { OnSidebarClick, OnElementClick, OnProjectionClick } from '../components/use_flyout'; import { SidebarItems } from '../../step_detail/waterfall/types'; +import { JourneyStep } from '../../../../../../common/runtime_types'; export type MarkerItems = Array<{ id: @@ -38,6 +39,7 @@ export interface IWaterfallContext { index?: number ) => JSX.Element; markerItems?: MarkerItems; + activeStep?: JourneyStep; } export const WaterfallContext = createContext>({}); @@ -56,6 +58,7 @@ interface ProviderProps { metadata: IWaterfallContext['metadata']; renderTooltipItem: IWaterfallContext['renderTooltipItem']; markerItems?: MarkerItems; + activeStep?: JourneyStep; } export const WaterfallProvider: React.FC = ({ @@ -73,11 +76,13 @@ export const WaterfallProvider: React.FC = ({ totalNetworkRequests, highlightedNetworkRequests, fetchedNetworkRequests, + activeStep, }) => { return ( >({}); @@ -14,3 +14,5 @@ export const UptimeStartupPluginsContextProvider: React.FC ; + +export const useUptimeStartPlugins = () => useContext(UptimeStartupPluginsContext); From a22066ef41a7d58cc23899a3b9b4126a83b53fca Mon Sep 17 00:00:00 2001 From: Michael Olorunnisola Date: Tue, 19 Oct 2021 16:18:17 -0400 Subject: [PATCH 090/204] [Security Solution] Fix height issue (#114718) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../public/components/t_grid/body/height_hack.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/x-pack/plugins/timelines/public/components/t_grid/body/height_hack.ts b/x-pack/plugins/timelines/public/components/t_grid/body/height_hack.ts index 5371d7004a864..26d32b13eede7 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/body/height_hack.ts +++ b/x-pack/plugins/timelines/public/components/t_grid/body/height_hack.ts @@ -35,6 +35,11 @@ const DATA_GRID_HEIGHT_BY_PAGE_SIZE: { [key: number]: number } = { * * Please delete me and allow DataGrid to calculate its height when the bug is fixed. */ + +const dataGridRowHeight = 36; +const headerSectionHeight = 32; +const additionalFiltersHeight = 44; + export const useDataGridHeightHack = (pageSize: number, rowCount: number) => { const [height, setHeight] = useState(DATA_GRID_HEIGHT_BY_PAGE_SIZE[pageSize]); @@ -44,7 +49,11 @@ export const useDataGridHeightHack = (pageSize: number, rowCount: number) => { if (rowCount === pageSize) { setHeight(DATA_GRID_HEIGHT_BY_PAGE_SIZE[pageSize]); + } else if (rowCount <= pageSize) { + // This is unnecessary if we add rowCount > pageSize below + setHeight(dataGridRowHeight * rowCount + (headerSectionHeight + additionalFiltersHeight)); } else if ( + // rowCount > pageSize && // This will fix the issue but is always full height so has a lot of empty state gridVirtualized && gridVirtualized.children[0].clientHeight !== gridVirtualized.clientHeight // check if it has vertical scroll ) { From 0b0d49ef5125f0551d4c6ddfc52f6bccc76df153 Mon Sep 17 00:00:00 2001 From: Zacqary Adam Xeper Date: Tue, 19 Oct 2021 15:51:39 -0500 Subject: [PATCH 091/204] [Logs/Metrics UI] Remove configurable fields in settings for 8.0.0 (#61302) * [Logs/Metrics UI] Remove configurable fields in settings for 8.0.0 * Fix logs fields * Fix i18n * Fix typecheck and i18n * Fix i18n Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../settings/fields_configuration_panel.tsx | 190 ------------ .../indices_configuration_form_state.ts | 43 +-- .../indices_configuration_panel.stories.tsx | 24 +- .../settings/indices_configuration_panel.tsx | 71 ++--- .../source_configuration_form_state.tsx | 18 +- .../source_configuration_settings.tsx | 4 - .../settings/fields_configuration_panel.tsx | 274 ------------------ .../indices_configuration_form_state.ts | 80 +---- .../source_configuration_form_state.tsx | 19 -- .../source_configuration_settings.tsx | 12 - .../translations/translations/ja-JP.json | 20 -- .../translations/translations/zh-CN.json | 20 -- 12 files changed, 33 insertions(+), 742 deletions(-) delete mode 100644 x-pack/plugins/infra/public/pages/logs/settings/fields_configuration_panel.tsx delete mode 100644 x-pack/plugins/infra/public/pages/metrics/settings/fields_configuration_panel.tsx diff --git a/x-pack/plugins/infra/public/pages/logs/settings/fields_configuration_panel.tsx b/x-pack/plugins/infra/public/pages/logs/settings/fields_configuration_panel.tsx deleted file mode 100644 index d21b0b0588dea..0000000000000 --- a/x-pack/plugins/infra/public/pages/logs/settings/fields_configuration_panel.tsx +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { - EuiCallOut, - EuiCode, - EuiDescribedFormGroup, - EuiFieldText, - EuiFormRow, - EuiLink, - EuiSpacer, - EuiTitle, -} from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; -import React, { useMemo } from 'react'; -import { FormElement } from './form_elements'; -import { getFormRowProps, getStringInputFieldProps } from './form_field_props'; -import { FormValidationError } from './validation_errors'; - -interface FieldsConfigurationPanelProps { - isLoading: boolean; - isReadOnly: boolean; - tiebreakerFieldFormElement: FormElement; - timestampFieldFormElement: FormElement; -} - -export const FieldsConfigurationPanel = ({ - isLoading, - isReadOnly, - tiebreakerFieldFormElement, - timestampFieldFormElement, -}: FieldsConfigurationPanelProps) => { - const isTimestampValueDefault = timestampFieldFormElement.value === '@timestamp'; - const isTiebreakerValueDefault = tiebreakerFieldFormElement.value === '_doc'; - - return ( - <> - -

- -

-
- - -

- - - - ), - ecsLink: ( - - ECS - - ), - }} - /> -

-
- - - - - } - description={ - - } - > - @timestamp, - }} - /> - } - label={ - - } - {...useMemo( - () => getFormRowProps(timestampFieldFormElement), - [timestampFieldFormElement] - )} - > - getStringInputFieldProps(timestampFieldFormElement), - [timestampFieldFormElement] - )} - /> - - - - - - } - description={ - - } - > - _doc, - }} - /> - } - label={ - - } - {...useMemo( - () => getFormRowProps(tiebreakerFieldFormElement), - [tiebreakerFieldFormElement] - )} - > - getStringInputFieldProps(tiebreakerFieldFormElement), - [tiebreakerFieldFormElement] - )} - /> - - - - ); -}; diff --git a/x-pack/plugins/infra/public/pages/logs/settings/indices_configuration_form_state.ts b/x-pack/plugins/infra/public/pages/logs/settings/indices_configuration_form_state.ts index 1a70aaff6636c..31f4b96c92379 100644 --- a/x-pack/plugins/infra/public/pages/logs/settings/indices_configuration_form_state.ts +++ b/x-pack/plugins/infra/public/pages/logs/settings/indices_configuration_form_state.ts @@ -14,7 +14,7 @@ import { LogIndexPatternReference, } from '../../../../common/log_sources'; import { useKibanaIndexPatternService } from '../../../hooks/use_kibana_index_patterns'; -import { useCompositeFormElement, useFormElement } from './form_elements'; +import { useFormElement } from './form_elements'; import { FormValidationError, validateIndexPattern, @@ -80,44 +80,3 @@ export const useLogIndicesFormElement = (initialValue: LogIndicesFormState) => { return logIndicesFormElement; }; - -export interface FieldsFormState { - tiebreakerField: string; - timestampField: string; -} - -export const useFieldsFormElement = (initialValues: FieldsFormState) => { - const tiebreakerFieldFormElement = useFormElement({ - initialValue: initialValues.tiebreakerField, - validate: useMemo( - () => async (tiebreakerField) => validateStringNotEmpty('tiebreaker', tiebreakerField), - [] - ), - }); - - const timestampFieldFormElement = useFormElement({ - initialValue: initialValues.timestampField, - validate: useMemo( - () => async (timestampField) => validateStringNotEmpty('timestamp', timestampField), - [] - ), - }); - - const fieldsFormElement = useCompositeFormElement( - useMemo( - () => ({ - childFormElements: { - tiebreaker: tiebreakerFieldFormElement, - timestamp: timestampFieldFormElement, - }, - }), - [tiebreakerFieldFormElement, timestampFieldFormElement] - ) - ); - - return { - fieldsFormElement, - tiebreakerFieldFormElement, - timestampFieldFormElement, - }; -}; diff --git a/x-pack/plugins/infra/public/pages/logs/settings/indices_configuration_panel.stories.tsx b/x-pack/plugins/infra/public/pages/logs/settings/indices_configuration_panel.stories.tsx index 546bb9aab0f33..cf0f302136fbf 100644 --- a/x-pack/plugins/infra/public/pages/logs/settings/indices_configuration_panel.stories.tsx +++ b/x-pack/plugins/infra/public/pages/logs/settings/indices_configuration_panel.stories.tsx @@ -15,12 +15,7 @@ import { MockIndexPatternsKibanaContextProvider, MockIndexPatternSpec, } from '../../../hooks/use_kibana_index_patterns.mock'; -import { - FieldsFormState, - LogIndicesFormState, - useFieldsFormElement, - useLogIndicesFormElement, -} from './indices_configuration_form_state'; +import { LogIndicesFormState, useLogIndicesFormElement } from './indices_configuration_form_state'; import { IndicesConfigurationPanel } from './indices_configuration_panel'; export default { @@ -69,17 +64,14 @@ type IndicesConfigurationPanelStoryArgs = Pick< > & { availableIndexPatterns: MockIndexPatternSpec[]; logIndices: LogIndicesFormState; - fields: FieldsFormState; }; const IndicesConfigurationPanelTemplate: Story = ({ isLoading, isReadOnly, logIndices, - fields, }) => { const logIndicesFormElement = useLogIndicesFormElement(logIndices); - const { tiebreakerFieldFormElement, timestampFieldFormElement } = useFieldsFormElement(fields); return ( <> @@ -87,8 +79,6 @@ const IndicesConfigurationPanelTemplate: Story // field states{'\n'} @@ -98,14 +88,6 @@ const IndicesConfigurationPanelTemplate: Story; - tiebreakerFieldFormElement: FormElement; - timestampFieldFormElement: FormElement; -}>( - ({ - isLoading, - isReadOnly, - indicesFormElement, - tiebreakerFieldFormElement, - timestampFieldFormElement, - }) => { - const trackSwitchToIndexPatternReference = useUiTracker({ app: 'infra_logs' }); +}>(({ isLoading, isReadOnly, indicesFormElement }) => { + const trackSwitchToIndexPatternReference = useUiTracker({ app: 'infra_logs' }); - const switchToIndexPatternReference = useCallback(() => { - indicesFormElement.updateValue(() => undefined); - trackSwitchToIndexPatternReference({ - metric: 'configuration_switch_to_index_pattern_reference', - }); - }, [indicesFormElement, trackSwitchToIndexPatternReference]); + const switchToIndexPatternReference = useCallback(() => { + indicesFormElement.updateValue(() => undefined); + trackSwitchToIndexPatternReference({ + metric: 'configuration_switch_to_index_pattern_reference', + }); + }, [indicesFormElement, trackSwitchToIndexPatternReference]); - if (isIndexPatternFormElement(indicesFormElement)) { - return ( - + ); + } else if (isIndexNamesFormElement(indicesFormElement)) { + return ( + <> + - ); - } else if (isIndexNamesFormElement(indicesFormElement)) { - return ( - <> - - - - ); - } else { - return null; - } + + ); + } else { + return null; } -); +}); const isIndexPatternFormElement = isFormElementForType( (value): value is LogIndexPatternReference | undefined => diff --git a/x-pack/plugins/infra/public/pages/logs/settings/source_configuration_form_state.tsx b/x-pack/plugins/infra/public/pages/logs/settings/source_configuration_form_state.tsx index ac390a5bcfb8b..6523708d18ee0 100644 --- a/x-pack/plugins/infra/public/pages/logs/settings/source_configuration_form_state.tsx +++ b/x-pack/plugins/infra/public/pages/logs/settings/source_configuration_form_state.tsx @@ -8,7 +8,7 @@ import { useMemo } from 'react'; import { LogSourceConfigurationProperties } from '../../../containers/logs/log_source'; import { useCompositeFormElement } from './form_elements'; -import { useFieldsFormElement, useLogIndicesFormElement } from './indices_configuration_form_state'; +import { useLogIndicesFormElement } from './indices_configuration_form_state'; import { useLogColumnsFormElement } from './log_columns_configuration_form_state'; import { useNameFormElement } from './name_configuration_form_state'; @@ -28,17 +28,6 @@ export const useLogSourceConfigurationFormState = ( ) ); - const { fieldsFormElement, tiebreakerFieldFormElement, timestampFieldFormElement } = - useFieldsFormElement( - useMemo( - () => ({ - tiebreakerField: configuration?.fields?.tiebreaker ?? '_doc', - timestampField: configuration?.fields?.timestamp ?? '@timestamp', - }), - [configuration] - ) - ); - const logColumnsFormElement = useLogColumnsFormElement( useMemo(() => configuration?.logColumns ?? [], [configuration]) ); @@ -49,12 +38,11 @@ export const useLogSourceConfigurationFormState = ( childFormElements: { name: nameFormElement, logIndices: logIndicesFormElement, - fields: fieldsFormElement, logColumns: logColumnsFormElement, }, validate: async () => [], }), - [nameFormElement, logIndicesFormElement, fieldsFormElement, logColumnsFormElement] + [nameFormElement, logIndicesFormElement, logColumnsFormElement] ) ); @@ -64,7 +52,5 @@ export const useLogSourceConfigurationFormState = ( logColumnsFormElement, nameFormElement, sourceConfigurationFormElement, - tiebreakerFieldFormElement, - timestampFieldFormElement, }; }; diff --git a/x-pack/plugins/infra/public/pages/logs/settings/source_configuration_settings.tsx b/x-pack/plugins/infra/public/pages/logs/settings/source_configuration_settings.tsx index 883c321db9ae6..b7fbef74781fc 100644 --- a/x-pack/plugins/infra/public/pages/logs/settings/source_configuration_settings.tsx +++ b/x-pack/plugins/infra/public/pages/logs/settings/source_configuration_settings.tsx @@ -67,8 +67,6 @@ export const LogsSettingsPage = () => { logIndicesFormElement, logColumnsFormElement, nameFormElement, - tiebreakerFieldFormElement, - timestampFieldFormElement, } = useLogSourceConfigurationFormState(source?.configuration); const persistUpdates = useCallback(async () => { @@ -113,8 +111,6 @@ export const LogsSettingsPage = () => { isLoading={isLoading} isReadOnly={!isWriteable} indicesFormElement={logIndicesFormElement} - tiebreakerFieldFormElement={tiebreakerFieldFormElement} - timestampFieldFormElement={timestampFieldFormElement} /> diff --git a/x-pack/plugins/infra/public/pages/metrics/settings/fields_configuration_panel.tsx b/x-pack/plugins/infra/public/pages/metrics/settings/fields_configuration_panel.tsx deleted file mode 100644 index 1c6c627c08d0b..0000000000000 --- a/x-pack/plugins/infra/public/pages/metrics/settings/fields_configuration_panel.tsx +++ /dev/null @@ -1,274 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { - EuiDescribedFormGroup, - EuiCode, - EuiFieldText, - EuiForm, - EuiFormRow, - EuiSpacer, - EuiTitle, - EuiCallOut, - EuiLink, -} from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; -import React from 'react'; -import { InputFieldProps } from './input_fields'; - -interface FieldsConfigurationPanelProps { - containerFieldProps: InputFieldProps; - hostFieldProps: InputFieldProps; - isLoading: boolean; - readOnly: boolean; - podFieldProps: InputFieldProps; - timestampFieldProps: InputFieldProps; -} - -export const FieldsConfigurationPanel = ({ - containerFieldProps, - hostFieldProps, - isLoading, - readOnly, - podFieldProps, - timestampFieldProps, -}: FieldsConfigurationPanelProps) => { - const isHostValueDefault = hostFieldProps.value === 'host.name'; - const isContainerValueDefault = containerFieldProps.value === 'container.id'; - const isPodValueDefault = podFieldProps.value === 'kubernetes.pod.uid'; - const isTimestampValueDefault = timestampFieldProps.value === '@timestamp'; - return ( - - -

- -

-
- - -

- - - - ), - ecsLink: ( - - ECS - - ), - }} - /> -

-
- - - - - } - description={ - - } - > - @timestamp, - }} - /> - } - isInvalid={timestampFieldProps.isInvalid} - label={ - - } - > - - - - - - - } - description={ - - } - > - container.id, - }} - /> - } - isInvalid={containerFieldProps.isInvalid} - label={ - - } - > - - - - - - - } - description={ - - } - > - host.name, - }} - /> - } - isInvalid={hostFieldProps.isInvalid} - label={ - - } - > - - - - - - - } - description={ - - } - > - kubernetes.pod.uid, - }} - /> - } - isInvalid={podFieldProps.isInvalid} - label={ - - } - > - - - -
- ); -}; diff --git a/x-pack/plugins/infra/public/pages/metrics/settings/indices_configuration_form_state.ts b/x-pack/plugins/infra/public/pages/metrics/settings/indices_configuration_form_state.ts index ced87112e6e9a..359ada83b2ffa 100644 --- a/x-pack/plugins/infra/public/pages/metrics/settings/indices_configuration_form_state.ts +++ b/x-pack/plugins/infra/public/pages/metrics/settings/indices_configuration_form_state.ts @@ -16,11 +16,6 @@ interface FormState { name: string; description: string; metricAlias: string; - containerField: string; - hostField: string; - podField: string; - tiebreakerField: string; - timestampField: string; anomalyThreshold: number; } @@ -63,59 +58,7 @@ export const useIndicesConfigurationFormState = ({ }), [formState.metricAlias] ); - const containerFieldFieldProps = useMemo( - () => - createInputFieldProps({ - errors: validateInputFieldNotEmpty(formState.containerField), - name: `containerField`, - onChange: (containerField) => - setFormStateChanges((changes) => ({ ...changes, containerField })), - value: formState.containerField, - }), - [formState.containerField] - ); - const hostFieldFieldProps = useMemo( - () => - createInputFieldProps({ - errors: validateInputFieldNotEmpty(formState.hostField), - name: `hostField`, - onChange: (hostField) => setFormStateChanges((changes) => ({ ...changes, hostField })), - value: formState.hostField, - }), - [formState.hostField] - ); - const podFieldFieldProps = useMemo( - () => - createInputFieldProps({ - errors: validateInputFieldNotEmpty(formState.podField), - name: `podField`, - onChange: (podField) => setFormStateChanges((changes) => ({ ...changes, podField })), - value: formState.podField, - }), - [formState.podField] - ); - const tiebreakerFieldFieldProps = useMemo( - () => - createInputFieldProps({ - errors: validateInputFieldNotEmpty(formState.tiebreakerField), - name: `tiebreakerField`, - onChange: (tiebreakerField) => - setFormStateChanges((changes) => ({ ...changes, tiebreakerField })), - value: formState.tiebreakerField, - }), - [formState.tiebreakerField] - ); - const timestampFieldFieldProps = useMemo( - () => - createInputFieldProps({ - errors: validateInputFieldNotEmpty(formState.timestampField), - name: `timestampField`, - onChange: (timestampField) => - setFormStateChanges((changes) => ({ ...changes, timestampField })), - value: formState.timestampField, - }), - [formState.timestampField] - ); + const anomalyThresholdFieldProps = useMemo( () => createInputRangeFieldProps({ @@ -132,23 +75,9 @@ export const useIndicesConfigurationFormState = ({ () => ({ name: nameFieldProps, metricAlias: metricAliasFieldProps, - containerField: containerFieldFieldProps, - hostField: hostFieldFieldProps, - podField: podFieldFieldProps, - tiebreakerField: tiebreakerFieldFieldProps, - timestampField: timestampFieldFieldProps, anomalyThreshold: anomalyThresholdFieldProps, }), - [ - nameFieldProps, - metricAliasFieldProps, - containerFieldFieldProps, - hostFieldFieldProps, - podFieldFieldProps, - tiebreakerFieldFieldProps, - timestampFieldFieldProps, - anomalyThresholdFieldProps, - ] + [nameFieldProps, metricAliasFieldProps, anomalyThresholdFieldProps] ); const errors = useMemo( @@ -179,10 +108,5 @@ const defaultFormState: FormState = { name: '', description: '', metricAlias: '', - containerField: '', - hostField: '', - podField: '', - tiebreakerField: '', - timestampField: '', anomalyThreshold: 0, }; diff --git a/x-pack/plugins/infra/public/pages/metrics/settings/source_configuration_form_state.tsx b/x-pack/plugins/infra/public/pages/metrics/settings/source_configuration_form_state.tsx index 909bf294e4098..d97d66cd5c05d 100644 --- a/x-pack/plugins/infra/public/pages/metrics/settings/source_configuration_form_state.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/settings/source_configuration_form_state.tsx @@ -20,11 +20,6 @@ export const useSourceConfigurationFormState = ( name: configuration.name, description: configuration.description, metricAlias: configuration.metricAlias, - containerField: configuration.fields.container, - hostField: configuration.fields.host, - podField: configuration.fields.pod, - tiebreakerField: configuration.fields.tiebreaker, - timestampField: configuration.fields.timestamp, anomalyThreshold: configuration.anomalyThreshold, } : undefined, @@ -56,13 +51,6 @@ export const useSourceConfigurationFormState = ( name: indicesConfigurationFormState.formState.name, description: indicesConfigurationFormState.formState.description, metricAlias: indicesConfigurationFormState.formState.metricAlias, - fields: { - container: indicesConfigurationFormState.formState.containerField, - host: indicesConfigurationFormState.formState.hostField, - pod: indicesConfigurationFormState.formState.podField, - tiebreaker: indicesConfigurationFormState.formState.tiebreakerField, - timestamp: indicesConfigurationFormState.formState.timestampField, - }, anomalyThreshold: indicesConfigurationFormState.formState.anomalyThreshold, }), [indicesConfigurationFormState.formState] @@ -73,13 +61,6 @@ export const useSourceConfigurationFormState = ( name: indicesConfigurationFormState.formStateChanges.name, description: indicesConfigurationFormState.formStateChanges.description, metricAlias: indicesConfigurationFormState.formStateChanges.metricAlias, - fields: { - container: indicesConfigurationFormState.formStateChanges.containerField, - host: indicesConfigurationFormState.formStateChanges.hostField, - pod: indicesConfigurationFormState.formStateChanges.podField, - tiebreaker: indicesConfigurationFormState.formStateChanges.tiebreakerField, - timestamp: indicesConfigurationFormState.formStateChanges.timestampField, - }, anomalyThreshold: indicesConfigurationFormState.formStateChanges.anomalyThreshold, }), [indicesConfigurationFormState.formStateChanges] diff --git a/x-pack/plugins/infra/public/pages/metrics/settings/source_configuration_settings.tsx b/x-pack/plugins/infra/public/pages/metrics/settings/source_configuration_settings.tsx index 0adf4ed6b5b45..f10bd7cf9e38a 100644 --- a/x-pack/plugins/infra/public/pages/metrics/settings/source_configuration_settings.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/settings/source_configuration_settings.tsx @@ -20,7 +20,6 @@ import { SourceLoadingPage } from '../../../components/source_loading_page'; import { Source } from '../../../containers/metrics_source'; import { useInfraMLCapabilitiesContext } from '../../../containers/ml/infra_ml_capabilities'; import { Prompt } from '../../../utils/navigation_warning_prompt'; -import { FieldsConfigurationPanel } from './fields_configuration_panel'; import { IndicesConfigurationPanel } from './indices_configuration_panel'; import { MLConfigurationPanel } from './ml_configuration_panel'; import { NameConfigurationPanel } from './name_configuration_panel'; @@ -123,17 +122,6 @@ export const SourceConfigurationSettings = ({ /> - - - - {hasInfraMLCapabilities && ( <> diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index b9313ccfa46a4..781ef74a872eb 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -13694,20 +13694,9 @@ "xpack.infra.sourceConfiguration.anomalyThresholdLabel": "最低重要度スコア", "xpack.infra.sourceConfiguration.anomalyThresholdTitle": "異常重要度しきい値", "xpack.infra.sourceConfiguration.applySettingsButtonLabel": "適用", - "xpack.infra.sourceConfiguration.containerFieldDescription": "Docker コンテナーの識別に使用されるフィールドです", - "xpack.infra.sourceConfiguration.containerFieldLabel": "コンテナー ID", - "xpack.infra.sourceConfiguration.containerFieldRecommendedValue": "推奨値は {defaultValue} です", - "xpack.infra.sourceConfiguration.deprecationMessage": "これらのフィールドの構成は廃止予定です。8.0.0で削除されます。このアプリケーションは{ecsLink}で動作するように設計されています。{documentationLink}を使用するには、インデックスを調整してください。", - "xpack.infra.sourceConfiguration.deprecationNotice": "廃止通知", "xpack.infra.sourceConfiguration.discardSettingsButtonLabel": "破棄", - "xpack.infra.sourceConfiguration.documentedFields": "文書化されたフィールド", "xpack.infra.sourceConfiguration.fieldEmptyErrorMessage": "このフィールドは未入力のままにできません。", "xpack.infra.sourceConfiguration.fieldLogColumnTitle": "フィールド", - "xpack.infra.sourceConfiguration.fieldsSectionTitle": "フィールド", - "xpack.infra.sourceConfiguration.hostFieldDescription": "推奨値は {defaultValue} です", - "xpack.infra.sourceConfiguration.hostFieldLabel": "ホスト名", - "xpack.infra.sourceConfiguration.hostNameFieldDescription": "ホストの識別に使用されるフィールドです", - "xpack.infra.sourceConfiguration.hostNameFieldLabel": "ホスト名", "xpack.infra.sourceConfiguration.indicesSectionTitle": "インデックス", "xpack.infra.sourceConfiguration.logColumnsSectionTitle": "ログ列", "xpack.infra.sourceConfiguration.logIndicesDescription": "ログデータを含む一致するインデックスのインデックスパターンです", @@ -13725,17 +13714,8 @@ "xpack.infra.sourceConfiguration.nameSectionTitle": "名前", "xpack.infra.sourceConfiguration.noLogColumnsDescription": "上のボタンでこのリストに列を追加します。", "xpack.infra.sourceConfiguration.noLogColumnsTitle": "列がありません", - "xpack.infra.sourceConfiguration.podFieldDescription": "Kubernetes ポッドの識別に使用されるフィールドです", - "xpack.infra.sourceConfiguration.podFieldLabel": "ポッド ID", - "xpack.infra.sourceConfiguration.podFieldRecommendedValue": "推奨値は {defaultValue} です", "xpack.infra.sourceConfiguration.removeLogColumnButtonLabel": "{columnDescription} 列を削除", "xpack.infra.sourceConfiguration.systemColumnBadgeLabel": "システム", - "xpack.infra.sourceConfiguration.tiebreakerFieldDescription": "同じタイムスタンプの 2 つのエントリーを識別するのに使用されるフィールドです", - "xpack.infra.sourceConfiguration.tiebreakerFieldLabel": "タイブレーカー", - "xpack.infra.sourceConfiguration.tiebreakerFieldRecommendedValue": "推奨値は {defaultValue} です", - "xpack.infra.sourceConfiguration.timestampFieldDescription": "ログエントリーの並べ替えに使用されるタイムスタンプです", - "xpack.infra.sourceConfiguration.timestampFieldLabel": "タイムスタンプ", - "xpack.infra.sourceConfiguration.timestampFieldRecommendedValue": "推奨値は {defaultValue} です", "xpack.infra.sourceConfiguration.timestampLogColumnDescription": "このシステムフィールドは、{timestampSetting} フィールド設定から判断されたログエントリーの時刻を表示します。", "xpack.infra.sourceConfiguration.unsavedFormPrompt": "終了してよろしいですか?変更内容は失われます", "xpack.infra.sourceErrorPage.failedToLoadDataSourcesMessage": "データソースの読み込みに失敗しました。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index b9739d4bd991e..92646cc1f9cd9 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -13882,20 +13882,9 @@ "xpack.infra.sourceConfiguration.anomalyThresholdLabel": "最低严重性分数", "xpack.infra.sourceConfiguration.anomalyThresholdTitle": "异常严重性阈值", "xpack.infra.sourceConfiguration.applySettingsButtonLabel": "应用", - "xpack.infra.sourceConfiguration.containerFieldDescription": "用于标识 Docker 容器的字段", - "xpack.infra.sourceConfiguration.containerFieldLabel": "容器 ID", - "xpack.infra.sourceConfiguration.containerFieldRecommendedValue": "推荐值为 {defaultValue}", - "xpack.infra.sourceConfiguration.deprecationMessage": "有关这些字段的配置已过时,将在 8.0.0 中移除。此应用程序专用于 {ecsLink},您应调整索引以使用{documentationLink}。", - "xpack.infra.sourceConfiguration.deprecationNotice": "过时通知", "xpack.infra.sourceConfiguration.discardSettingsButtonLabel": "丢弃", - "xpack.infra.sourceConfiguration.documentedFields": "已记录字段", "xpack.infra.sourceConfiguration.fieldEmptyErrorMessage": "字段不得为空。", "xpack.infra.sourceConfiguration.fieldLogColumnTitle": "字段", - "xpack.infra.sourceConfiguration.fieldsSectionTitle": "字段", - "xpack.infra.sourceConfiguration.hostFieldDescription": "推荐值为 {defaultValue}", - "xpack.infra.sourceConfiguration.hostFieldLabel": "主机名", - "xpack.infra.sourceConfiguration.hostNameFieldDescription": "用于标识主机的字段", - "xpack.infra.sourceConfiguration.hostNameFieldLabel": "主机名", "xpack.infra.sourceConfiguration.indicesSectionTitle": "索引", "xpack.infra.sourceConfiguration.logColumnsSectionTitle": "日志列", "xpack.infra.sourceConfiguration.logIndicesDescription": "用于匹配包含日志数据的索引的索引模式", @@ -13913,17 +13902,8 @@ "xpack.infra.sourceConfiguration.nameSectionTitle": "名称", "xpack.infra.sourceConfiguration.noLogColumnsDescription": "使用上面的按钮将列添加到此列表。", "xpack.infra.sourceConfiguration.noLogColumnsTitle": "无列", - "xpack.infra.sourceConfiguration.podFieldDescription": "用于标识 Kubernetes Pod 的字段", - "xpack.infra.sourceConfiguration.podFieldLabel": "Pod ID", - "xpack.infra.sourceConfiguration.podFieldRecommendedValue": "推荐值为 {defaultValue}", "xpack.infra.sourceConfiguration.removeLogColumnButtonLabel": "删除“{columnDescription}”列", "xpack.infra.sourceConfiguration.systemColumnBadgeLabel": "系统", - "xpack.infra.sourceConfiguration.tiebreakerFieldDescription": "用于时间戳相同的两个条目间决胜的字段", - "xpack.infra.sourceConfiguration.tiebreakerFieldLabel": "决胜属性", - "xpack.infra.sourceConfiguration.tiebreakerFieldRecommendedValue": "推荐值为 {defaultValue}", - "xpack.infra.sourceConfiguration.timestampFieldDescription": "用于排序日志条目的时间戳", - "xpack.infra.sourceConfiguration.timestampFieldLabel": "时间戳", - "xpack.infra.sourceConfiguration.timestampFieldRecommendedValue": "推荐值为 {defaultValue}", "xpack.infra.sourceConfiguration.timestampLogColumnDescription": "此系统字段显示 {timestampSetting} 字段设置所确定的日志条目时间。", "xpack.infra.sourceConfiguration.unsavedFormPrompt": "是否确定要离开?更改将丢失", "xpack.infra.sourceErrorPage.failedToLoadDataSourcesMessage": "无法加载数据源。", From 26d42523c57dfb695f8de1820e96c3e6084fa955 Mon Sep 17 00:00:00 2001 From: Gloria Hornero Date: Tue, 19 Oct 2021 22:53:18 +0200 Subject: [PATCH 092/204] fixes style bug (#114976) --- .../public/timelines/components/timeline/tabs_content/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx index 1041d5fbc7d7d..e38e380292260 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx @@ -201,7 +201,7 @@ const CountBadge = styled(EuiBadge)` `; const StyledEuiTab = styled(EuiTab)` - > span { + .euiTab__content { display: flex; flex-direction: row; white-space: pre; From db53a79cc4de33b85fbdae1d2bfa51c655477b4a Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Tue, 19 Oct 2021 16:04:02 -0500 Subject: [PATCH 093/204] Waterfall layout and expansion fixes (#114889) Fix according toggling behavior on trace waterfall. Previously clicking on any item would collapse the whole waterfall, now it just collapses the one you clicked on. Truncate spans so ones with very long names don't overflow. Make the left margin relative to the max level of depth so waterfalls with deep trees don't overflow. --- .../Waterfall/accordion_waterfall.tsx | 10 +- .../waterfall_container/Waterfall/index.tsx | 25 +- .../Waterfall/waterfall_item.tsx | 8 +- .../WaterfallContainer.stories.tsx | 56 - .../waterfallContainer.stories.data.ts | 2269 ------- .../waterfall_container.stories.data.ts | 5865 +++++++++++++++++ .../waterfall_container.stories.tsx | 88 + .../waterfall_container.test.tsx | 42 + 8 files changed, 6019 insertions(+), 2344 deletions(-) delete mode 100644 x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/WaterfallContainer.stories.tsx delete mode 100644 x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfallContainer.stories.data.ts create mode 100644 x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall_container.stories.data.ts create mode 100644 x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall_container.stories.tsx create mode 100644 x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall_container.test.tsx diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/accordion_waterfall.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/accordion_waterfall.tsx index e4a851b890a7c..15883e7905142 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/accordion_waterfall.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/accordion_waterfall.tsx @@ -7,7 +7,7 @@ import { EuiAccordion, EuiAccordionProps } from '@elastic/eui'; import { isEmpty } from 'lodash'; -import React, { useState } from 'react'; +import React, { Dispatch, SetStateAction, useState } from 'react'; import { euiStyled } from '../../../../../../../../../../src/plugins/kibana_react/common'; import { Margins } from '../../../../../shared/charts/Timeline'; import { WaterfallItem } from './waterfall_item'; @@ -22,8 +22,8 @@ interface AccordionWaterfallProps { level: number; duration: IWaterfall['duration']; waterfallItemId?: string; + setMaxLevel: Dispatch>; waterfall: IWaterfall; - onToggleEntryTransaction?: () => void; timelineMargins: Margins; onClickWaterfallItem: (item: IWaterfallSpanOrTransaction) => void; } @@ -97,12 +97,13 @@ export function AccordionWaterfall(props: AccordionWaterfallProps) { duration, waterfall, waterfallItemId, + setMaxLevel, timelineMargins, onClickWaterfallItem, - onToggleEntryTransaction, } = props; const nextLevel = level + 1; + setMaxLevel(nextLevel); const children = waterfall.childrenByParentId[item.id] || []; const errorCount = waterfall.getErrorCount(item.id); @@ -139,9 +140,6 @@ export function AccordionWaterfall(props: AccordionWaterfallProps) { forceState={isOpen ? 'open' : 'closed'} onToggle={() => { setIsOpen((isCurrentOpen) => !isCurrentOpen); - if (onToggleEntryTransaction) { - onToggleEntryTransaction(); - } }} > {children.map((child) => ( diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/index.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/index.tsx index 3932a02c9d974..5b4bf99f7dae6 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/index.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/index.tsx @@ -29,13 +29,6 @@ const Container = euiStyled.div` overflow: hidden; `; -const TIMELINE_MARGINS = { - top: 40, - left: 100, - right: 50, - bottom: 0, -}; - const toggleFlyout = ({ history, item, @@ -72,6 +65,16 @@ export function Waterfall({ waterfall, waterfallItemId }: Props) { const agentMarks = getAgentMarks(waterfall.entryWaterfallTransaction?.doc); const errorMarks = getErrorMarks(waterfall.errorItems); + // Calculate the left margin relative to the deepest level, or 100px, whichever + // is more. + const [maxLevel, setMaxLevel] = useState(0); + const timelineMargins = { + top: 40, + left: Math.max(100, maxLevel * 10), + right: 50, + bottom: 0, + }; + return ( @@ -99,7 +102,7 @@ export function Waterfall({ waterfall, waterfallItemId }: Props) { marks={[...agentMarks, ...errorMarks]} xMax={duration} height={waterfallHeight} - margins={TIMELINE_MARGINS} + margins={timelineMargins} />
@@ -110,16 +113,14 @@ export function Waterfall({ waterfall, waterfallItemId }: Props) { isOpen={isAccordionOpen} item={waterfall.entryWaterfallTransaction} level={0} + setMaxLevel={setMaxLevel} waterfallItemId={waterfallItemId} duration={duration} waterfall={waterfall} - timelineMargins={TIMELINE_MARGINS} + timelineMargins={timelineMargins} onClickWaterfallItem={(item: IWaterfallItem) => toggleFlyout({ history, item }) } - onToggleEntryTransaction={() => - setIsAccordionOpen((isOpen) => !isOpen) - } /> )} diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/waterfall_item.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/waterfall_item.tsx index 4001a0624a809..caa0cac3acef8 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/waterfall_item.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/waterfall_item.tsx @@ -17,6 +17,7 @@ import { } from '../../../../../../../common/elasticsearch_fieldnames'; import { asDuration } from '../../../../../../../common/utils/formatters'; import { Margins } from '../../../../../shared/charts/Timeline'; +import { TruncateWithTooltip } from '../../../../../shared/truncate_with_tooltip'; import { SyncBadge } from './sync_badge'; import { IWaterfallSpanOrTransaction } from './waterfall_helpers/waterfall_helpers'; import { FailureBadge } from './failure_badge'; @@ -67,6 +68,7 @@ const ItemText = euiStyled.span` display: flex; align-items: center; height: ${({ theme }) => theme.eui.euiSizeL}; + max-width: 100%; /* add margin to all direct descendants */ & > * { @@ -160,7 +162,11 @@ function NameLabel({ item }: { item: IWaterfallSpanOrTransaction }) { : ''; name = `${item.doc.span.composite.count}${compositePrefix} ${name}`; } - return {name}; + return ( + + + + ); case 'transaction': return ( diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/WaterfallContainer.stories.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/WaterfallContainer.stories.tsx deleted file mode 100644 index a03b7b29f9666..0000000000000 --- a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/WaterfallContainer.stories.tsx +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { ComponentType } from 'react'; -import { MemoryRouter } from 'react-router-dom'; -import { MockApmPluginContextWrapper } from '../../../../../context/apm_plugin/mock_apm_plugin_context'; -import { WaterfallContainer } from './index'; -import { getWaterfall } from './Waterfall/waterfall_helpers/waterfall_helpers'; -import { - inferredSpans, - simpleTrace, - traceChildStartBeforeParent, - traceWithErrors, - urlParams, -} from './waterfallContainer.stories.data'; - -export default { - title: 'app/TransactionDetails/Waterfall', - component: WaterfallContainer, - decorators: [ - (Story: ComponentType) => ( - - - - - - ), - ], -}; - -export function Example() { - const waterfall = getWaterfall(simpleTrace, '975c8d5bfd1dd20b'); - return ; -} - -export function WithErrors() { - const waterfall = getWaterfall(traceWithErrors, '975c8d5bfd1dd20b'); - return ; -} - -export function ChildStartsBeforeParent() { - const waterfall = getWaterfall( - traceChildStartBeforeParent, - '975c8d5bfd1dd20b' - ); - return ; -} - -export function InferredSpans() { - const waterfall = getWaterfall(inferredSpans, 'f2387d37260d00bd'); - return ; -} diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfallContainer.stories.data.ts b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfallContainer.stories.data.ts deleted file mode 100644 index 60285c835bbf3..0000000000000 --- a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfallContainer.stories.data.ts +++ /dev/null @@ -1,2269 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { Location } from 'history'; -import type { ApmUrlParams } from '../../../../../context/url_params_context/types'; -import { APIReturnType } from '../../../../../services/rest/createCallApmApi'; - -export const location = { - pathname: '/services/opbeans-go/transactions/view', - search: - '?rangeFrom=now-24h&rangeTo=now&refreshPaused=true&refreshInterval=0&kuery=service.name%253A%2520%2522opbeans-java%2522%2520or%2520service.name%2520%253A%2520%2522opbeans-go%2522&traceId=513d33fafe99bbe6134749310c9b5322&transactionId=975c8d5bfd1dd20b&transactionName=GET%20%2Fapi%2Forders&transactionType=request', - hash: '', -} as Location; - -type TraceAPIResponse = APIReturnType<'GET /internal/apm/traces/{traceId}'>; - -export const urlParams = { - start: '2020-03-22T15:16:38.742Z', - end: '2020-03-23T15:16:38.742Z', - rangeFrom: 'now-24h', - rangeTo: 'now', - refreshPaused: true, - refreshInterval: 0, - page: 0, - transactionId: '975c8d5bfd1dd20b', - traceId: '513d33fafe99bbe6134749310c9b5322', - transactionName: 'GET /api/orders', - transactionType: 'request', - processorEvent: 'transaction', - serviceName: 'opbeans-go', -} as ApmUrlParams; - -export const simpleTrace = { - traceDocs: [ - { - container: { - id: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', - }, - process: { - pid: 6, - title: '/usr/lib/jvm/java-10-openjdk-amd64/bin/java', - ppid: 1, - }, - agent: { - name: 'java', - ephemeral_id: '99ce8403-5875-4945-b074-d37dc10563eb', - version: '1.14.1-SNAPSHOT', - }, - internal: { - sampler: { - value: 46, - }, - }, - source: { - ip: '172.19.0.13', - }, - processor: { - name: 'transaction', - event: 'transaction', - }, - url: { - path: '/api/orders', - scheme: 'http', - port: 3000, - domain: '172.19.0.9', - full: 'http://172.19.0.9:3000/api/orders', - }, - observer: { - hostname: 'f37f48d8b60b', - id: 'd8522e1f-be8e-43c2-b290-ac6b6c0f171e', - ephemeral_id: '6ed88f14-170e-478d-a4f5-ea5e7f4b16b9', - type: 'apm-server', - version: '8.0.0', - version_major: 8, - }, - trace: { - id: '513d33fafe99bbe6134749310c9b5322', - }, - '@timestamp': '2020-03-23T15:04:28.785Z', - ecs: { - version: '1.4.0', - }, - service: { - node: { - name: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', - }, - environment: 'production', - name: 'opbeans-java', - runtime: { - name: 'Java', - version: '10.0.2', - }, - language: { - name: 'Java', - version: '10.0.2', - }, - version: 'None', - }, - host: { - hostname: '4cf84d094553', - os: { - platform: 'Linux', - }, - ip: '172.19.0.9', - name: '4cf84d094553', - architecture: 'amd64', - }, - http: { - request: { - headers: { - Accept: ['*/*'], - 'User-Agent': ['Python/3.7 aiohttp/3.3.2'], - Host: ['172.19.0.9:3000'], - 'Accept-Encoding': ['gzip, deflate'], - }, - method: 'get', - socket: { - encrypted: false, - remote_address: '172.19.0.13', - }, - body: { - original: '[REDACTED]', - }, - }, - response: { - headers: { - 'Transfer-Encoding': ['chunked'], - Date: ['Mon, 23 Mar 2020 15:04:28 GMT'], - 'Content-Type': ['application/json;charset=ISO-8859-1'], - }, - status_code: 200, - finished: true, - headers_sent: true, - }, - version: '1.1', - }, - client: { - ip: '172.19.0.13', - }, - transaction: { - duration: { - us: 18842, - }, - result: 'HTTP 2xx', - name: 'DispatcherServlet#doGet', - id: '49809ad3c26adf74', - span_count: { - dropped: 0, - started: 1, - }, - type: 'request', - sampled: true, - }, - user_agent: { - original: 'Python/3.7 aiohttp/3.3.2', - name: 'Other', - device: { - name: 'Other', - }, - }, - timestamp: { - us: 1584975868785000, - }, - }, - { - parent: { - id: 'fc107f7b556eb49b', - }, - agent: { - name: 'go', - version: '1.7.2', - }, - processor: { - name: 'transaction', - event: 'transaction', - }, - url: { - path: '/api/orders', - scheme: 'http', - port: 3000, - domain: 'opbeans-go', - full: 'http://opbeans-go:3000/api/orders', - }, - trace: { - id: '513d33fafe99bbe6134749310c9b5322', - }, - '@timestamp': '2020-03-23T15:04:28.787Z', - service: { - node: { - name: 'e948a08b8f5efe99b5da01f50da48c7d8aee3bbf4701f3da85ebe760c2ffef29', - }, - environment: 'production', - framework: { - name: 'gin', - version: 'v1.4.0', - }, - name: 'opbeans-go', - runtime: { - name: 'gc', - version: 'go1.14.1', - }, - language: { - name: 'go', - version: 'go1.14.1', - }, - version: 'None', - }, - transaction: { - duration: { - us: 16597, - }, - result: 'HTTP 2xx', - name: 'GET /api/orders', - id: '975c8d5bfd1dd20b', - span_count: { - dropped: 0, - started: 1, - }, - type: 'request', - sampled: true, - }, - timestamp: { - us: 1584975868787052, - }, - }, - { - parent: { - id: 'daae24d83c269918', - }, - agent: { - name: 'python', - version: '5.5.2', - }, - trace: { - id: '513d33fafe99bbe6134749310c9b5322', - }, - timestamp: { - us: 1584975868788603, - }, - processor: { - name: 'transaction', - event: 'transaction', - }, - url: { - path: '/api/orders', - scheme: 'http', - port: 3000, - domain: 'opbeans-go', - full: 'http://opbeans-go:3000/api/orders', - }, - '@timestamp': '2020-03-23T15:04:28.788Z', - service: { - node: { - name: 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51', - }, - environment: 'production', - framework: { - name: 'django', - version: '2.1.13', - }, - name: 'opbeans-python', - runtime: { - name: 'CPython', - version: '3.6.10', - }, - language: { - name: 'python', - version: '3.6.10', - }, - version: 'None', - }, - transaction: { - result: 'HTTP 2xx', - duration: { - us: 14648, - }, - name: 'GET opbeans.views.orders', - span_count: { - dropped: 0, - started: 1, - }, - id: '6fb0ff7365b87298', - type: 'request', - sampled: true, - }, - }, - { - container: { - id: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', - }, - parent: { - id: '49809ad3c26adf74', - }, - process: { - pid: 6, - title: '/usr/lib/jvm/java-10-openjdk-amd64/bin/java', - ppid: 1, - }, - agent: { - name: 'java', - ephemeral_id: '99ce8403-5875-4945-b074-d37dc10563eb', - version: '1.14.1-SNAPSHOT', - }, - internal: { - sampler: { - value: 44, - }, - }, - destination: { - address: 'opbeans-go', - port: 3000, - }, - processor: { - name: 'transaction', - event: 'span', - }, - observer: { - hostname: 'f37f48d8b60b', - id: 'd8522e1f-be8e-43c2-b290-ac6b6c0f171e', - type: 'apm-server', - ephemeral_id: '6ed88f14-170e-478d-a4f5-ea5e7f4b16b9', - version: '8.0.0', - version_major: 8, - }, - trace: { - id: '513d33fafe99bbe6134749310c9b5322', - }, - '@timestamp': '2020-03-23T15:04:28.785Z', - ecs: { - version: '1.4.0', - }, - service: { - node: { - name: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', - }, - environment: 'production', - name: 'opbeans-java', - runtime: { - name: 'Java', - version: '10.0.2', - }, - language: { - name: 'Java', - version: '10.0.2', - }, - version: 'None', - }, - host: { - hostname: '4cf84d094553', - os: { - platform: 'Linux', - }, - ip: '172.19.0.9', - name: '4cf84d094553', - architecture: 'amd64', - }, - connection: { - hash: "{service.environment:'production'}/{service.name:'opbeans-java'}/{span.subtype:'http'}/{destination.address:'opbeans-go'}/{span.type:'external'}", - }, - transaction: { - id: '49809ad3c26adf74', - }, - timestamp: { - us: 1584975868785273, - }, - span: { - duration: { - us: 17530, - }, - subtype: 'http', - name: 'GET opbeans-go', - destination: { - service: { - resource: 'opbeans-go:3000', - name: 'http://opbeans-go:3000', - type: 'external', - }, - }, - http: { - response: { - status_code: 200, - }, - url: { - original: 'http://opbeans-go:3000/api/orders', - }, - }, - id: 'fc107f7b556eb49b', - type: 'external', - }, - }, - { - parent: { - id: '975c8d5bfd1dd20b', - }, - agent: { - name: 'go', - version: '1.7.2', - }, - processor: { - name: 'transaction', - event: 'span', - }, - trace: { - id: '513d33fafe99bbe6134749310c9b5322', - }, - '@timestamp': '2020-03-23T15:04:28.787Z', - service: { - node: { - name: 'e948a08b8f5efe99b5da01f50da48c7d8aee3bbf4701f3da85ebe760c2ffef29', - }, - environment: 'production', - name: 'opbeans-go', - runtime: { - name: 'gc', - version: 'go1.14.1', - }, - language: { - name: 'go', - version: 'go1.14.1', - }, - version: 'None', - }, - transaction: { - id: '975c8d5bfd1dd20b', - }, - timestamp: { - us: 1584975868787174, - }, - span: { - duration: { - us: 16250, - }, - subtype: 'http', - destination: { - service: { - resource: 'opbeans-python:3000', - name: 'http://opbeans-python:3000', - type: 'external', - }, - }, - name: 'GET opbeans-python:3000', - http: { - response: { - status_code: 200, - }, - url: { - original: 'http://opbeans-python:3000/api/orders', - }, - }, - id: 'daae24d83c269918', - type: 'external', - }, - }, - { - container: { - id: 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51', - }, - parent: { - id: '6fb0ff7365b87298', - }, - agent: { - name: 'python', - version: '5.5.2', - }, - processor: { - name: 'transaction', - event: 'span', - }, - trace: { - id: '513d33fafe99bbe6134749310c9b5322', - }, - '@timestamp': '2020-03-23T15:04:28.790Z', - service: { - node: { - name: 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51', - }, - environment: 'production', - framework: { - name: 'django', - version: '2.1.13', - }, - name: 'opbeans-python', - runtime: { - name: 'CPython', - version: '3.6.10', - }, - language: { - name: 'python', - version: '3.6.10', - }, - version: 'None', - }, - transaction: { - id: '6fb0ff7365b87298', - }, - timestamp: { - us: 1584975868790080, - }, - span: { - duration: { - us: 2519, - }, - subtype: 'postgresql', - name: 'SELECT FROM opbeans_order', - destination: { - service: { - resource: 'postgresql', - name: 'postgresql', - type: 'db', - }, - }, - action: 'query', - id: 'c9407abb4d08ead1', - type: 'db', - sync: true, - db: { - statement: - 'SELECT "opbeans_order"."id", "opbeans_order"."customer_id", "opbeans_customer"."full_name", "opbeans_order"."created_at" FROM "opbeans_order" INNER JOIN "opbeans_customer" ON ("opbeans_order"."customer_id" = "opbeans_customer"."id") LIMIT 1000', - type: 'sql', - }, - }, - }, - ], - exceedsMax: false, - errorDocs: [], -} as TraceAPIResponse; - -export const traceWithErrors = { - traceDocs: [ - { - container: { - id: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', - }, - process: { - pid: 6, - title: '/usr/lib/jvm/java-10-openjdk-amd64/bin/java', - ppid: 1, - }, - agent: { - name: 'java', - ephemeral_id: '99ce8403-5875-4945-b074-d37dc10563eb', - version: '1.14.1-SNAPSHOT', - }, - internal: { - sampler: { - value: 46, - }, - }, - source: { - ip: '172.19.0.13', - }, - processor: { - name: 'transaction', - event: 'transaction', - }, - url: { - path: '/api/orders', - scheme: 'http', - port: 3000, - domain: '172.19.0.9', - full: 'http://172.19.0.9:3000/api/orders', - }, - observer: { - hostname: 'f37f48d8b60b', - id: 'd8522e1f-be8e-43c2-b290-ac6b6c0f171e', - ephemeral_id: '6ed88f14-170e-478d-a4f5-ea5e7f4b16b9', - type: 'apm-server', - version: '8.0.0', - version_major: 8, - }, - trace: { - id: '513d33fafe99bbe6134749310c9b5322', - }, - '@timestamp': '2020-03-23T15:04:28.785Z', - ecs: { - version: '1.4.0', - }, - service: { - node: { - name: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', - }, - environment: 'production', - name: 'opbeans-java', - runtime: { - name: 'Java', - version: '10.0.2', - }, - language: { - name: 'Java', - version: '10.0.2', - }, - version: 'None', - }, - host: { - hostname: '4cf84d094553', - os: { - platform: 'Linux', - }, - ip: '172.19.0.9', - name: '4cf84d094553', - architecture: 'amd64', - }, - http: { - request: { - headers: { - Accept: ['*/*'], - 'User-Agent': ['Python/3.7 aiohttp/3.3.2'], - Host: ['172.19.0.9:3000'], - 'Accept-Encoding': ['gzip, deflate'], - }, - method: 'get', - socket: { - encrypted: false, - remote_address: '172.19.0.13', - }, - body: { - original: '[REDACTED]', - }, - }, - response: { - headers: { - 'Transfer-Encoding': ['chunked'], - Date: ['Mon, 23 Mar 2020 15:04:28 GMT'], - 'Content-Type': ['application/json;charset=ISO-8859-1'], - }, - status_code: 200, - finished: true, - headers_sent: true, - }, - version: '1.1', - }, - client: { - ip: '172.19.0.13', - }, - transaction: { - duration: { - us: 18842, - }, - result: 'HTTP 2xx', - name: 'DispatcherServlet#doGet', - id: '49809ad3c26adf74', - span_count: { - dropped: 0, - started: 1, - }, - type: 'request', - sampled: true, - }, - user_agent: { - original: 'Python/3.7 aiohttp/3.3.2', - name: 'Other', - device: { - name: 'Other', - }, - }, - timestamp: { - us: 1584975868785000, - }, - }, - { - parent: { - id: 'fc107f7b556eb49b', - }, - agent: { - name: 'go', - version: '1.7.2', - }, - processor: { - name: 'transaction', - event: 'transaction', - }, - url: { - path: '/api/orders', - scheme: 'http', - port: 3000, - domain: 'opbeans-go', - full: 'http://opbeans-go:3000/api/orders', - }, - trace: { - id: '513d33fafe99bbe6134749310c9b5322', - }, - '@timestamp': '2020-03-23T15:04:28.787Z', - service: { - node: { - name: 'e948a08b8f5efe99b5da01f50da48c7d8aee3bbf4701f3da85ebe760c2ffef29', - }, - environment: 'production', - framework: { - name: 'gin', - version: 'v1.4.0', - }, - name: 'opbeans-go', - runtime: { - name: 'gc', - version: 'go1.14.1', - }, - language: { - name: 'go', - version: 'go1.14.1', - }, - version: 'None', - }, - transaction: { - duration: { - us: 16597, - }, - result: 'HTTP 2xx', - name: 'GET /api/orders', - id: '975c8d5bfd1dd20b', - span_count: { - dropped: 0, - started: 1, - }, - type: 'request', - sampled: true, - }, - timestamp: { - us: 1584975868787052, - }, - }, - { - parent: { - id: 'daae24d83c269918', - }, - agent: { - name: 'python', - version: '5.5.2', - }, - trace: { - id: '513d33fafe99bbe6134749310c9b5322', - }, - timestamp: { - us: 1584975868788603, - }, - processor: { - name: 'transaction', - event: 'transaction', - }, - url: { - path: '/api/orders', - scheme: 'http', - port: 3000, - domain: 'opbeans-go', - full: 'http://opbeans-go:3000/api/orders', - }, - '@timestamp': '2020-03-23T15:04:28.788Z', - service: { - node: { - name: 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51', - }, - environment: 'production', - framework: { - name: 'django', - version: '2.1.13', - }, - name: 'opbeans-python', - runtime: { - name: 'CPython', - version: '3.6.10', - }, - language: { - name: 'python', - version: '3.6.10', - }, - version: 'None', - }, - transaction: { - result: 'HTTP 2xx', - duration: { - us: 14648, - }, - name: 'GET opbeans.views.orders', - span_count: { - dropped: 0, - started: 1, - }, - id: '6fb0ff7365b87298', - type: 'request', - sampled: true, - }, - }, - { - container: { - id: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', - }, - parent: { - id: '49809ad3c26adf74', - }, - process: { - pid: 6, - title: '/usr/lib/jvm/java-10-openjdk-amd64/bin/java', - ppid: 1, - }, - agent: { - name: 'java', - ephemeral_id: '99ce8403-5875-4945-b074-d37dc10563eb', - version: '1.14.1-SNAPSHOT', - }, - internal: { - sampler: { - value: 44, - }, - }, - destination: { - address: 'opbeans-go', - port: 3000, - }, - processor: { - name: 'transaction', - event: 'span', - }, - observer: { - hostname: 'f37f48d8b60b', - id: 'd8522e1f-be8e-43c2-b290-ac6b6c0f171e', - type: 'apm-server', - ephemeral_id: '6ed88f14-170e-478d-a4f5-ea5e7f4b16b9', - version: '8.0.0', - version_major: 8, - }, - trace: { - id: '513d33fafe99bbe6134749310c9b5322', - }, - '@timestamp': '2020-03-23T15:04:28.785Z', - ecs: { - version: '1.4.0', - }, - service: { - node: { - name: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', - }, - environment: 'production', - name: 'opbeans-java', - runtime: { - name: 'Java', - version: '10.0.2', - }, - language: { - name: 'Java', - version: '10.0.2', - }, - version: 'None', - }, - host: { - hostname: '4cf84d094553', - os: { - platform: 'Linux', - }, - ip: '172.19.0.9', - name: '4cf84d094553', - architecture: 'amd64', - }, - connection: { - hash: "{service.environment:'production'}/{service.name:'opbeans-java'}/{span.subtype:'http'}/{destination.address:'opbeans-go'}/{span.type:'external'}", - }, - transaction: { - id: '49809ad3c26adf74', - }, - timestamp: { - us: 1584975868785273, - }, - span: { - duration: { - us: 17530, - }, - subtype: 'http', - name: 'GET opbeans-go', - destination: { - service: { - resource: 'opbeans-go:3000', - name: 'http://opbeans-go:3000', - type: 'external', - }, - }, - http: { - response: { - status_code: 200, - }, - url: { - original: 'http://opbeans-go:3000/api/orders', - }, - }, - id: 'fc107f7b556eb49b', - type: 'external', - }, - }, - { - parent: { - id: '975c8d5bfd1dd20b', - }, - agent: { - name: 'go', - version: '1.7.2', - }, - processor: { - name: 'transaction', - event: 'span', - }, - trace: { - id: '513d33fafe99bbe6134749310c9b5322', - }, - '@timestamp': '2020-03-23T15:04:28.787Z', - service: { - node: { - name: 'e948a08b8f5efe99b5da01f50da48c7d8aee3bbf4701f3da85ebe760c2ffef29', - }, - environment: 'production', - name: 'opbeans-go', - runtime: { - name: 'gc', - version: 'go1.14.1', - }, - language: { - name: 'go', - version: 'go1.14.1', - }, - version: 'None', - }, - transaction: { - id: '975c8d5bfd1dd20b', - }, - timestamp: { - us: 1584975868787174, - }, - span: { - duration: { - us: 16250, - }, - subtype: 'http', - destination: { - service: { - resource: 'opbeans-python:3000', - name: 'http://opbeans-python:3000', - type: 'external', - }, - }, - name: 'GET opbeans-python:3000', - http: { - response: { - status_code: 200, - }, - url: { - original: 'http://opbeans-python:3000/api/orders', - }, - }, - id: 'daae24d83c269918', - type: 'external', - }, - }, - { - container: { - id: 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51', - }, - parent: { - id: '6fb0ff7365b87298', - }, - agent: { - name: 'python', - version: '5.5.2', - }, - processor: { - name: 'transaction', - event: 'span', - }, - trace: { - id: '513d33fafe99bbe6134749310c9b5322', - }, - '@timestamp': '2020-03-23T15:04:28.790Z', - service: { - node: { - name: 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51', - }, - environment: 'production', - framework: { - name: 'django', - version: '2.1.13', - }, - name: 'opbeans-python', - runtime: { - name: 'CPython', - version: '3.6.10', - }, - language: { - name: 'python', - version: '3.6.10', - }, - version: 'None', - }, - transaction: { - id: '6fb0ff7365b87298', - }, - timestamp: { - us: 1584975868790080, - }, - span: { - duration: { - us: 2519, - }, - subtype: 'postgresql', - name: 'SELECT FROM opbeans_order', - destination: { - service: { - resource: 'postgresql', - name: 'postgresql', - type: 'db', - }, - }, - action: 'query', - id: 'c9407abb4d08ead1', - type: 'db', - sync: true, - db: { - statement: - 'SELECT "opbeans_order"."id", "opbeans_order"."customer_id", "opbeans_customer"."full_name", "opbeans_order"."created_at" FROM "opbeans_order" INNER JOIN "opbeans_customer" ON ("opbeans_order"."customer_id" = "opbeans_customer"."id") LIMIT 1000', - type: 'sql', - }, - }, - }, - ], - exceedsMax: false, - errorDocs: [ - { - parent: { - id: '975c8d5bfd1dd20b', - }, - agent: { - name: 'go', - version: '1.7.2', - }, - error: { - culprit: 'logrusMiddleware', - log: { - level: 'error', - message: 'GET //api/products (502)', - }, - id: '1f3cb98206b5c54225cb7c8908a658da', - grouping_key: '4dba2ff58fe6c036a5dee2ce411e512a', - }, - processor: { - name: 'error', - event: 'error', - }, - trace: { - id: '513d33fafe99bbe6134749310c9b5322', - }, - '@timestamp': '2020-03-23T16:04:28.787Z', - service: { - node: { - name: 'e948a08b8f5efe99b5da01f50da48c7d8aee3bbf4701f3da85ebe760c2ffef29', - }, - environment: 'production', - name: 'opbeans-go', - runtime: { - name: 'gc', - version: 'go1.14.1', - }, - language: { - name: 'go', - version: 'go1.14.1', - }, - version: 'None', - }, - transaction: { - id: '975c8d5bfd1dd20b', - sampled: false, - }, - timestamp: { - us: 1584975868787052, - }, - }, - { - parent: { - id: '6fb0ff7365b87298', - }, - agent: { - name: 'python', - version: '5.5.2', - }, - error: { - culprit: 'logrusMiddleware', - log: { - level: 'error', - message: 'GET //api/products (502)', - }, - id: '1f3cb98206b5c54225cb7c8908a658d2', - grouping_key: '4dba2ff58fe6c036a5dee2ce411e512a', - }, - processor: { - name: 'error', - event: 'error', - }, - trace: { - id: '513d33fafe99bbe6134749310c9b5322', - }, - '@timestamp': '2020-03-23T16:04:28.790Z', - service: { - node: { - name: 'e948a08b8f5efe99b5da01f50da48c7d8aee3bbf4701f3da85ebe760c2ffef29', - }, - environment: 'production', - name: 'opbeans-python', - runtime: { - name: 'gc', - version: 'go1.14.1', - }, - version: 'None', - }, - transaction: { - id: '6fb0ff7365b87298', - sampled: false, - }, - timestamp: { - us: 1584975868790000, - }, - }, - ], -} as unknown as TraceAPIResponse; - -export const traceChildStartBeforeParent = { - traceDocs: [ - { - container: { - id: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', - }, - process: { - pid: 6, - title: '/usr/lib/jvm/java-10-openjdk-amd64/bin/java', - ppid: 1, - }, - agent: { - name: 'java', - ephemeral_id: '99ce8403-5875-4945-b074-d37dc10563eb', - version: '1.14.1-SNAPSHOT', - }, - internal: { - sampler: { - value: 46, - }, - }, - source: { - ip: '172.19.0.13', - }, - processor: { - name: 'transaction', - event: 'transaction', - }, - url: { - path: '/api/orders', - scheme: 'http', - port: 3000, - domain: '172.19.0.9', - full: 'http://172.19.0.9:3000/api/orders', - }, - observer: { - hostname: 'f37f48d8b60b', - id: 'd8522e1f-be8e-43c2-b290-ac6b6c0f171e', - ephemeral_id: '6ed88f14-170e-478d-a4f5-ea5e7f4b16b9', - type: 'apm-server', - version: '8.0.0', - version_major: 8, - }, - trace: { - id: '513d33fafe99bbe6134749310c9b5322', - }, - '@timestamp': '2020-03-23T15:04:28.785Z', - ecs: { - version: '1.4.0', - }, - service: { - node: { - name: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', - }, - environment: 'production', - name: 'opbeans-java', - runtime: { - name: 'Java', - version: '10.0.2', - }, - language: { - name: 'Java', - version: '10.0.2', - }, - version: 'None', - }, - host: { - hostname: '4cf84d094553', - os: { - platform: 'Linux', - }, - ip: '172.19.0.9', - name: '4cf84d094553', - architecture: 'amd64', - }, - http: { - request: { - headers: { - Accept: ['*/*'], - 'User-Agent': ['Python/3.7 aiohttp/3.3.2'], - Host: ['172.19.0.9:3000'], - 'Accept-Encoding': ['gzip, deflate'], - }, - method: 'get', - socket: { - encrypted: false, - remote_address: '172.19.0.13', - }, - body: { - original: '[REDACTED]', - }, - }, - response: { - headers: { - 'Transfer-Encoding': ['chunked'], - Date: ['Mon, 23 Mar 2020 15:04:28 GMT'], - 'Content-Type': ['application/json;charset=ISO-8859-1'], - }, - status_code: 200, - finished: true, - headers_sent: true, - }, - version: '1.1', - }, - client: { - ip: '172.19.0.13', - }, - transaction: { - duration: { - us: 18842, - }, - result: 'HTTP 2xx', - name: 'DispatcherServlet#doGet', - id: '49809ad3c26adf74', - span_count: { - dropped: 0, - started: 1, - }, - type: 'request', - sampled: true, - }, - user_agent: { - original: 'Python/3.7 aiohttp/3.3.2', - name: 'Other', - device: { - name: 'Other', - }, - }, - timestamp: { - us: 1584975868785000, - }, - }, - { - parent: { - id: 'fc107f7b556eb49b', - }, - agent: { - name: 'go', - version: '1.7.2', - }, - processor: { - name: 'transaction', - event: 'transaction', - }, - url: { - path: '/api/orders', - scheme: 'http', - port: 3000, - domain: 'opbeans-go', - full: 'http://opbeans-go:3000/api/orders', - }, - trace: { - id: '513d33fafe99bbe6134749310c9b5322', - }, - '@timestamp': '2020-03-23T15:04:28.787Z', - service: { - node: { - name: 'e948a08b8f5efe99b5da01f50da48c7d8aee3bbf4701f3da85ebe760c2ffef29', - }, - environment: 'production', - framework: { - name: 'gin', - version: 'v1.4.0', - }, - name: 'opbeans-go', - runtime: { - name: 'gc', - version: 'go1.14.1', - }, - language: { - name: 'go', - version: 'go1.14.1', - }, - version: 'None', - }, - transaction: { - duration: { - us: 16597, - }, - result: 'HTTP 2xx', - name: 'GET /api/orders', - id: '975c8d5bfd1dd20b', - span_count: { - dropped: 0, - started: 1, - }, - type: 'request', - sampled: true, - }, - timestamp: { - us: 1584975868787052, - }, - }, - { - parent: { - id: 'daae24d83c269918', - }, - agent: { - name: 'python', - version: '5.5.2', - }, - trace: { - id: '513d33fafe99bbe6134749310c9b5322', - }, - timestamp: { - us: 1584975868780000, - }, - processor: { - name: 'transaction', - event: 'transaction', - }, - url: { - path: '/api/orders', - scheme: 'http', - port: 3000, - domain: 'opbeans-go', - full: 'http://opbeans-go:3000/api/orders', - }, - '@timestamp': '2020-03-23T15:04:28.788Z', - service: { - node: { - name: 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51', - }, - environment: 'production', - framework: { - name: 'django', - version: '2.1.13', - }, - name: 'opbeans-python', - runtime: { - name: 'CPython', - version: '3.6.10', - }, - language: { - name: 'python', - version: '3.6.10', - }, - version: 'None', - }, - transaction: { - result: 'HTTP 2xx', - duration: { - us: 1464, - }, - name: 'I started before my parent 😰', - span_count: { - dropped: 0, - started: 1, - }, - id: '6fb0ff7365b87298', - type: 'request', - sampled: true, - }, - }, - { - container: { - id: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', - }, - parent: { - id: '49809ad3c26adf74', - }, - process: { - pid: 6, - title: '/usr/lib/jvm/java-10-openjdk-amd64/bin/java', - ppid: 1, - }, - agent: { - name: 'java', - ephemeral_id: '99ce8403-5875-4945-b074-d37dc10563eb', - version: '1.14.1-SNAPSHOT', - }, - internal: { - sampler: { - value: 44, - }, - }, - destination: { - address: 'opbeans-go', - port: 3000, - }, - processor: { - name: 'transaction', - event: 'span', - }, - observer: { - hostname: 'f37f48d8b60b', - id: 'd8522e1f-be8e-43c2-b290-ac6b6c0f171e', - type: 'apm-server', - ephemeral_id: '6ed88f14-170e-478d-a4f5-ea5e7f4b16b9', - version: '8.0.0', - version_major: 8, - }, - trace: { - id: '513d33fafe99bbe6134749310c9b5322', - }, - '@timestamp': '2020-03-23T15:04:28.785Z', - ecs: { - version: '1.4.0', - }, - service: { - node: { - name: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', - }, - environment: 'production', - name: 'opbeans-java', - runtime: { - name: 'Java', - version: '10.0.2', - }, - language: { - name: 'Java', - version: '10.0.2', - }, - version: 'None', - }, - host: { - hostname: '4cf84d094553', - os: { - platform: 'Linux', - }, - ip: '172.19.0.9', - name: '4cf84d094553', - architecture: 'amd64', - }, - connection: { - hash: "{service.environment:'production'}/{service.name:'opbeans-java'}/{span.subtype:'http'}/{destination.address:'opbeans-go'}/{span.type:'external'}", - }, - transaction: { - id: '49809ad3c26adf74', - }, - timestamp: { - us: 1584975868785273, - }, - span: { - duration: { - us: 17530, - }, - subtype: 'http', - name: 'GET opbeans-go', - destination: { - service: { - resource: 'opbeans-go:3000', - name: 'http://opbeans-go:3000', - type: 'external', - }, - }, - http: { - response: { - status_code: 200, - }, - url: { - original: 'http://opbeans-go:3000/api/orders', - }, - }, - id: 'fc107f7b556eb49b', - type: 'external', - }, - }, - { - parent: { - id: '975c8d5bfd1dd20b', - }, - agent: { - name: 'go', - version: '1.7.2', - }, - processor: { - name: 'transaction', - event: 'span', - }, - trace: { - id: '513d33fafe99bbe6134749310c9b5322', - }, - '@timestamp': '2020-03-23T15:04:28.787Z', - service: { - node: { - name: 'e948a08b8f5efe99b5da01f50da48c7d8aee3bbf4701f3da85ebe760c2ffef29', - }, - environment: 'production', - name: 'opbeans-go', - runtime: { - name: 'gc', - version: 'go1.14.1', - }, - language: { - name: 'go', - version: 'go1.14.1', - }, - version: 'None', - }, - transaction: { - id: '975c8d5bfd1dd20b', - }, - timestamp: { - us: 1584975868787174, - }, - span: { - duration: { - us: 16250, - }, - subtype: 'http', - destination: { - service: { - resource: 'opbeans-python:3000', - name: 'http://opbeans-python:3000', - type: 'external', - }, - }, - name: 'I am his 👇🏻 parent 😡', - http: { - response: { - status_code: 200, - }, - url: { - original: 'http://opbeans-python:3000/api/orders', - }, - }, - id: 'daae24d83c269918', - type: 'external', - }, - }, - { - container: { - id: 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51', - }, - parent: { - id: '6fb0ff7365b87298', - }, - agent: { - name: 'python', - version: '5.5.2', - }, - processor: { - name: 'transaction', - event: 'span', - }, - trace: { - id: '513d33fafe99bbe6134749310c9b5322', - }, - '@timestamp': '2020-03-23T15:04:28.790Z', - service: { - node: { - name: 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51', - }, - environment: 'production', - framework: { - name: 'django', - version: '2.1.13', - }, - name: 'opbeans-python', - runtime: { - name: 'CPython', - version: '3.6.10', - }, - language: { - name: 'python', - version: '3.6.10', - }, - version: 'None', - }, - transaction: { - id: '6fb0ff7365b87298', - }, - timestamp: { - us: 1584975868781000, - }, - span: { - duration: { - us: 2519, - }, - subtype: 'postgresql', - name: 'I am using my parents skew 😇', - destination: { - service: { - resource: 'postgresql', - name: 'postgresql', - type: 'db', - }, - }, - action: 'query', - id: 'c9407abb4d08ead1', - type: 'db', - sync: true, - db: { - statement: - 'SELECT "opbeans_order"."id", "opbeans_order"."customer_id", "opbeans_customer"."full_name", "opbeans_order"."created_at" FROM "opbeans_order" INNER JOIN "opbeans_customer" ON ("opbeans_order"."customer_id" = "opbeans_customer"."id") LIMIT 1000', - type: 'sql', - }, - }, - }, - ], - exceedsMax: false, - errorDocs: [], -} as TraceAPIResponse; - -export const inferredSpans = { - traceDocs: [ - { - container: { - id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', - }, - agent: { - name: 'java', - ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3', - version: '1.15.1-SNAPSHOT', - }, - process: { - pid: 6, - title: '/opt/java/openjdk/bin/java', - ppid: 1, - }, - source: { - ip: '172.18.0.8', - }, - processor: { - name: 'transaction', - event: 'transaction', - }, - url: { - path: '/api/products/2', - scheme: 'http', - port: 3000, - domain: '172.18.0.7', - full: 'http://172.18.0.7:3000/api/products/2', - }, - observer: { - hostname: '7189f754b5a3', - id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc', - ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954', - type: 'apm-server', - version: '8.0.0', - version_major: 8, - }, - trace: { - id: '3b0dc77f3754e5bcb9da0e4c15e0db97', - }, - '@timestamp': '2020-04-09T11:36:00.786Z', - ecs: { - version: '1.5.0', - }, - service: { - node: { - name: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', - }, - environment: 'production', - name: 'opbeans-java', - runtime: { - name: 'Java', - version: '11.0.6', - }, - language: { - name: 'Java', - version: '11.0.6', - }, - version: 'None', - }, - host: { - hostname: 'fc2ae281f56f', - os: { - platform: 'Linux', - }, - ip: '172.18.0.7', - name: 'fc2ae281f56f', - architecture: 'amd64', - }, - client: { - ip: '172.18.0.8', - }, - http: { - request: { - headers: { - Accept: ['*/*'], - 'User-Agent': ['Python/3.7 aiohttp/3.3.2'], - Host: ['172.18.0.7:3000'], - 'Accept-Encoding': ['gzip, deflate'], - }, - method: 'get', - socket: { - encrypted: false, - remote_address: '172.18.0.8', - }, - }, - response: { - headers: { - 'Transfer-Encoding': ['chunked'], - Date: ['Thu, 09 Apr 2020 11:36:01 GMT'], - 'Content-Type': ['application/json;charset=UTF-8'], - }, - status_code: 200, - finished: true, - headers_sent: true, - }, - version: '1.1', - }, - user_agent: { - original: 'Python/3.7 aiohttp/3.3.2', - name: 'Other', - device: { - name: 'Other', - }, - }, - transaction: { - duration: { - us: 237537, - }, - result: 'HTTP 2xx', - name: 'APIRestController#product', - span_count: { - dropped: 0, - started: 3, - }, - id: 'f2387d37260d00bd', - type: 'request', - sampled: true, - }, - timestamp: { - us: 1586432160786001, - }, - }, - { - container: { - id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', - }, - parent: { - id: 'f2387d37260d00bd', - }, - agent: { - name: 'java', - ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3', - version: '1.15.1-SNAPSHOT', - }, - process: { - pid: 6, - title: '/opt/java/openjdk/bin/java', - ppid: 1, - }, - processor: { - name: 'transaction', - event: 'span', - }, - observer: { - hostname: '7189f754b5a3', - id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc', - ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954', - type: 'apm-server', - version: '8.0.0', - version_major: 8, - }, - trace: { - id: '3b0dc77f3754e5bcb9da0e4c15e0db97', - }, - '@timestamp': '2020-04-09T11:36:00.810Z', - ecs: { - version: '1.5.0', - }, - service: { - node: { - name: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', - }, - environment: 'production', - name: 'opbeans-java', - runtime: { - name: 'Java', - version: '11.0.6', - }, - language: { - name: 'Java', - version: '11.0.6', - }, - version: 'None', - }, - host: { - hostname: 'fc2ae281f56f', - os: { - platform: 'Linux', - }, - ip: '172.18.0.7', - name: 'fc2ae281f56f', - architecture: 'amd64', - }, - transaction: { - id: 'f2387d37260d00bd', - }, - span: { - duration: { - us: 204574, - }, - subtype: 'inferred', - name: 'ServletInvocableHandlerMethod#invokeAndHandle', - id: 'a5df600bd7bd5e38', - type: 'app', - }, - timestamp: { - us: 1586432160810441, - }, - }, - { - container: { - id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', - }, - parent: { - id: 'a5df600bd7bd5e38', - }, - agent: { - name: 'java', - ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3', - version: '1.15.1-SNAPSHOT', - }, - process: { - pid: 6, - title: '/opt/java/openjdk/bin/java', - ppid: 1, - }, - processor: { - name: 'transaction', - event: 'span', - }, - observer: { - hostname: '7189f754b5a3', - id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc', - type: 'apm-server', - ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954', - version: '8.0.0', - version_major: 8, - }, - trace: { - id: '3b0dc77f3754e5bcb9da0e4c15e0db97', - }, - '@timestamp': '2020-04-09T11:36:00.810Z', - ecs: { - version: '1.5.0', - }, - service: { - node: { - name: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', - }, - environment: 'production', - name: 'opbeans-java', - runtime: { - name: 'Java', - version: '11.0.6', - }, - language: { - name: 'Java', - version: '11.0.6', - }, - version: 'None', - }, - host: { - hostname: 'fc2ae281f56f', - os: { - platform: 'Linux', - }, - ip: '172.18.0.7', - name: 'fc2ae281f56f', - architecture: 'amd64', - }, - transaction: { - id: 'f2387d37260d00bd', - }, - timestamp: { - us: 1586432160810441, - }, - span: { - duration: { - us: 102993, - }, - stacktrace: [ - { - library_frame: true, - exclude_from_grouping: false, - filename: 'InvocableHandlerMethod.java', - line: { - number: -1, - }, - function: 'doInvoke', - }, - { - exclude_from_grouping: false, - library_frame: true, - filename: 'InvocableHandlerMethod.java', - line: { - number: -1, - }, - function: 'invokeForRequest', - }, - ], - subtype: 'inferred', - name: 'APIRestController#product', - id: '808dc34fc41ce522', - type: 'app', - }, - }, - { - container: { - id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', - }, - parent: { - id: 'f2387d37260d00bd', - }, - agent: { - name: 'java', - ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3', - version: '1.15.1-SNAPSHOT', - }, - process: { - pid: 6, - title: '/opt/java/openjdk/bin/java', - ppid: 1, - }, - processor: { - name: 'transaction', - event: 'span', - }, - labels: { - productId: '2', - }, - observer: { - hostname: '7189f754b5a3', - id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc', - ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954', - type: 'apm-server', - version: '8.0.0', - version_major: 8, - }, - trace: { - id: '3b0dc77f3754e5bcb9da0e4c15e0db97', - }, - '@timestamp': '2020-04-09T11:36:00.832Z', - ecs: { - version: '1.5.0', - }, - service: { - node: { - name: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', - }, - environment: 'production', - name: 'opbeans-java', - runtime: { - name: 'Java', - version: '11.0.6', - }, - language: { - name: 'Java', - version: '11.0.6', - }, - version: 'None', - }, - host: { - hostname: 'fc2ae281f56f', - os: { - platform: 'Linux', - }, - ip: '172.18.0.7', - name: 'fc2ae281f56f', - architecture: 'amd64', - }, - transaction: { - id: 'f2387d37260d00bd', - }, - timestamp: { - us: 1586432160832300, - }, - span: { - duration: { - us: 99295, - }, - name: 'OpenTracing product span', - id: '41226ae63af4f235', - type: 'unknown', - }, - child: { id: ['8d80de06aa11a6fc'] }, - }, - { - container: { - id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', - }, - parent: { - id: '808dc34fc41ce522', - }, - process: { - pid: 6, - title: '/opt/java/openjdk/bin/java', - ppid: 1, - }, - agent: { - name: 'java', - ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3', - version: '1.15.1-SNAPSHOT', - }, - processor: { - name: 'transaction', - event: 'span', - }, - observer: { - hostname: '7189f754b5a3', - id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc', - ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954', - type: 'apm-server', - version: '8.0.0', - version_major: 8, - }, - trace: { - id: '3b0dc77f3754e5bcb9da0e4c15e0db97', - }, - '@timestamp': '2020-04-09T11:36:00.859Z', - ecs: { - version: '1.5.0', - }, - service: { - node: { - name: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', - }, - environment: 'production', - name: 'opbeans-java', - runtime: { - name: 'Java', - version: '11.0.6', - }, - language: { - name: 'Java', - version: '11.0.6', - }, - version: 'None', - }, - host: { - hostname: 'fc2ae281f56f', - os: { - platform: 'Linux', - }, - ip: '172.18.0.7', - name: 'fc2ae281f56f', - architecture: 'amd64', - }, - transaction: { - id: 'f2387d37260d00bd', - }, - timestamp: { - us: 1586432160859600, - }, - span: { - duration: { - us: 53835, - }, - subtype: 'inferred', - name: 'Loader#executeQueryStatement', - id: '8d80de06aa11a6fc', - type: 'app', - }, - }, - { - container: { - id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', - }, - parent: { - id: '41226ae63af4f235', - }, - agent: { - name: 'java', - ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3', - version: '1.15.1-SNAPSHOT', - }, - process: { - pid: 6, - title: '/opt/java/openjdk/bin/java', - ppid: 1, - }, - destination: { - address: 'postgres', - port: 5432, - }, - processor: { - name: 'transaction', - event: 'span', - }, - observer: { - hostname: '7189f754b5a3', - id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc', - ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954', - type: 'apm-server', - version: '8.0.0', - version_major: 8, - }, - trace: { - id: '3b0dc77f3754e5bcb9da0e4c15e0db97', - }, - '@timestamp': '2020-04-09T11:36:00.903Z', - ecs: { - version: '1.5.0', - }, - service: { - node: { - name: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', - }, - environment: 'production', - name: 'opbeans-java', - runtime: { - name: 'Java', - version: '11.0.6', - }, - language: { - name: 'Java', - version: '11.0.6', - }, - version: 'None', - }, - host: { - hostname: 'fc2ae281f56f', - os: { - platform: 'Linux', - }, - ip: '172.18.0.7', - name: 'fc2ae281f56f', - architecture: 'amd64', - }, - transaction: { - id: 'f2387d37260d00bd', - }, - timestamp: { - us: 1586432160903236, - }, - span: { - duration: { - us: 10211, - }, - subtype: 'postgresql', - destination: { - service: { - resource: 'postgresql', - name: 'postgresql', - type: 'db', - }, - }, - name: 'SELECT FROM products', - action: 'query', - id: '3708d5623658182f', - type: 'db', - db: { - statement: - 'select product0_.id as col_0_0_, product0_.sku as col_1_0_, product0_.name as col_2_0_, product0_.description as col_3_0_, product0_.cost as col_4_0_, product0_.selling_price as col_5_0_, product0_.stock as col_6_0_, producttyp1_.id as col_7_0_, producttyp1_.name as col_8_0_, (select sum(orderline2_.amount) from order_lines orderline2_ where orderline2_.product_id=product0_.id) as col_9_0_ from products product0_ left outer join product_types producttyp1_ on product0_.type_id=producttyp1_.id where product0_.id=?', - type: 'sql', - user: { - name: 'postgres', - }, - }, - }, - }, - { - container: { - id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', - }, - parent: { - id: '41226ae63af4f235', - }, - process: { - pid: 6, - title: '/opt/java/openjdk/bin/java', - ppid: 1, - }, - agent: { - name: 'java', - ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3', - version: '1.15.1-SNAPSHOT', - }, - destination: { - address: 'postgres', - port: 5432, - }, - processor: { - name: 'transaction', - event: 'span', - }, - observer: { - hostname: '7189f754b5a3', - id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc', - ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954', - type: 'apm-server', - version: '8.0.0', - version_major: 8, - }, - trace: { - id: '3b0dc77f3754e5bcb9da0e4c15e0db97', - }, - '@timestamp': '2020-04-09T11:36:00.859Z', - ecs: { - version: '1.5.0', - }, - service: { - node: { - name: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', - }, - environment: 'production', - name: 'opbeans-java', - runtime: { - name: 'Java', - version: '11.0.6', - }, - language: { - name: 'Java', - version: '11.0.6', - }, - version: 'None', - }, - host: { - hostname: 'fc2ae281f56f', - os: { - platform: 'Linux', - }, - ip: '172.18.0.7', - name: 'fc2ae281f56f', - architecture: 'amd64', - }, - transaction: { - id: 'f2387d37260d00bd', - }, - timestamp: { - us: 1586432160859508, - }, - span: { - duration: { - us: 4503, - }, - subtype: 'postgresql', - destination: { - service: { - resource: 'postgresql', - name: 'postgresql', - type: 'db', - }, - }, - name: 'empty query', - action: 'query', - id: '9871cfd612368932', - type: 'db', - db: { - rows_affected: 0, - statement: '(empty query)', - type: 'sql', - user: { - name: 'postgres', - }, - }, - }, - }, - ], - exceedsMax: false, - errorDocs: [], -} as TraceAPIResponse; diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall_container.stories.data.ts b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall_container.stories.data.ts new file mode 100644 index 0000000000000..6cca3726a7d00 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall_container.stories.data.ts @@ -0,0 +1,5865 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Location } from 'history'; +import type { ApmUrlParams } from '../../../../../context/url_params_context/types'; +import { APIReturnType } from '../../../../../services/rest/createCallApmApi'; + +export const location = { + pathname: '/services/opbeans-go/transactions/view', + search: + '?rangeFrom=now-24h&rangeTo=now&refreshPaused=true&refreshInterval=0&kuery=service.name%253A%2520%2522opbeans-java%2522%2520or%2520service.name%2520%253A%2520%2522opbeans-go%2522&traceId=513d33fafe99bbe6134749310c9b5322&transactionId=975c8d5bfd1dd20b&transactionName=GET%20%2Fapi%2Forders&transactionType=request', + hash: '', +} as Location; + +type TraceAPIResponse = APIReturnType<'GET /internal/apm/traces/{traceId}'>; + +export const urlParams = { + start: '2020-03-22T15:16:38.742Z', + end: '2020-03-23T15:16:38.742Z', + rangeFrom: 'now-24h', + rangeTo: 'now', + refreshPaused: true, + refreshInterval: 0, + page: 0, + transactionId: '975c8d5bfd1dd20b', + traceId: '513d33fafe99bbe6134749310c9b5322', + transactionName: 'GET /api/orders', + transactionType: 'request', + processorEvent: 'transaction', + serviceName: 'opbeans-go', +} as ApmUrlParams; + +export const simpleTrace = { + traceDocs: [ + { + container: { + id: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', + }, + process: { + pid: 6, + title: '/usr/lib/jvm/java-10-openjdk-amd64/bin/java', + ppid: 1, + }, + agent: { + name: 'java', + ephemeral_id: '99ce8403-5875-4945-b074-d37dc10563eb', + version: '1.14.1-SNAPSHOT', + }, + internal: { + sampler: { + value: 46, + }, + }, + source: { + ip: '172.19.0.13', + }, + processor: { + name: 'transaction', + event: 'transaction', + }, + url: { + path: '/api/orders', + scheme: 'http', + port: 3000, + domain: '172.19.0.9', + full: 'http://172.19.0.9:3000/api/orders', + }, + observer: { + hostname: 'f37f48d8b60b', + id: 'd8522e1f-be8e-43c2-b290-ac6b6c0f171e', + ephemeral_id: '6ed88f14-170e-478d-a4f5-ea5e7f4b16b9', + type: 'apm-server', + version: '8.0.0', + version_major: 8, + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322', + }, + '@timestamp': '2020-03-23T15:04:28.785Z', + ecs: { + version: '1.4.0', + }, + service: { + node: { + name: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', + }, + environment: 'production', + name: 'opbeans-java', + runtime: { + name: 'Java', + version: '10.0.2', + }, + language: { + name: 'Java', + version: '10.0.2', + }, + version: 'None', + }, + host: { + hostname: '4cf84d094553', + os: { + platform: 'Linux', + }, + ip: '172.19.0.9', + name: '4cf84d094553', + architecture: 'amd64', + }, + http: { + request: { + headers: { + Accept: ['*/*'], + 'User-Agent': ['Python/3.7 aiohttp/3.3.2'], + Host: ['172.19.0.9:3000'], + 'Accept-Encoding': ['gzip, deflate'], + }, + method: 'get', + socket: { + encrypted: false, + remote_address: '172.19.0.13', + }, + body: { + original: '[REDACTED]', + }, + }, + response: { + headers: { + 'Transfer-Encoding': ['chunked'], + Date: ['Mon, 23 Mar 2020 15:04:28 GMT'], + 'Content-Type': ['application/json;charset=ISO-8859-1'], + }, + status_code: 200, + finished: true, + headers_sent: true, + }, + version: '1.1', + }, + client: { + ip: '172.19.0.13', + }, + transaction: { + duration: { + us: 18842, + }, + result: 'HTTP 2xx', + name: 'DispatcherServlet#doGet', + id: '49809ad3c26adf74', + span_count: { + dropped: 0, + started: 1, + }, + type: 'request', + sampled: true, + }, + user_agent: { + original: 'Python/3.7 aiohttp/3.3.2', + name: 'Other', + device: { + name: 'Other', + }, + }, + timestamp: { + us: 1584975868785000, + }, + }, + { + parent: { + id: 'fc107f7b556eb49b', + }, + agent: { + name: 'go', + version: '1.7.2', + }, + processor: { + name: 'transaction', + event: 'transaction', + }, + url: { + path: '/api/orders', + scheme: 'http', + port: 3000, + domain: 'opbeans-go', + full: 'http://opbeans-go:3000/api/orders', + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322', + }, + '@timestamp': '2020-03-23T15:04:28.787Z', + service: { + node: { + name: 'e948a08b8f5efe99b5da01f50da48c7d8aee3bbf4701f3da85ebe760c2ffef29', + }, + environment: 'production', + framework: { + name: 'gin', + version: 'v1.4.0', + }, + name: 'opbeans-go', + runtime: { + name: 'gc', + version: 'go1.14.1', + }, + language: { + name: 'go', + version: 'go1.14.1', + }, + version: 'None', + }, + transaction: { + duration: { + us: 16597, + }, + result: 'HTTP 2xx', + name: 'GET /api/orders', + id: '975c8d5bfd1dd20b', + span_count: { + dropped: 0, + started: 1, + }, + type: 'request', + sampled: true, + }, + timestamp: { + us: 1584975868787052, + }, + }, + { + parent: { + id: 'daae24d83c269918', + }, + agent: { + name: 'python', + version: '5.5.2', + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322', + }, + timestamp: { + us: 1584975868788603, + }, + processor: { + name: 'transaction', + event: 'transaction', + }, + url: { + path: '/api/orders', + scheme: 'http', + port: 3000, + domain: 'opbeans-go', + full: 'http://opbeans-go:3000/api/orders', + }, + '@timestamp': '2020-03-23T15:04:28.788Z', + service: { + node: { + name: 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51', + }, + environment: 'production', + framework: { + name: 'django', + version: '2.1.13', + }, + name: 'opbeans-python', + runtime: { + name: 'CPython', + version: '3.6.10', + }, + language: { + name: 'python', + version: '3.6.10', + }, + version: 'None', + }, + transaction: { + result: 'HTTP 2xx', + duration: { + us: 14648, + }, + name: 'GET opbeans.views.orders', + span_count: { + dropped: 0, + started: 1, + }, + id: '6fb0ff7365b87298', + type: 'request', + sampled: true, + }, + }, + { + container: { + id: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', + }, + parent: { + id: '49809ad3c26adf74', + }, + process: { + pid: 6, + title: '/usr/lib/jvm/java-10-openjdk-amd64/bin/java', + ppid: 1, + }, + agent: { + name: 'java', + ephemeral_id: '99ce8403-5875-4945-b074-d37dc10563eb', + version: '1.14.1-SNAPSHOT', + }, + internal: { + sampler: { + value: 44, + }, + }, + destination: { + address: 'opbeans-go', + port: 3000, + }, + processor: { + name: 'transaction', + event: 'span', + }, + observer: { + hostname: 'f37f48d8b60b', + id: 'd8522e1f-be8e-43c2-b290-ac6b6c0f171e', + type: 'apm-server', + ephemeral_id: '6ed88f14-170e-478d-a4f5-ea5e7f4b16b9', + version: '8.0.0', + version_major: 8, + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322', + }, + '@timestamp': '2020-03-23T15:04:28.785Z', + ecs: { + version: '1.4.0', + }, + service: { + node: { + name: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', + }, + environment: 'production', + name: 'opbeans-java', + runtime: { + name: 'Java', + version: '10.0.2', + }, + language: { + name: 'Java', + version: '10.0.2', + }, + version: 'None', + }, + host: { + hostname: '4cf84d094553', + os: { + platform: 'Linux', + }, + ip: '172.19.0.9', + name: '4cf84d094553', + architecture: 'amd64', + }, + connection: { + hash: "{service.environment:'production'}/{service.name:'opbeans-java'}/{span.subtype:'http'}/{destination.address:'opbeans-go'}/{span.type:'external'}", + }, + transaction: { + id: '49809ad3c26adf74', + }, + timestamp: { + us: 1584975868785273, + }, + span: { + duration: { + us: 17530, + }, + subtype: 'http', + name: 'GET opbeans-go', + destination: { + service: { + resource: 'opbeans-go:3000', + name: 'http://opbeans-go:3000', + type: 'external', + }, + }, + http: { + response: { + status_code: 200, + }, + url: { + original: 'http://opbeans-go:3000/api/orders', + }, + }, + id: 'fc107f7b556eb49b', + type: 'external', + }, + }, + { + parent: { + id: '975c8d5bfd1dd20b', + }, + agent: { + name: 'go', + version: '1.7.2', + }, + processor: { + name: 'transaction', + event: 'span', + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322', + }, + '@timestamp': '2020-03-23T15:04:28.787Z', + service: { + node: { + name: 'e948a08b8f5efe99b5da01f50da48c7d8aee3bbf4701f3da85ebe760c2ffef29', + }, + environment: 'production', + name: 'opbeans-go', + runtime: { + name: 'gc', + version: 'go1.14.1', + }, + language: { + name: 'go', + version: 'go1.14.1', + }, + version: 'None', + }, + transaction: { + id: '975c8d5bfd1dd20b', + }, + timestamp: { + us: 1584975868787174, + }, + span: { + duration: { + us: 16250, + }, + subtype: 'http', + destination: { + service: { + resource: 'opbeans-python:3000', + name: 'http://opbeans-python:3000', + type: 'external', + }, + }, + name: 'GET opbeans-python:3000', + http: { + response: { + status_code: 200, + }, + url: { + original: 'http://opbeans-python:3000/api/orders', + }, + }, + id: 'daae24d83c269918', + type: 'external', + }, + }, + { + container: { + id: 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51', + }, + parent: { + id: '6fb0ff7365b87298', + }, + agent: { + name: 'python', + version: '5.5.2', + }, + processor: { + name: 'transaction', + event: 'span', + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322', + }, + '@timestamp': '2020-03-23T15:04:28.790Z', + service: { + node: { + name: 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51', + }, + environment: 'production', + framework: { + name: 'django', + version: '2.1.13', + }, + name: 'opbeans-python', + runtime: { + name: 'CPython', + version: '3.6.10', + }, + language: { + name: 'python', + version: '3.6.10', + }, + version: 'None', + }, + transaction: { + id: '6fb0ff7365b87298', + }, + timestamp: { + us: 1584975868790080, + }, + span: { + duration: { + us: 2519, + }, + subtype: 'postgresql', + name: 'SELECT FROM opbeans_order', + destination: { + service: { + resource: 'postgresql', + name: 'postgresql', + type: 'db', + }, + }, + action: 'query', + id: 'c9407abb4d08ead1', + type: 'db', + sync: true, + db: { + statement: + 'SELECT "opbeans_order"."id", "opbeans_order"."customer_id", "opbeans_customer"."full_name", "opbeans_order"."created_at" FROM "opbeans_order" INNER JOIN "opbeans_customer" ON ("opbeans_order"."customer_id" = "opbeans_customer"."id") LIMIT 1000', + type: 'sql', + }, + }, + }, + ], + exceedsMax: false, + errorDocs: [], +} as TraceAPIResponse; + +export const manyChildrenWithSameLength = { + exceedsMax: false, + traceDocs: [ + { + container: { + id: '46721e28e45ec1926798491069d8585865b031b4eaa9800e35d06fef6be5e170', + }, + kubernetes: { + pod: { + uid: '900f3cac-eb7c-4308-9376-f644f173c3ee', + }, + }, + process: { + args: ['-C', 'config/puma.rb'], + pid: 38, + title: '/usr/local/bundle/bin/puma', + }, + agent: { + name: 'ruby', + version: '4.3.0', + }, + processor: { + name: 'transaction', + event: 'transaction', + }, + url: { + path: '/api/products/3', + scheme: 'http', + port: 3000, + domain: '10.15.245.224', + full: 'http://10.15.245.224:3000/api/products/3', + }, + cloud: { + availability_zone: 'us-central1-c', + instance: { + name: 'gke-dev-oblt-dev-oblt-pool-18e89389-qntq', + id: '5278603844673466232', + }, + provider: 'gcp', + machine: { + type: 'projects/8560181848/machineTypes/n1-standard-4', + }, + project: { + name: 'elastic-observability', + id: '8560181848', + }, + region: 'us-central1', + }, + observer: { + hostname: 'apm-apm-server-65d9d8dd68-zvs6p', + id: '69a7066f-46d2-42c4-a4cc-8400f60bf2b5', + ephemeral_id: '0ab88569-c301-40e9-8e78-cac7c1dac2bc', + type: 'apm-server', + version: '7.16.0', + version_major: 7, + }, + trace: { + id: 'd5e80ae688f1fef91533f02dd2bdc769', + }, + '@timestamp': '2021-10-19T13:57:02.536Z', + ecs: { + version: '1.11.0', + }, + service: { + node: { + name: '46721e28e45ec1926798491069d8585865b031b4eaa9800e35d06fef6be5e170', + }, + environment: 'production', + framework: { + name: 'Ruby on Rails', + version: '6.1.4.1', + }, + name: 'opbeans-ruby', + runtime: { + name: 'ruby', + version: '2.7.3', + }, + language: { + name: 'ruby', + version: '2.7.3', + }, + version: '2021-10-14 17:49:53', + }, + host: { + os: { + platform: 'linux', + }, + ip: '10.12.0.22', + architecture: 'x86_64', + }, + http: { + request: { + headers: { + Accept: ['*/*'], + Version: ['HTTP/1.1'], + 'User-Agent': ['Python/3.7 aiohttp/3.3.2'], + Host: ['10.15.245.224:3000'], + 'Accept-Encoding': ['gzip, deflate'], + }, + method: 'GET', + env: { + GATEWAY_INTERFACE: 'CGI/1.2', + ORIGINAL_FULLPATH: '/api/products/3', + SERVER_PORT: '3000', + SERVER_PROTOCOL: 'HTTP/1.1', + REMOTE_ADDR: '10.12.6.45', + REQUEST_URI: '/api/products/3', + ORIGINAL_SCRIPT_NAME: '', + SERVER_SOFTWARE: 'puma 5.5.0 Zawgyi', + QUERY_STRING: '', + SCRIPT_NAME: '', + REQUEST_METHOD: 'GET', + SERVER_NAME: '10.15.245.224', + REQUEST_PATH: '/api/products/3', + PATH_INFO: '/api/products/3', + ROUTES_9240_SCRIPT_NAME: '', + }, + body: { + original: '[SKIPPED]', + }, + }, + response: { + headers: { + 'Content-Type': ['application/json;charset=UTF-8'], + }, + status_code: 500, + finished: true, + headers_sent: true, + }, + version: '1.1', + }, + event: { + ingested: '2021-10-19T13:57:12.417144879Z', + outcome: 'failure', + }, + transaction: { + duration: { + us: 13359, + }, + result: 'HTTP 5xx', + name: 'Rack', + span_count: { + dropped: 0, + started: 1, + }, + id: '9a7f717439921d39', + type: 'request', + sampled: true, + }, + user_agent: { + original: 'Python/3.7 aiohttp/3.3.2', + name: 'Python aiohttp', + device: { + name: 'Other', + type: 'Other', + }, + version: '3.3.2', + }, + timestamp: { + us: 1634651822536408, + }, + }, + { + container: { + id: 'e7b69f99cb7523bedea6d7c97b684cf4b7ff458d0cba1efb1ac843300b3bf3c7', + }, + kubernetes: { + pod: { + uid: 'c5169b50-f3b3-4693-8e4b-150fca17c333', + name: 'opbeans-go-5d795ddf6b-rhlvf', + }, + }, + parent: { + id: '4eeaa6dfbfd047cd', + }, + agent: { + name: 'go', + version: '1.14.0', + }, + source: { + ip: '10.12.0.22', + }, + cloud: { + availability_zone: 'us-central1-c', + instance: { + name: 'gke-dev-oblt-dev-oblt-pool-18e89389-qntq', + id: '5278603844673466232', + }, + provider: 'gcp', + machine: { + type: 'n1-standard-4', + }, + project: { + name: 'elastic-observability', + id: '8560181848', + }, + region: 'us-central1', + }, + observer: { + hostname: 'apm-apm-server-65d9d8dd68-lmf6c', + id: '7eedab18-1171-4a1b-a590-975e13fd103a', + type: 'apm-server', + ephemeral_id: '90034868-48e6-418c-8ab4-6616b403bca7', + version: '7.16.0', + version_major: 7, + }, + trace: { + id: 'd5e80ae688f1fef91533f02dd2bdc769', + }, + ecs: { + version: '1.11.0', + }, + host: { + os: { + platform: 'linux', + }, + ip: '10.12.0.14', + architecture: 'amd64', + }, + client: { + ip: '10.12.0.22', + }, + event: { + ingested: '2021-10-19T13:57:05.413190788Z', + outcome: 'failure', + }, + user_agent: { + original: 'http.rb/5.0.2', + name: 'Other', + device: { + name: 'Generic Feature Phone', + type: 'Other', + }, + }, + timestamp: { + us: 1634651822536408, + }, + process: { + args: [ + '/opbeans-go', + '-log-level=debug', + '-log-json', + '-listen=:3000', + '-frontend=/opbeans-frontend', + '-db=postgres:', + '-cache=redis://redis-master:6379', + ], + pid: 1, + title: 'opbeans-go', + ppid: 0, + }, + processor: { + name: 'transaction', + event: 'transaction', + }, + url: { + path: '/api/products/3', + scheme: 'http', + port: 3000, + domain: 'opbeans', + full: 'http://opbeans:3000/api/products/3', + }, + '@timestamp': '2021-10-19T13:57:02.536Z', + service: { + node: { + name: 'e7b69f99cb7523bedea6d7c97b684cf4b7ff458d0cba1efb1ac843300b3bf3c7', + }, + environment: 'testing', + framework: { + name: 'gin', + version: 'v1.7.3', + }, + name: 'opbeans-go', + runtime: { + name: 'gc', + version: 'go1.17.2', + }, + language: { + name: 'go', + version: 'go1.17.2', + }, + version: '2021-10-14 17:49:50', + }, + http: { + request: { + headers: { + Connection: ['close'], + 'User-Agent': ['http.rb/5.0.2'], + 'Elastic-Apm-Traceparent': [ + '00-d5e80ae688f1fef91533f02dd2bdc769-4eeaa6dfbfd047cd-01', + ], + Tracestate: ['es=s:1.0'], + Traceparent: [ + '00-d5e80ae688f1fef91533f02dd2bdc769-4eeaa6dfbfd047cd-01', + ], + }, + method: 'GET', + }, + response: { + headers: { + Date: ['Tue, 19 Oct 2021 13:57:02 GMT'], + 'Content-Type': ['application/json;charset=UTF-8'], + }, + status_code: 500, + }, + version: '1.1', + }, + transaction: { + result: 'HTTP 5xx', + duration: { + us: 13359, + }, + name: 'GET /api/products/:id', + id: '9f50f43e924d0b46', + span_count: { + dropped: 0, + started: 3, + }, + type: 'request', + sampled: true, + }, + }, + { + container: { + id: '015d1127421e2c3d42a0fb031fc75e989813f58973143b6c7e33dca6ccc6f31b', + }, + parent: { + id: '8d099ab4fcec4ab9', + }, + kubernetes: { + pod: { + uid: '459a6abf-198e-4107-b4dd-b0ae826755ab', + name: 'opbeans-go-nsn-69b89c4598-xsvgh', + }, + }, + agent: { + name: 'go', + version: '1.14.0', + }, + source: { + ip: '10.12.0.14', + }, + cloud: { + availability_zone: 'us-central1-c', + instance: { + name: 'gke-dev-oblt-dev-oblt-pool-18e89389-qntq', + id: '5278603844673466232', + }, + provider: 'gcp', + machine: { + type: 'n1-standard-4', + }, + project: { + name: 'elastic-observability', + id: '8560181848', + }, + region: 'us-central1', + }, + observer: { + hostname: 'apm-apm-server-65d9d8dd68-zvs6p', + id: '69a7066f-46d2-42c4-a4cc-8400f60bf2b5', + ephemeral_id: '0ab88569-c301-40e9-8e78-cac7c1dac2bc', + type: 'apm-server', + version: '7.16.0', + version_major: 7, + }, + trace: { + id: 'd5e80ae688f1fef91533f02dd2bdc769', + }, + ecs: { + version: '1.11.0', + }, + host: { + os: { + platform: 'linux', + }, + ip: '10.12.0.13', + architecture: 'amd64', + }, + client: { + ip: '10.12.0.22', + }, + event: { + ingested: '2021-10-19T13:57:08.267103644Z', + outcome: 'failure', + }, + user_agent: { + original: 'http.rb/5.0.2', + name: 'Other', + device: { + name: 'Generic Feature Phone', + type: 'Other', + }, + }, + timestamp: { + us: 1634651822536408, + }, + process: { + args: [ + '/opbeans-go', + '-log-level=debug', + '-log-json', + '-listen=:3000', + '-frontend=/opbeans-frontend', + '-db=postgres:', + '-cache=redis://redis-master:6379', + ], + pid: 1, + title: 'opbeans-go', + ppid: 0, + }, + processor: { + name: 'transaction', + event: 'transaction', + }, + url: { + path: '/api/products/3', + scheme: 'http', + port: 3000, + domain: 'opbeans', + full: 'http://opbeans:3000/api/products/3', + }, + '@timestamp': '2021-10-19T13:57:02.536Z', + service: { + node: { + name: '015d1127421e2c3d42a0fb031fc75e989813f58973143b6c7e33dca6ccc6f31b', + }, + environment: 'testing', + framework: { + name: 'gin', + version: 'v1.7.3', + }, + name: 'opbeans-go', + runtime: { + name: 'gc', + version: 'go1.17.2', + }, + language: { + name: 'go', + version: 'go1.17.2', + }, + version: '2021-10-14 17:49:50', + }, + http: { + request: { + headers: { + 'User-Agent': ['http.rb/5.0.2'], + 'X-Forwarded-For': ['10.12.0.22'], + 'Accept-Encoding': ['gzip'], + 'Elastic-Apm-Traceparent': [ + '00-d5e80ae688f1fef91533f02dd2bdc769-8d099ab4fcec4ab9-01', + ], + Tracestate: ['es=s:1.0'], + Traceparent: [ + '00-d5e80ae688f1fef91533f02dd2bdc769-8d099ab4fcec4ab9-01', + ], + }, + method: 'GET', + }, + response: { + headers: { + Date: ['Tue, 19 Oct 2021 13:57:02 GMT'], + 'Content-Type': ['application/json;charset=UTF-8'], + }, + status_code: 500, + }, + version: '1.1', + }, + transaction: { + result: 'HTTP 5xx', + duration: { + us: 13359, + }, + name: 'GET /api/products/:id', + span_count: { + dropped: 0, + started: 3, + }, + id: 'b7801be83bcdc972', + type: 'request', + sampled: true, + }, + }, + { + container: { + id: '59036ecb70908dfec4e03edc477f6875d08677871b4af0db3144373802d00cb1', + }, + kubernetes: { + pod: { + uid: '878bab2a-1309-44ae-a0e2-c98a0b187da1', + name: 'opbeans-java-5f45d77dd8-h8bnb', + }, + }, + parent: { + id: '35e3637e26919055', + }, + agent: { + name: 'java', + ephemeral_id: '75e36588-9adb-4bb0-bfee-a333b1c57e67', + version: 'unknown', + }, + source: { + ip: '10.12.0.13', + }, + cloud: { + availability_zone: 'us-central1-c', + instance: { + name: 'gke-dev-oblt-dev-oblt-pool-18e89389-qntq', + id: '5278603844673466232', + }, + provider: 'gcp', + machine: { + type: 'n1-standard-4', + }, + project: { + name: 'elastic-observability', + id: '8560181848', + }, + region: 'us-central1', + }, + observer: { + hostname: 'apm-apm-server-65d9d8dd68-lmf6c', + id: '7eedab18-1171-4a1b-a590-975e13fd103a', + ephemeral_id: '90034868-48e6-418c-8ab4-6616b403bca7', + type: 'apm-server', + version: '7.16.0', + version_major: 7, + }, + trace: { + id: 'd5e80ae688f1fef91533f02dd2bdc769', + }, + ecs: { + version: '1.11.0', + }, + host: { + os: { + platform: 'Linux', + }, + ip: '10.12.0.15', + architecture: 'amd64', + }, + client: { + ip: '10.12.0.22', + }, + event: { + ingested: '2021-10-19T13:57:10.382829210Z', + outcome: 'failure', + }, + user_agent: { + original: 'http.rb/5.0.2', + name: 'Other', + device: { + name: 'Generic Feature Phone', + type: 'Other', + }, + }, + timestamp: { + us: 1634651822536408, + }, + process: { + pid: 7, + title: '/opt/java/openjdk/bin/java', + ppid: 1, + }, + processor: { + name: 'transaction', + event: 'transaction', + }, + url: { + path: '/api/products/3', + scheme: 'http', + port: 3000, + domain: 'opbeans', + full: 'http://opbeans:3000/api/products/3', + }, + '@timestamp': '2021-10-19T13:57:02.536Z', + service: { + node: { + name: '59036ecb70908dfec4e03edc477f6875d08677871b4af0db3144373802d00cb1', + }, + environment: 'production', + framework: { + name: 'Spring Web MVC', + version: '5.0.6.RELEASE', + }, + name: 'opbeans-java', + runtime: { + name: 'Java', + version: '11.0.11', + }, + language: { + name: 'Java', + version: '11.0.11', + }, + version: '2021-10-14 17:49:52', + }, + http: { + request: { + headers: { + 'User-Agent': ['http.rb/5.0.2'], + 'X-Forwarded-For': ['10.12.0.22, 10.12.0.14'], + Host: ['opbeans:3000'], + 'Accept-Encoding': ['gzip'], + 'Elastic-Apm-Traceparent': [ + '00-d5e80ae688f1fef91533f02dd2bdc769-35e3637e26919055-01', + ], + Tracestate: ['es=s:1.0'], + Traceparent: [ + '00-d5e80ae688f1fef91533f02dd2bdc769-35e3637e26919055-01', + ], + }, + method: 'GET', + }, + response: { + status_code: 500, + finished: true, + headers_sent: true, + }, + version: '1.1', + }, + transaction: { + duration: { + us: 13359, + }, + result: 'HTTP 5xx', + name: 'APIRestController#product', + id: '2c30263c4ad8fe8b', + span_count: { + dropped: 0, + started: 3, + }, + type: 'request', + sampled: true, + }, + }, + { + parent: { + id: '9a7f717439921d39', + }, + agent: { + name: 'ruby', + version: '4.3.0', + }, + destination: { + address: 'opbeans', + port: 3000, + }, + cloud: { + availability_zone: 'us-central1-c', + instance: { + name: 'gke-dev-oblt-dev-oblt-pool-18e89389-qntq', + id: '5278603844673466232', + }, + provider: 'gcp', + machine: { + type: 'projects/8560181848/machineTypes/n1-standard-4', + }, + project: { + name: 'elastic-observability', + id: '8560181848', + }, + region: 'us-central1', + }, + observer: { + hostname: 'apm-apm-server-65d9d8dd68-lmf6c', + id: '7eedab18-1171-4a1b-a590-975e13fd103a', + ephemeral_id: '90034868-48e6-418c-8ab4-6616b403bca7', + type: 'apm-server', + version: '7.16.0', + version_major: 7, + }, + trace: { + id: 'd5e80ae688f1fef91533f02dd2bdc769', + }, + ecs: { + version: '1.11.0', + }, + event: { + outcome: 'failure', + }, + timestamp: { + us: 1634651822536408, + }, + processor: { + name: 'transaction', + event: 'span', + }, + url: { + original: 'http://opbeans:3000/api/products/3', + }, + '@timestamp': '2021-10-19T13:57:02.536Z', + service: { + environment: 'production', + name: 'opbeans-ruby', + }, + http: { + request: { + method: 'GET', + }, + response: { + status_code: 500, + }, + }, + transaction: { + id: '9a7f717439921d39', + }, + span: { + duration: { + us: 13359, + }, + stacktrace: [ + { + exclude_from_grouping: false, + library_frame: true, + filename: 'elastic_apm.rb', + abs_path: + '/usr/local/bundle/gems/elastic-apm-4.3.0/lib/elastic_apm.rb', + line: { + number: 235, + }, + function: 'tap', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'elastic_apm.rb', + abs_path: + '/usr/local/bundle/gems/elastic-apm-4.3.0/lib/elastic_apm.rb', + line: { + number: 235, + }, + function: 'start_span', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'elastic_apm.rb', + abs_path: + '/usr/local/bundle/gems/elastic-apm-4.3.0/lib/elastic_apm.rb', + line: { + number: 287, + }, + function: 'with_span', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'elastic_apm/spies/http.rb', + abs_path: + '/usr/local/bundle/gems/elastic-apm-4.3.0/lib/elastic_apm/spies/http.rb', + line: { + number: 45, + }, + function: 'perform', + }, + { + library_frame: true, + exclude_from_grouping: false, + abs_path: '/usr/local/bundle/gems/http-5.0.2/lib/http/client.rb', + filename: 'http/client.rb', + line: { + number: 31, + }, + function: 'request', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'http/chainable.rb', + abs_path: '/usr/local/bundle/gems/http-5.0.2/lib/http/chainable.rb', + line: { + number: 75, + }, + function: 'request', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'http/chainable.rb', + abs_path: '/usr/local/bundle/gems/http-5.0.2/lib/http/chainable.rb', + line: { + number: 20, + }, + function: 'get', + }, + { + exclude_from_grouping: false, + filename: 'opbeans_shuffle.rb', + abs_path: '/app/lib/opbeans_shuffle.rb', + line: { + number: 23, + context: ' resp = HTTP.get("#{lucky_winner}#{path}")\n', + }, + function: 'block in call', + context: { + pre: ['\n', ' Timeout.timeout(15) do\n'], + post: ['\n', ' [\n'], + }, + }, + { + exclude_from_grouping: false, + library_frame: true, + filename: 'timeout.rb', + abs_path: '/usr/local/lib/ruby/2.7.0/timeout.rb', + line: { + number: 95, + }, + function: 'block in timeout', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'timeout.rb', + abs_path: '/usr/local/lib/ruby/2.7.0/timeout.rb', + line: { + number: 33, + }, + function: 'block in catch', + }, + { + library_frame: true, + exclude_from_grouping: false, + abs_path: '/usr/local/lib/ruby/2.7.0/timeout.rb', + filename: 'timeout.rb', + line: { + number: 33, + }, + function: 'catch', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'timeout.rb', + abs_path: '/usr/local/lib/ruby/2.7.0/timeout.rb', + line: { + number: 33, + }, + function: 'catch', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'timeout.rb', + abs_path: '/usr/local/lib/ruby/2.7.0/timeout.rb', + line: { + number: 110, + }, + function: 'timeout', + }, + { + exclude_from_grouping: false, + filename: 'opbeans_shuffle.rb', + abs_path: '/app/lib/opbeans_shuffle.rb', + line: { + number: 22, + context: ' Timeout.timeout(15) do\n', + }, + function: 'call', + context: { + pre: [' end\n', '\n'], + post: [ + ' resp = HTTP.get("#{lucky_winner}#{path}")\n', + '\n', + ], + }, + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'elastic_apm/middleware.rb', + abs_path: + '/usr/local/bundle/gems/elastic-apm-4.3.0/lib/elastic_apm/middleware.rb', + line: { + number: 36, + }, + function: 'call', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'rails/engine.rb', + abs_path: + '/usr/local/bundle/gems/railties-6.1.4.1/lib/rails/engine.rb', + line: { + number: 539, + }, + function: 'call', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'puma/configuration.rb', + abs_path: + '/usr/local/bundle/gems/puma-5.5.0/lib/puma/configuration.rb', + line: { + number: 249, + }, + function: 'call', + }, + { + library_frame: true, + exclude_from_grouping: false, + abs_path: '/usr/local/bundle/gems/puma-5.5.0/lib/puma/request.rb', + filename: 'puma/request.rb', + line: { + number: 77, + }, + function: 'block in handle_request', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'puma/thread_pool.rb', + abs_path: + '/usr/local/bundle/gems/puma-5.5.0/lib/puma/thread_pool.rb', + line: { + number: 340, + }, + function: 'with_force_shutdown', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'puma/request.rb', + abs_path: '/usr/local/bundle/gems/puma-5.5.0/lib/puma/request.rb', + line: { + number: 76, + }, + function: 'handle_request', + }, + { + exclude_from_grouping: false, + library_frame: true, + filename: 'puma/server.rb', + abs_path: '/usr/local/bundle/gems/puma-5.5.0/lib/puma/server.rb', + line: { + number: 447, + }, + function: 'process_client', + }, + { + exclude_from_grouping: false, + library_frame: true, + filename: 'puma/thread_pool.rb', + abs_path: + '/usr/local/bundle/gems/puma-5.5.0/lib/puma/thread_pool.rb', + line: { + number: 147, + }, + function: 'block in spawn_thread', + }, + ], + subtype: 'http', + destination: { + service: { + resource: 'opbeans:3000', + name: 'http', + type: 'external', + }, + }, + name: 'GET opbeans', + http: { + method: 'GET', + response: { + status_code: 500, + }, + }, + 'http.url.original': 'http://opbeans:3000/api/products/3', + id: '4eeaa6dfbfd047cd', + type: 'external', + }, + }, + { + parent: { + id: '9f50f43e924d0b46', + }, + agent: { + name: 'go', + version: '1.14.0', + }, + destination: { + address: 'opbeans', + port: 3000, + }, + cloud: { + availability_zone: 'us-central1-c', + instance: { + name: 'gke-dev-oblt-dev-oblt-pool-18e89389-qntq', + id: '5278603844673466232', + }, + provider: 'gcp', + machine: { + type: 'n1-standard-4', + }, + project: { + name: 'elastic-observability', + id: '8560181848', + }, + region: 'us-central1', + }, + observer: { + hostname: 'apm-apm-server-65d9d8dd68-lmf6c', + id: '7eedab18-1171-4a1b-a590-975e13fd103a', + ephemeral_id: '90034868-48e6-418c-8ab4-6616b403bca7', + type: 'apm-server', + version: '7.16.0', + version_major: 7, + }, + trace: { + id: 'd5e80ae688f1fef91533f02dd2bdc769', + }, + ecs: { + version: '1.11.0', + }, + event: { + outcome: 'failure', + }, + timestamp: { + us: 1634651822536408, + }, + processor: { + name: 'transaction', + event: 'span', + }, + url: { + original: 'http://opbeans:3000/api/products/3', + }, + '@timestamp': '2021-10-19T13:57:02.536Z', + service: { + environment: 'testing', + name: 'opbeans-go', + }, + http: { + response: { + status_code: 500, + }, + }, + transaction: { + id: '9f50f43e924d0b46', + }, + span: { + duration: { + us: 13359, + }, + stacktrace: [ + { + exclude_from_grouping: false, + library_frame: true, + filename: 'span.go', + abs_path: '/go/pkg/mod/go.elastic.co/apm@v1.14.0/span.go', + line: { + number: 334, + }, + module: 'go.elastic.co/apm', + function: '(*Span).End', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'client.go', + abs_path: + '/go/pkg/mod/go.elastic.co/apm/module/apmhttp@v1.14.0/client.go', + line: { + number: 198, + }, + module: 'go.elastic.co/apm/module/apmhttp', + function: '(*responseBody).endSpan', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'client.go', + abs_path: + '/go/pkg/mod/go.elastic.co/apm/module/apmhttp@v1.14.0/client.go', + line: { + number: 187, + }, + function: '(*responseBody).Read', + module: 'go.elastic.co/apm/module/apmhttp', + }, + { + exclude_from_grouping: false, + library_frame: true, + filename: 'reverseproxy.go', + abs_path: '/usr/local/go/src/net/http/httputil/reverseproxy.go', + line: { + number: 461, + }, + module: 'net/http/httputil', + function: '(*ReverseProxy).copyBuffer', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'reverseproxy.go', + abs_path: '/usr/local/go/src/net/http/httputil/reverseproxy.go', + line: { + number: 449, + }, + module: 'net/http/httputil', + function: '(*ReverseProxy).copyResponse', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'reverseproxy.go', + abs_path: '/usr/local/go/src/net/http/httputil/reverseproxy.go', + line: { + number: 338, + }, + module: 'net/http/httputil', + function: '(*ReverseProxy).ServeHTTP', + }, + { + exclude_from_grouping: false, + filename: 'main.go', + abs_path: '/src/opbeans-go/main.go', + line: { + number: 196, + }, + module: 'main', + function: 'Main.func2', + }, + { + library_frame: true, + exclude_from_grouping: false, + abs_path: '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/context.go', + filename: 'context.go', + line: { + number: 165, + }, + module: 'github.com/gin-gonic/gin', + function: '(*Context).Next', + }, + { + exclude_from_grouping: false, + filename: 'main.go', + abs_path: '/src/opbeans-go/main.go', + line: { + number: 174, + }, + module: 'main', + function: 'Main.func1', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'context.go', + abs_path: '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/context.go', + line: { + number: 165, + }, + module: 'github.com/gin-gonic/gin', + function: '(*Context).Next', + }, + { + exclude_from_grouping: false, + filename: 'logger.go', + abs_path: '/src/opbeans-go/logger.go', + line: { + number: 36, + }, + module: 'main', + function: 'logrusMiddleware', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'context.go', + abs_path: '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/context.go', + line: { + number: 165, + }, + module: 'github.com/gin-gonic/gin', + function: '(*Context).Next', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'middleware.go', + abs_path: + '/go/pkg/mod/go.elastic.co/apm/module/apmgin@v1.14.0/middleware.go', + line: { + number: 98, + }, + module: 'go.elastic.co/apm/module/apmgin', + function: '(*middleware).handle', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'context.go', + abs_path: '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/context.go', + line: { + number: 165, + }, + module: 'github.com/gin-gonic/gin', + function: '(*Context).Next', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'cache.go', + abs_path: + '/go/pkg/mod/github.com/gin-contrib/cache@v1.1.0/cache.go', + line: { + number: 128, + }, + module: 'github.com/gin-contrib/cache', + function: 'Cache.func1', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'context.go', + abs_path: '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/context.go', + line: { + number: 165, + }, + module: 'github.com/gin-gonic/gin', + function: '(*Context).Next', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'gin.go', + abs_path: '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/gin.go', + line: { + number: 489, + }, + function: '(*Engine).handleHTTPRequest', + module: 'github.com/gin-gonic/gin', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'gin.go', + abs_path: '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/gin.go', + line: { + number: 445, + }, + function: '(*Engine).ServeHTTP', + module: 'github.com/gin-gonic/gin', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'server.go', + abs_path: '/usr/local/go/src/net/http/server.go', + line: { + number: 2878, + }, + module: 'net/http', + function: 'serverHandler.ServeHTTP', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'server.go', + abs_path: '/usr/local/go/src/net/http/server.go', + line: { + number: 1929, + }, + module: 'net/http', + function: '(*conn).serve', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'asm_amd64.s', + abs_path: '/usr/local/go/src/runtime/asm_amd64.s', + line: { + number: 1581, + }, + module: 'runtime', + function: 'goexit', + }, + ], + subtype: 'http', + name: 'GET opbeans:3000', + destination: { + service: { + resource: 'opbeans:3000', + name: 'http://opbeans:3000', + type: 'external', + }, + }, + http: { + response: { + status_code: 500, + }, + }, + 'http.url.original': 'http://opbeans:3000/api/products/3', + id: '8d099ab4fcec4ab9', + type: 'external', + }, + }, + { + parent: { + id: '86c43ac014573747', + }, + agent: { + name: 'go', + version: '1.14.0', + }, + processor: { + name: 'transaction', + event: 'span', + }, + cloud: { + availability_zone: 'us-central1-c', + instance: { + name: 'gke-dev-oblt-dev-oblt-pool-18e89389-qntq', + id: '5278603844673466232', + }, + provider: 'gcp', + machine: { + type: 'n1-standard-4', + }, + project: { + name: 'elastic-observability', + id: '8560181848', + }, + region: 'us-central1', + }, + observer: { + hostname: 'apm-apm-server-65d9d8dd68-lmf6c', + id: '7eedab18-1171-4a1b-a590-975e13fd103a', + type: 'apm-server', + ephemeral_id: '90034868-48e6-418c-8ab4-6616b403bca7', + version: '7.16.0', + version_major: 7, + }, + trace: { + id: 'd5e80ae688f1fef91533f02dd2bdc769', + }, + '@timestamp': '2021-10-19T13:57:02.539Z', + ecs: { + version: '1.11.0', + }, + service: { + environment: 'testing', + name: 'opbeans-go', + }, + event: { + outcome: 'unknown', + }, + transaction: { + id: '9f50f43e924d0b46', + }, + span: { + duration: { + us: 13359, + }, + stacktrace: [ + { + exclude_from_grouping: false, + library_frame: true, + filename: 'span.go', + abs_path: '/go/pkg/mod/go.elastic.co/apm@v1.14.0/span.go', + line: { + number: 334, + }, + module: 'go.elastic.co/apm', + function: '(*Span).End', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'clienttrace.go', + abs_path: + '/go/pkg/mod/go.elastic.co/apm/module/apmhttp@v1.14.0/clienttrace.go', + line: { + number: 130, + }, + module: 'go.elastic.co/apm/module/apmhttp', + function: 'withClientTrace.func8', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'transport.go', + abs_path: '/usr/local/go/src/net/http/transport.go', + line: { + number: 2272, + }, + function: '(*persistConn).readResponse', + module: 'net/http', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'transport.go', + abs_path: '/usr/local/go/src/net/http/transport.go', + line: { + number: 2102, + }, + function: '(*persistConn).readLoop', + module: 'net/http', + }, + { + exclude_from_grouping: false, + library_frame: true, + filename: 'asm_amd64.s', + abs_path: '/usr/local/go/src/runtime/asm_amd64.s', + line: { + number: 1581, + }, + module: 'runtime', + function: 'goexit', + }, + ], + subtype: 'http', + name: 'Request', + action: 'request', + id: '997cdcc26a60d0ad', + type: 'external', + }, + timestamp: { + us: 1634651822536408, + }, + }, + { + parent: { + id: 'b7801be83bcdc972', + }, + agent: { + name: 'go', + version: '1.14.0', + }, + destination: { + address: 'opbeans', + port: 3000, + }, + cloud: { + availability_zone: 'us-central1-c', + instance: { + name: 'gke-dev-oblt-dev-oblt-pool-18e89389-qntq', + id: '5278603844673466232', + }, + provider: 'gcp', + machine: { + type: 'n1-standard-4', + }, + project: { + name: 'elastic-observability', + id: '8560181848', + }, + region: 'us-central1', + }, + observer: { + hostname: 'apm-apm-server-65d9d8dd68-zvs6p', + id: '69a7066f-46d2-42c4-a4cc-8400f60bf2b5', + ephemeral_id: '0ab88569-c301-40e9-8e78-cac7c1dac2bc', + type: 'apm-server', + version: '7.16.0', + version_major: 7, + }, + trace: { + id: 'd5e80ae688f1fef91533f02dd2bdc769', + }, + ecs: { + version: '1.11.0', + }, + event: { + outcome: 'failure', + }, + timestamp: { + us: 1634651822536408, + }, + processor: { + name: 'transaction', + event: 'span', + }, + url: { + original: 'http://opbeans:3000/api/products/3', + }, + '@timestamp': '2021-10-19T13:57:02.539Z', + service: { + environment: 'testing', + name: 'opbeans-go', + }, + http: { + response: { + status_code: 500, + }, + }, + transaction: { + id: 'b7801be83bcdc972', + }, + span: { + duration: { + us: 13359, + }, + stacktrace: [ + { + library_frame: true, + exclude_from_grouping: false, + filename: 'span.go', + abs_path: '/go/pkg/mod/go.elastic.co/apm@v1.14.0/span.go', + line: { + number: 334, + }, + module: 'go.elastic.co/apm', + function: '(*Span).End', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'client.go', + abs_path: + '/go/pkg/mod/go.elastic.co/apm/module/apmhttp@v1.14.0/client.go', + line: { + number: 198, + }, + module: 'go.elastic.co/apm/module/apmhttp', + function: '(*responseBody).endSpan', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'client.go', + abs_path: + '/go/pkg/mod/go.elastic.co/apm/module/apmhttp@v1.14.0/client.go', + line: { + number: 187, + }, + module: 'go.elastic.co/apm/module/apmhttp', + function: '(*responseBody).Read', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'reverseproxy.go', + abs_path: '/usr/local/go/src/net/http/httputil/reverseproxy.go', + line: { + number: 461, + }, + module: 'net/http/httputil', + function: '(*ReverseProxy).copyBuffer', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'reverseproxy.go', + abs_path: '/usr/local/go/src/net/http/httputil/reverseproxy.go', + line: { + number: 449, + }, + module: 'net/http/httputil', + function: '(*ReverseProxy).copyResponse', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'reverseproxy.go', + abs_path: '/usr/local/go/src/net/http/httputil/reverseproxy.go', + line: { + number: 338, + }, + module: 'net/http/httputil', + function: '(*ReverseProxy).ServeHTTP', + }, + { + exclude_from_grouping: false, + filename: 'main.go', + abs_path: '/src/opbeans-go/main.go', + line: { + number: 196, + }, + module: 'main', + function: 'Main.func2', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'context.go', + abs_path: '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/context.go', + line: { + number: 165, + }, + module: 'github.com/gin-gonic/gin', + function: '(*Context).Next', + }, + { + exclude_from_grouping: false, + filename: 'main.go', + abs_path: '/src/opbeans-go/main.go', + line: { + number: 174, + }, + module: 'main', + function: 'Main.func1', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'context.go', + abs_path: '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/context.go', + line: { + number: 165, + }, + module: 'github.com/gin-gonic/gin', + function: '(*Context).Next', + }, + { + exclude_from_grouping: false, + filename: 'logger.go', + abs_path: '/src/opbeans-go/logger.go', + line: { + number: 36, + }, + module: 'main', + function: 'logrusMiddleware', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'context.go', + abs_path: '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/context.go', + line: { + number: 165, + }, + module: 'github.com/gin-gonic/gin', + function: '(*Context).Next', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'middleware.go', + abs_path: + '/go/pkg/mod/go.elastic.co/apm/module/apmgin@v1.14.0/middleware.go', + line: { + number: 98, + }, + function: '(*middleware).handle', + module: 'go.elastic.co/apm/module/apmgin', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'context.go', + abs_path: '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/context.go', + line: { + number: 165, + }, + module: 'github.com/gin-gonic/gin', + function: '(*Context).Next', + }, + { + library_frame: true, + exclude_from_grouping: false, + abs_path: + '/go/pkg/mod/github.com/gin-contrib/cache@v1.1.0/cache.go', + filename: 'cache.go', + line: { + number: 128, + }, + module: 'github.com/gin-contrib/cache', + function: 'Cache.func1', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'context.go', + abs_path: '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/context.go', + line: { + number: 165, + }, + function: '(*Context).Next', + module: 'github.com/gin-gonic/gin', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'gin.go', + abs_path: '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/gin.go', + line: { + number: 489, + }, + module: 'github.com/gin-gonic/gin', + function: '(*Engine).handleHTTPRequest', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'gin.go', + abs_path: '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/gin.go', + line: { + number: 445, + }, + module: 'github.com/gin-gonic/gin', + function: '(*Engine).ServeHTTP', + }, + { + library_frame: true, + exclude_from_grouping: false, + abs_path: '/usr/local/go/src/net/http/server.go', + filename: 'server.go', + line: { + number: 2878, + }, + module: 'net/http', + function: 'serverHandler.ServeHTTP', + }, + { + library_frame: true, + exclude_from_grouping: false, + abs_path: '/usr/local/go/src/net/http/server.go', + filename: 'server.go', + line: { + number: 1929, + }, + module: 'net/http', + function: '(*conn).serve', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'asm_amd64.s', + abs_path: '/usr/local/go/src/runtime/asm_amd64.s', + line: { + number: 1581, + }, + module: 'runtime', + function: 'goexit', + }, + ], + subtype: 'http', + name: 'GET opbeans:3000', + destination: { + service: { + resource: 'opbeans:3000', + name: 'http://opbeans:3000', + type: 'external', + }, + }, + http: { + response: { + status_code: 500, + }, + }, + 'http.url.original': 'http://opbeans:3000/api/products/3', + id: '35e3637e26919055', + type: 'external', + }, + }, + { + parent: { + id: '84749ec73b1268b3', + }, + agent: { + name: 'go', + version: '1.14.0', + }, + processor: { + name: 'transaction', + event: 'span', + }, + cloud: { + availability_zone: 'us-central1-c', + instance: { + name: 'gke-dev-oblt-dev-oblt-pool-18e89389-qntq', + id: '5278603844673466232', + }, + provider: 'gcp', + machine: { + type: 'n1-standard-4', + }, + project: { + name: 'elastic-observability', + id: '8560181848', + }, + region: 'us-central1', + }, + observer: { + hostname: 'apm-apm-server-65d9d8dd68-zvs6p', + id: '69a7066f-46d2-42c4-a4cc-8400f60bf2b5', + type: 'apm-server', + ephemeral_id: '0ab88569-c301-40e9-8e78-cac7c1dac2bc', + version: '7.16.0', + version_major: 7, + }, + trace: { + id: 'd5e80ae688f1fef91533f02dd2bdc769', + }, + '@timestamp': '2021-10-19T13:57:02.539Z', + ecs: { + version: '1.11.0', + }, + service: { + environment: 'testing', + name: 'opbeans-go', + }, + event: { + outcome: 'unknown', + }, + transaction: { + id: 'b7801be83bcdc972', + }, + span: { + duration: { + us: 13359, + }, + stacktrace: [ + { + library_frame: true, + exclude_from_grouping: false, + filename: 'span.go', + abs_path: '/go/pkg/mod/go.elastic.co/apm@v1.14.0/span.go', + line: { + number: 334, + }, + module: 'go.elastic.co/apm', + function: '(*Span).End', + }, + { + exclude_from_grouping: false, + library_frame: true, + filename: 'clienttrace.go', + abs_path: + '/go/pkg/mod/go.elastic.co/apm/module/apmhttp@v1.14.0/clienttrace.go', + line: { + number: 130, + }, + module: 'go.elastic.co/apm/module/apmhttp', + function: 'withClientTrace.func8', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'transport.go', + abs_path: '/usr/local/go/src/net/http/transport.go', + line: { + number: 2272, + }, + module: 'net/http', + function: '(*persistConn).readResponse', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'transport.go', + abs_path: '/usr/local/go/src/net/http/transport.go', + line: { + number: 2102, + }, + module: 'net/http', + function: '(*persistConn).readLoop', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'asm_amd64.s', + abs_path: '/usr/local/go/src/runtime/asm_amd64.s', + line: { + number: 1581, + }, + module: 'runtime', + function: 'goexit', + }, + ], + subtype: 'http', + name: 'Request', + action: 'request', + id: 'a9b4d44c3d699cbb', + type: 'external', + }, + timestamp: { + us: 1634651822536408, + }, + }, + { + parent: { + id: '2c30263c4ad8fe8b', + }, + agent: { + name: 'java', + ephemeral_id: '75e36588-9adb-4bb0-bfee-a333b1c57e67', + version: 'unknown', + }, + processor: { + name: 'transaction', + event: 'span', + }, + labels: { + productId: '3', + }, + cloud: { + availability_zone: 'us-central1-c', + instance: { + name: 'gke-dev-oblt-dev-oblt-pool-18e89389-qntq', + id: '5278603844673466232', + }, + provider: 'gcp', + machine: { + type: 'n1-standard-4', + }, + project: { + name: 'elastic-observability', + id: '8560181848', + }, + region: 'us-central1', + }, + observer: { + hostname: 'apm-apm-server-65d9d8dd68-lmf6c', + id: '7eedab18-1171-4a1b-a590-975e13fd103a', + ephemeral_id: '90034868-48e6-418c-8ab4-6616b403bca7', + type: 'apm-server', + version: '7.16.0', + version_major: 7, + }, + trace: { + id: 'd5e80ae688f1fef91533f02dd2bdc769', + }, + '@timestamp': '2021-10-19T13:57:02.540Z', + ecs: { + version: '1.11.0', + }, + service: { + environment: 'production', + name: 'opbeans-java', + }, + event: { + outcome: 'success', + }, + transaction: { + id: '2c30263c4ad8fe8b', + }, + span: { + duration: { + us: 13359, + }, + name: 'OpenTracing product span', + id: 'd22c1e48b2489017', + type: 'custom', + }, + timestamp: { + us: 1634651822536408, + }, + }, + { + parent: { + id: 'd22c1e48b2489017', + }, + agent: { + name: 'java', + ephemeral_id: '75e36588-9adb-4bb0-bfee-a333b1c57e67', + version: 'unknown', + }, + destination: { + address: 'db-postgresql', + port: 5432, + }, + processor: { + name: 'transaction', + event: 'span', + }, + cloud: { + availability_zone: 'us-central1-c', + instance: { + name: 'gke-dev-oblt-dev-oblt-pool-18e89389-qntq', + id: '5278603844673466232', + }, + provider: 'gcp', + machine: { + type: 'n1-standard-4', + }, + project: { + name: 'elastic-observability', + id: '8560181848', + }, + region: 'us-central1', + }, + observer: { + hostname: 'apm-apm-server-65d9d8dd68-lmf6c', + id: '7eedab18-1171-4a1b-a590-975e13fd103a', + type: 'apm-server', + ephemeral_id: '90034868-48e6-418c-8ab4-6616b403bca7', + version: '7.16.0', + version_major: 7, + }, + trace: { + id: 'd5e80ae688f1fef91533f02dd2bdc769', + }, + '@timestamp': '2021-10-19T13:57:02.542Z', + ecs: { + version: '1.11.0', + }, + service: { + environment: 'production', + name: 'opbeans-java', + }, + event: { + outcome: 'success', + }, + transaction: { + id: '2c30263c4ad8fe8b', + }, + timestamp: { + us: 1634651822536408, + }, + span: { + duration: { + us: 13359, + }, + subtype: 'postgresql', + destination: { + service: { + resource: 'postgresql', + }, + }, + name: 'SELECT FROM products', + action: 'query', + id: '3851260ca4365f9e', + type: 'db', + db: { + instance: 'opbeans-java', + statement: + 'select product0_.id as col_0_0_, product0_.sku as col_1_0_, product0_.name as col_2_0_, product0_.description as col_3_0_, product0_.cost as col_4_0_, product0_.selling_price as col_5_0_, product0_.stock as col_6_0_, producttyp1_.id as col_7_0_, producttyp1_.name as col_8_0_, (select sum(orderline2_.amount) from order_lines orderline2_ where orderline2_.product_id=product0_.id) as col_9_0_ from products product0_ left outer join product_types producttyp1_ on product0_.type_id=producttyp1_.id where product0_.id=?', + type: 'sql', + user: { + name: 'elastic', + }, + }, + }, + }, + { + parent: { + id: '3851260ca4365f9e', + }, + agent: { + name: 'java', + ephemeral_id: '75e36588-9adb-4bb0-bfee-a333b1c57e67', + version: 'unknown', + }, + destination: { + address: 'db-postgresql', + port: 5432, + }, + processor: { + name: 'transaction', + event: 'span', + }, + cloud: { + availability_zone: 'us-central1-c', + instance: { + name: 'gke-dev-oblt-dev-oblt-pool-18e89389-qntq', + id: '5278603844673466232', + }, + provider: 'gcp', + machine: { + type: 'n1-standard-4', + }, + project: { + name: 'elastic-observability', + id: '8560181848', + }, + region: 'us-central1', + }, + observer: { + hostname: 'apm-apm-server-65d9d8dd68-lmf6c', + id: '7eedab18-1171-4a1b-a590-975e13fd103a', + type: 'apm-server', + ephemeral_id: '90034868-48e6-418c-8ab4-6616b403bca7', + version: '7.16.0', + version_major: 7, + }, + trace: { + id: 'd5e80ae688f1fef91533f02dd2bdc769', + }, + '@timestamp': '2021-10-19T13:57:02.541Z', + ecs: { + version: '1.11.0', + }, + service: { + environment: 'production', + name: 'opbeans-java', + }, + event: { + outcome: 'success', + }, + transaction: { + id: '2c30263c4ad8fe8b', + }, + span: { + duration: { + us: 13359, + }, + subtype: 'postgresql', + destination: { + service: { + resource: 'postgresql', + }, + }, + name: 'empty query', + action: 'query', + id: '86c43ac014573747', + type: 'db', + db: { + rows_affected: 0, + instance: 'opbeans-java', + statement: '(empty query)', + type: 'sql', + user: { + name: 'elastic', + }, + }, + }, + timestamp: { + us: 1634651822536408, + }, + }, + { + parent: { + id: '997cdcc26a60d0ad', + }, + agent: { + name: 'go', + version: '1.14.0', + }, + processor: { + name: 'transaction', + event: 'span', + }, + cloud: { + availability_zone: 'us-central1-c', + instance: { + name: 'gke-dev-oblt-dev-oblt-pool-18e89389-qntq', + id: '5278603844673466232', + }, + provider: 'gcp', + machine: { + type: 'n1-standard-4', + }, + project: { + name: 'elastic-observability', + id: '8560181848', + }, + region: 'us-central1', + }, + observer: { + hostname: 'apm-apm-server-65d9d8dd68-lmf6c', + id: '7eedab18-1171-4a1b-a590-975e13fd103a', + type: 'apm-server', + ephemeral_id: '90034868-48e6-418c-8ab4-6616b403bca7', + version: '7.16.0', + version_major: 7, + }, + trace: { + id: 'd5e80ae688f1fef91533f02dd2bdc769', + }, + '@timestamp': '2021-10-19T13:57:02.548Z', + ecs: { + version: '1.11.0', + }, + service: { + environment: 'testing', + name: 'opbeans-go', + }, + event: { + outcome: 'unknown', + }, + transaction: { + id: '9f50f43e924d0b46', + }, + timestamp: { + us: 1634651822536408, + }, + span: { + duration: { + us: 13359, + }, + subtype: 'http', + name: 'Response', + action: 'response', + id: '84749ec73b1268b3', + type: 'external', + }, + }, + { + parent: { + id: 'a9b4d44c3d699cbb', + }, + agent: { + name: 'go', + version: '1.14.0', + }, + processor: { + name: 'transaction', + event: 'span', + }, + cloud: { + availability_zone: 'us-central1-c', + instance: { + name: 'gke-dev-oblt-dev-oblt-pool-18e89389-qntq', + id: '5278603844673466232', + }, + provider: 'gcp', + machine: { + type: 'n1-standard-4', + }, + project: { + name: 'elastic-observability', + id: '8560181848', + }, + region: 'us-central1', + }, + observer: { + hostname: 'apm-apm-server-65d9d8dd68-zvs6p', + id: '69a7066f-46d2-42c4-a4cc-8400f60bf2b5', + type: 'apm-server', + ephemeral_id: '0ab88569-c301-40e9-8e78-cac7c1dac2bc', + version: '7.16.0', + version_major: 7, + }, + trace: { + id: 'd5e80ae688f1fef91533f02dd2bdc769', + }, + '@timestamp': '2021-10-19T13:57:02.547Z', + ecs: { + version: '1.11.0', + }, + service: { + environment: 'testing', + name: 'opbeans-go', + }, + event: { + outcome: 'unknown', + }, + transaction: { + id: 'b7801be83bcdc972', + }, + span: { + duration: { + us: 13359, + }, + subtype: 'http', + name: 'Response', + action: 'response', + id: '04991f3b9d3696c5', + type: 'external', + }, + timestamp: { + us: 1634651822536408, + }, + }, + ], + errorDocs: [ + { + container: { + id: '59036ecb70908dfec4e03edc477f6875d08677871b4af0db3144373802d00cb1', + }, + kubernetes: { + pod: { + uid: '878bab2a-1309-44ae-a0e2-c98a0b187da1', + name: 'opbeans-java-5f45d77dd8-h8bnb', + }, + }, + parent: { + id: '2c30263c4ad8fe8b', + }, + agent: { + name: 'java', + ephemeral_id: '75e36588-9adb-4bb0-bfee-a333b1c57e67', + version: 'unknown', + }, + source: { + ip: '10.12.0.13', + }, + error: { + exception: [ + { + stacktrace: [ + { + exclude_from_grouping: false, + library_frame: true, + filename: 'AbstractMessageConverterMethodProcessor.java', + classname: + 'org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor', + line: { + number: 226, + }, + module: 'org.springframework.web.servlet.mvc.method.annotation', + function: 'writeWithMessageConverters', + }, + { + exclude_from_grouping: false, + library_frame: true, + filename: 'RequestResponseBodyMethodProcessor.java', + classname: + 'org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor', + line: { + number: 180, + }, + module: 'org.springframework.web.servlet.mvc.method.annotation', + function: 'handleReturnValue', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'HandlerMethodReturnValueHandlerComposite.java', + classname: + 'org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite', + line: { + number: 82, + }, + module: 'org.springframework.web.method.support', + function: 'handleReturnValue', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'ServletInvocableHandlerMethod.java', + classname: + 'org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod', + line: { + number: 119, + }, + module: 'org.springframework.web.servlet.mvc.method.annotation', + function: 'invokeAndHandle', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'RequestMappingHandlerAdapter.java', + classname: + 'org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter', + line: { + number: 877, + }, + function: 'invokeHandlerMethod', + module: 'org.springframework.web.servlet.mvc.method.annotation', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'RequestMappingHandlerAdapter.java', + classname: + 'org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter', + line: { + number: 783, + }, + module: 'org.springframework.web.servlet.mvc.method.annotation', + function: 'handleInternal', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'AbstractHandlerMethodAdapter.java', + classname: + 'org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter', + line: { + number: 87, + }, + module: 'org.springframework.web.servlet.mvc.method', + function: 'handle', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'DispatcherServlet.java', + classname: 'org.springframework.web.servlet.DispatcherServlet', + line: { + number: 991, + }, + function: 'doDispatch', + module: 'org.springframework.web.servlet', + }, + { + exclude_from_grouping: false, + library_frame: true, + filename: 'DispatcherServlet.java', + classname: 'org.springframework.web.servlet.DispatcherServlet', + line: { + number: 925, + }, + module: 'org.springframework.web.servlet', + function: 'doService', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'FrameworkServlet.java', + classname: 'org.springframework.web.servlet.FrameworkServlet', + line: { + number: 974, + }, + module: 'org.springframework.web.servlet', + function: 'processRequest', + }, + { + exclude_from_grouping: false, + library_frame: true, + filename: 'FrameworkServlet.java', + classname: 'org.springframework.web.servlet.FrameworkServlet', + line: { + number: 866, + }, + module: 'org.springframework.web.servlet', + function: 'doGet', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'HttpServlet.java', + classname: 'javax.servlet.http.HttpServlet', + line: { + number: 635, + }, + module: 'javax.servlet.http', + function: 'service', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'FrameworkServlet.java', + classname: 'org.springframework.web.servlet.FrameworkServlet', + line: { + number: 851, + }, + function: 'service', + module: 'org.springframework.web.servlet', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'HttpServlet.java', + classname: 'javax.servlet.http.HttpServlet', + line: { + number: 742, + }, + module: 'javax.servlet.http', + function: 'service', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'ApplicationFilterChain.java', + classname: 'org.apache.catalina.core.ApplicationFilterChain', + line: { + number: 231, + }, + module: 'org.apache.catalina.core', + function: 'internalDoFilter', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'ApplicationFilterChain.java', + classname: 'org.apache.catalina.core.ApplicationFilterChain', + line: { + number: 166, + }, + module: 'org.apache.catalina.core', + function: 'doFilter', + }, + { + exclude_from_grouping: false, + library_frame: true, + filename: 'WsFilter.java', + classname: 'org.apache.tomcat.websocket.server.WsFilter', + line: { + number: 52, + }, + module: 'org.apache.tomcat.websocket.server', + function: 'doFilter', + }, + { + exclude_from_grouping: false, + library_frame: true, + filename: 'ApplicationFilterChain.java', + classname: 'org.apache.catalina.core.ApplicationFilterChain', + line: { + number: 193, + }, + module: 'org.apache.catalina.core', + function: 'internalDoFilter', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'ApplicationFilterChain.java', + classname: 'org.apache.catalina.core.ApplicationFilterChain', + line: { + number: 166, + }, + module: 'org.apache.catalina.core', + function: 'doFilter', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'RequestContextFilter.java', + classname: + 'org.springframework.web.filter.RequestContextFilter', + line: { + number: 99, + }, + function: 'doFilterInternal', + module: 'org.springframework.web.filter', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'OncePerRequestFilter.java', + classname: + 'org.springframework.web.filter.OncePerRequestFilter', + line: { + number: 107, + }, + module: 'org.springframework.web.filter', + function: 'doFilter', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'ApplicationFilterChain.java', + classname: 'org.apache.catalina.core.ApplicationFilterChain', + line: { + number: 193, + }, + module: 'org.apache.catalina.core', + function: 'internalDoFilter', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'ApplicationFilterChain.java', + classname: 'org.apache.catalina.core.ApplicationFilterChain', + line: { + number: 166, + }, + module: 'org.apache.catalina.core', + function: 'doFilter', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'HttpPutFormContentFilter.java', + classname: + 'org.springframework.web.filter.HttpPutFormContentFilter', + line: { + number: 109, + }, + function: 'doFilterInternal', + module: 'org.springframework.web.filter', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'OncePerRequestFilter.java', + classname: + 'org.springframework.web.filter.OncePerRequestFilter', + line: { + number: 107, + }, + module: 'org.springframework.web.filter', + function: 'doFilter', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'ApplicationFilterChain.java', + classname: 'org.apache.catalina.core.ApplicationFilterChain', + line: { + number: 193, + }, + module: 'org.apache.catalina.core', + function: 'internalDoFilter', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'ApplicationFilterChain.java', + classname: 'org.apache.catalina.core.ApplicationFilterChain', + line: { + number: 166, + }, + module: 'org.apache.catalina.core', + function: 'doFilter', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'HiddenHttpMethodFilter.java', + classname: + 'org.springframework.web.filter.HiddenHttpMethodFilter', + line: { + number: 81, + }, + function: 'doFilterInternal', + module: 'org.springframework.web.filter', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'OncePerRequestFilter.java', + classname: + 'org.springframework.web.filter.OncePerRequestFilter', + line: { + number: 107, + }, + module: 'org.springframework.web.filter', + function: 'doFilter', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'ApplicationFilterChain.java', + classname: 'org.apache.catalina.core.ApplicationFilterChain', + line: { + number: 193, + }, + module: 'org.apache.catalina.core', + function: 'internalDoFilter', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'ApplicationFilterChain.java', + classname: 'org.apache.catalina.core.ApplicationFilterChain', + line: { + number: 166, + }, + module: 'org.apache.catalina.core', + function: 'doFilter', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'CharacterEncodingFilter.java', + classname: + 'org.springframework.web.filter.CharacterEncodingFilter', + line: { + number: 200, + }, + module: 'org.springframework.web.filter', + function: 'doFilterInternal', + }, + { + exclude_from_grouping: false, + library_frame: true, + filename: 'OncePerRequestFilter.java', + classname: + 'org.springframework.web.filter.OncePerRequestFilter', + line: { + number: 107, + }, + module: 'org.springframework.web.filter', + function: 'doFilter', + }, + { + exclude_from_grouping: false, + library_frame: true, + filename: 'ApplicationFilterChain.java', + classname: 'org.apache.catalina.core.ApplicationFilterChain', + line: { + number: 193, + }, + module: 'org.apache.catalina.core', + function: 'internalDoFilter', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'ApplicationFilterChain.java', + classname: 'org.apache.catalina.core.ApplicationFilterChain', + line: { + number: 166, + }, + module: 'org.apache.catalina.core', + function: 'doFilter', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'StandardWrapperValve.java', + classname: 'org.apache.catalina.core.StandardWrapperValve', + line: { + number: 198, + }, + module: 'org.apache.catalina.core', + function: 'invoke', + }, + { + exclude_from_grouping: false, + library_frame: true, + filename: 'StandardContextValve.java', + classname: 'org.apache.catalina.core.StandardContextValve', + line: { + number: 96, + }, + module: 'org.apache.catalina.core', + function: 'invoke', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'AuthenticatorBase.java', + classname: + 'org.apache.catalina.authenticator.AuthenticatorBase', + line: { + number: 496, + }, + module: 'org.apache.catalina.authenticator', + function: 'invoke', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'StandardHostValve.java', + classname: 'org.apache.catalina.core.StandardHostValve', + line: { + number: 140, + }, + module: 'org.apache.catalina.core', + function: 'invoke', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'ErrorReportValve.java', + classname: 'org.apache.catalina.valves.ErrorReportValve', + line: { + number: 81, + }, + module: 'org.apache.catalina.valves', + function: 'invoke', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'StandardEngineValve.java', + classname: 'org.apache.catalina.core.StandardEngineValve', + line: { + number: 87, + }, + module: 'org.apache.catalina.core', + function: 'invoke', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'CoyoteAdapter.java', + classname: 'org.apache.catalina.connector.CoyoteAdapter', + line: { + number: 342, + }, + module: 'org.apache.catalina.connector', + function: 'service', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'Http11Processor.java', + classname: 'org.apache.coyote.http11.Http11Processor', + line: { + number: 803, + }, + module: 'org.apache.coyote.http11', + function: 'service', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'AbstractProcessorLight.java', + classname: 'org.apache.coyote.AbstractProcessorLight', + line: { + number: 66, + }, + module: 'org.apache.coyote', + function: 'process', + }, + { + exclude_from_grouping: false, + library_frame: true, + filename: 'AbstractProtocol.java', + classname: + 'org.apache.coyote.AbstractProtocol$ConnectionHandler', + line: { + number: 790, + }, + module: 'org.apache.coyote', + function: 'process', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'NioEndpoint.java', + classname: + 'org.apache.tomcat.util.net.NioEndpoint$SocketProcessor', + line: { + number: 1468, + }, + module: 'org.apache.tomcat.util.net', + function: 'doRun', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'SocketProcessorBase.java', + classname: 'org.apache.tomcat.util.net.SocketProcessorBase', + line: { + number: 49, + }, + module: 'org.apache.tomcat.util.net', + function: 'run', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'TaskThread.java', + classname: + 'org.apache.tomcat.util.threads.TaskThread$WrappingRunnable', + line: { + number: 61, + }, + module: 'org.apache.tomcat.util.threads', + function: 'run', + }, + ], + message: + 'No converter found for return value of type: class com.sun.proxy.$Proxy158', + type: 'org.springframework.http.converter.HttpMessageNotWritableException', + }, + ], + id: '128f8ecf47bc8a800269ee6e5ac90008', + grouping_key: 'cc9272d7511c88a533ac41cc3e2ce54b', + grouping_name: + 'No converter found for return value of type: class com.sun.proxy.$Proxy158', + }, + cloud: { + availability_zone: 'us-central1-c', + instance: { + name: 'gke-dev-oblt-dev-oblt-pool-18e89389-qntq', + id: '5278603844673466232', + }, + provider: 'gcp', + machine: { + type: 'n1-standard-4', + }, + project: { + name: 'elastic-observability', + id: '8560181848', + }, + region: 'us-central1', + }, + observer: { + hostname: 'apm-apm-server-65d9d8dd68-lmf6c', + id: '7eedab18-1171-4a1b-a590-975e13fd103a', + ephemeral_id: '90034868-48e6-418c-8ab4-6616b403bca7', + type: 'apm-server', + version: '7.16.0', + version_major: 7, + }, + trace: { + id: 'd5e80ae688f1fef91533f02dd2bdc769', + }, + ecs: { + version: '1.11.0', + }, + host: { + os: { + platform: 'Linux', + }, + ip: '10.12.0.15', + architecture: 'amd64', + }, + client: { + ip: '10.12.0.22', + }, + event: { + ingested: '2021-10-19T13:57:10.382394342Z', + }, + user_agent: { + original: 'http.rb/5.0.2', + name: 'Other', + device: { + name: 'Generic Feature Phone', + type: 'Other', + }, + }, + timestamp: { + us: 1634651822536408, + }, + process: { + pid: 7, + title: '/opt/java/openjdk/bin/java', + ppid: 1, + }, + message: + 'No converter found for return value of type: class com.sun.proxy.$Proxy158', + processor: { + name: 'error', + event: 'error', + }, + url: { + path: '/api/products/3', + scheme: 'http', + port: 3000, + domain: 'opbeans', + full: 'http://opbeans:3000/api/products/3', + }, + '@timestamp': '2021-10-19T13:57:02.546Z', + service: { + node: { + name: '59036ecb70908dfec4e03edc477f6875d08677871b4af0db3144373802d00cb1', + }, + environment: 'production', + name: 'opbeans-java', + runtime: { + name: 'Java', + version: '11.0.11', + }, + language: { + name: 'Java', + version: '11.0.11', + }, + version: '2021-10-14 17:49:52', + }, + http: { + request: { + headers: { + 'User-Agent': ['http.rb/5.0.2'], + 'X-Forwarded-For': ['10.12.0.22, 10.12.0.14'], + Host: ['opbeans:3000'], + 'Accept-Encoding': ['gzip'], + 'Elastic-Apm-Traceparent': [ + '00-d5e80ae688f1fef91533f02dd2bdc769-35e3637e26919055-01', + ], + Tracestate: ['es=s:1.0'], + Traceparent: [ + '00-d5e80ae688f1fef91533f02dd2bdc769-35e3637e26919055-01', + ], + }, + method: 'GET', + }, + response: { + status_code: 500, + finished: true, + headers_sent: true, + }, + version: '1.1', + }, + transaction: { + id: '2c30263c4ad8fe8b', + type: 'request', + sampled: true, + }, + }, + { + container: { + id: 'e7b69f99cb7523bedea6d7c97b684cf4b7ff458d0cba1efb1ac843300b3bf3c7', + }, + kubernetes: { + pod: { + uid: 'c5169b50-f3b3-4693-8e4b-150fca17c333', + name: 'opbeans-go-5d795ddf6b-rhlvf', + }, + }, + parent: { + id: '9f50f43e924d0b46', + }, + agent: { + name: 'go', + version: '1.14.0', + }, + process: { + args: [ + '/opbeans-go', + '-log-level=debug', + '-log-json', + '-listen=:3000', + '-frontend=/opbeans-frontend', + '-db=postgres:', + '-cache=redis://redis-master:6379', + ], + pid: 1, + title: 'opbeans-go', + ppid: 0, + }, + error: { + culprit: 'logrusMiddleware', + log: { + stacktrace: [ + { + library_frame: true, + exclude_from_grouping: false, + filename: 'hook.go', + abs_path: + '/go/pkg/mod/go.elastic.co/apm/module/apmlogrus@v1.14.0/hook.go', + line: { + number: 102, + }, + module: 'go.elastic.co/apm/module/apmlogrus', + function: '(*Hook).Fire', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'hooks.go', + abs_path: + '/go/pkg/mod/github.com/sirupsen/logrus@v1.8.1/hooks.go', + line: { + number: 28, + }, + module: 'github.com/sirupsen/logrus', + function: 'LevelHooks.Fire', + }, + { + library_frame: true, + exclude_from_grouping: false, + abs_path: + '/go/pkg/mod/github.com/sirupsen/logrus@v1.8.1/entry.go', + filename: 'entry.go', + line: { + number: 272, + }, + module: 'github.com/sirupsen/logrus', + function: '(*Entry).fireHooks', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'entry.go', + abs_path: + '/go/pkg/mod/github.com/sirupsen/logrus@v1.8.1/entry.go', + line: { + number: 241, + }, + module: 'github.com/sirupsen/logrus', + function: '(*Entry).log', + }, + { + exclude_from_grouping: false, + library_frame: true, + filename: 'entry.go', + abs_path: + '/go/pkg/mod/github.com/sirupsen/logrus@v1.8.1/entry.go', + line: { + number: 293, + }, + module: 'github.com/sirupsen/logrus', + function: '(*Entry).Log', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'entry.go', + abs_path: + '/go/pkg/mod/github.com/sirupsen/logrus@v1.8.1/entry.go', + line: { + number: 338, + }, + module: 'github.com/sirupsen/logrus', + function: '(*Entry).Logf', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'entry.go', + abs_path: + '/go/pkg/mod/github.com/sirupsen/logrus@v1.8.1/entry.go', + line: { + number: 367, + }, + function: '(*Entry).Errorf', + module: 'github.com/sirupsen/logrus', + }, + { + exclude_from_grouping: false, + filename: 'logger.go', + abs_path: '/src/opbeans-go/logger.go', + line: { + number: 56, + }, + module: 'main', + function: 'logrusMiddleware', + }, + { + library_frame: true, + exclude_from_grouping: false, + abs_path: + '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/context.go', + filename: 'context.go', + line: { + number: 165, + }, + module: 'github.com/gin-gonic/gin', + function: '(*Context).Next', + }, + { + library_frame: true, + exclude_from_grouping: false, + abs_path: + '/go/pkg/mod/go.elastic.co/apm/module/apmgin@v1.14.0/middleware.go', + filename: 'middleware.go', + line: { + number: 98, + }, + module: 'go.elastic.co/apm/module/apmgin', + function: '(*middleware).handle', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'context.go', + abs_path: + '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/context.go', + line: { + number: 165, + }, + function: '(*Context).Next', + module: 'github.com/gin-gonic/gin', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'cache.go', + abs_path: + '/go/pkg/mod/github.com/gin-contrib/cache@v1.1.0/cache.go', + line: { + number: 128, + }, + module: 'github.com/gin-contrib/cache', + function: 'Cache.func1', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'context.go', + abs_path: + '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/context.go', + line: { + number: 165, + }, + module: 'github.com/gin-gonic/gin', + function: '(*Context).Next', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'gin.go', + abs_path: '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/gin.go', + line: { + number: 489, + }, + module: 'github.com/gin-gonic/gin', + function: '(*Engine).handleHTTPRequest', + }, + { + library_frame: true, + exclude_from_grouping: false, + abs_path: '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/gin.go', + filename: 'gin.go', + line: { + number: 445, + }, + module: 'github.com/gin-gonic/gin', + function: '(*Engine).ServeHTTP', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'server.go', + abs_path: '/usr/local/go/src/net/http/server.go', + line: { + number: 2878, + }, + module: 'net/http', + function: 'serverHandler.ServeHTTP', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'server.go', + abs_path: '/usr/local/go/src/net/http/server.go', + line: { + number: 1929, + }, + module: 'net/http', + function: '(*conn).serve', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'asm_amd64.s', + abs_path: '/usr/local/go/src/runtime/asm_amd64.s', + line: { + number: 1581, + }, + module: 'runtime', + function: 'goexit', + }, + ], + level: 'error', + message: 'GET /api/products/3 (500)', + }, + id: '1660f82c1340f415e9a31b47565300ee', + grouping_key: '7a640436a9be648fd708703d1ac84650', + grouping_name: 'GET /api/products/3 (500)', + }, + message: 'GET /api/products/3 (500)', + processor: { + name: 'error', + event: 'error', + }, + cloud: { + availability_zone: 'us-central1-c', + instance: { + name: 'gke-dev-oblt-dev-oblt-pool-18e89389-qntq', + id: '5278603844673466232', + }, + provider: 'gcp', + machine: { + type: 'n1-standard-4', + }, + project: { + name: 'elastic-observability', + id: '8560181848', + }, + region: 'us-central1', + }, + observer: { + hostname: 'apm-apm-server-65d9d8dd68-lmf6c', + id: '7eedab18-1171-4a1b-a590-975e13fd103a', + ephemeral_id: '90034868-48e6-418c-8ab4-6616b403bca7', + type: 'apm-server', + version: '7.16.0', + version_major: 7, + }, + trace: { + id: 'd5e80ae688f1fef91533f02dd2bdc769', + }, + '@timestamp': '2021-10-19T13:57:02.539Z', + ecs: { + version: '1.11.0', + }, + service: { + node: { + name: 'e7b69f99cb7523bedea6d7c97b684cf4b7ff458d0cba1efb1ac843300b3bf3c7', + }, + environment: 'testing', + name: 'opbeans-go', + runtime: { + name: 'gc', + version: 'go1.17.2', + }, + language: { + name: 'go', + version: 'go1.17.2', + }, + version: '2021-10-14 17:49:50', + }, + host: { + os: { + platform: 'linux', + }, + ip: '10.12.0.14', + architecture: 'amd64', + }, + event: { + ingested: '2021-10-19T13:57:05.412811279Z', + }, + transaction: { + id: '9f50f43e924d0b46', + }, + timestamp: { + us: 1634651822536408, + }, + }, + { + container: { + id: '015d1127421e2c3d42a0fb031fc75e989813f58973143b6c7e33dca6ccc6f31b', + }, + kubernetes: { + pod: { + uid: '459a6abf-198e-4107-b4dd-b0ae826755ab', + name: 'opbeans-go-nsn-69b89c4598-xsvgh', + }, + }, + parent: { + id: 'b7801be83bcdc972', + }, + process: { + args: [ + '/opbeans-go', + '-log-level=debug', + '-log-json', + '-listen=:3000', + '-frontend=/opbeans-frontend', + '-db=postgres:', + '-cache=redis://redis-master:6379', + ], + pid: 1, + title: 'opbeans-go', + ppid: 0, + }, + agent: { + name: 'go', + version: '1.14.0', + }, + message: 'GET /api/products/3 (500)', + error: { + culprit: 'logrusMiddleware', + log: { + stacktrace: [ + { + library_frame: true, + exclude_from_grouping: false, + filename: 'hook.go', + abs_path: + '/go/pkg/mod/go.elastic.co/apm/module/apmlogrus@v1.14.0/hook.go', + line: { + number: 102, + }, + module: 'go.elastic.co/apm/module/apmlogrus', + function: '(*Hook).Fire', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'hooks.go', + abs_path: + '/go/pkg/mod/github.com/sirupsen/logrus@v1.8.1/hooks.go', + line: { + number: 28, + }, + function: 'LevelHooks.Fire', + module: 'github.com/sirupsen/logrus', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'entry.go', + abs_path: + '/go/pkg/mod/github.com/sirupsen/logrus@v1.8.1/entry.go', + line: { + number: 272, + }, + module: 'github.com/sirupsen/logrus', + function: '(*Entry).fireHooks', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'entry.go', + abs_path: + '/go/pkg/mod/github.com/sirupsen/logrus@v1.8.1/entry.go', + line: { + number: 241, + }, + module: 'github.com/sirupsen/logrus', + function: '(*Entry).log', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'entry.go', + abs_path: + '/go/pkg/mod/github.com/sirupsen/logrus@v1.8.1/entry.go', + line: { + number: 293, + }, + function: '(*Entry).Log', + module: 'github.com/sirupsen/logrus', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'entry.go', + abs_path: + '/go/pkg/mod/github.com/sirupsen/logrus@v1.8.1/entry.go', + line: { + number: 338, + }, + module: 'github.com/sirupsen/logrus', + function: '(*Entry).Logf', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'entry.go', + abs_path: + '/go/pkg/mod/github.com/sirupsen/logrus@v1.8.1/entry.go', + line: { + number: 367, + }, + module: 'github.com/sirupsen/logrus', + function: '(*Entry).Errorf', + }, + { + exclude_from_grouping: false, + filename: 'logger.go', + abs_path: '/src/opbeans-go/logger.go', + line: { + number: 56, + }, + module: 'main', + function: 'logrusMiddleware', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'context.go', + abs_path: + '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/context.go', + line: { + number: 165, + }, + module: 'github.com/gin-gonic/gin', + function: '(*Context).Next', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'middleware.go', + abs_path: + '/go/pkg/mod/go.elastic.co/apm/module/apmgin@v1.14.0/middleware.go', + line: { + number: 98, + }, + module: 'go.elastic.co/apm/module/apmgin', + function: '(*middleware).handle', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'context.go', + abs_path: + '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/context.go', + line: { + number: 165, + }, + module: 'github.com/gin-gonic/gin', + function: '(*Context).Next', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'cache.go', + abs_path: + '/go/pkg/mod/github.com/gin-contrib/cache@v1.1.0/cache.go', + line: { + number: 128, + }, + module: 'github.com/gin-contrib/cache', + function: 'Cache.func1', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'context.go', + abs_path: + '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/context.go', + line: { + number: 165, + }, + module: 'github.com/gin-gonic/gin', + function: '(*Context).Next', + }, + { + library_frame: true, + exclude_from_grouping: false, + abs_path: '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/gin.go', + filename: 'gin.go', + line: { + number: 489, + }, + module: 'github.com/gin-gonic/gin', + function: '(*Engine).handleHTTPRequest', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'gin.go', + abs_path: '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/gin.go', + line: { + number: 445, + }, + module: 'github.com/gin-gonic/gin', + function: '(*Engine).ServeHTTP', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'server.go', + abs_path: '/usr/local/go/src/net/http/server.go', + line: { + number: 2878, + }, + module: 'net/http', + function: 'serverHandler.ServeHTTP', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'server.go', + abs_path: '/usr/local/go/src/net/http/server.go', + line: { + number: 1929, + }, + module: 'net/http', + function: '(*conn).serve', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'asm_amd64.s', + abs_path: '/usr/local/go/src/runtime/asm_amd64.s', + line: { + number: 1581, + }, + function: 'goexit', + module: 'runtime', + }, + ], + level: 'error', + message: 'GET /api/products/3 (500)', + }, + id: '7a265a869ad88851591e0e9734aa4a70', + grouping_key: '7a640436a9be648fd708703d1ac84650', + grouping_name: 'GET /api/products/3 (500)', + }, + processor: { + name: 'error', + event: 'error', + }, + cloud: { + availability_zone: 'us-central1-c', + instance: { + name: 'gke-dev-oblt-dev-oblt-pool-18e89389-qntq', + id: '5278603844673466232', + }, + provider: 'gcp', + machine: { + type: 'n1-standard-4', + }, + project: { + name: 'elastic-observability', + id: '8560181848', + }, + region: 'us-central1', + }, + observer: { + hostname: 'apm-apm-server-65d9d8dd68-zvs6p', + id: '69a7066f-46d2-42c4-a4cc-8400f60bf2b5', + ephemeral_id: '0ab88569-c301-40e9-8e78-cac7c1dac2bc', + type: 'apm-server', + version: '7.16.0', + version_major: 7, + }, + trace: { + id: 'd5e80ae688f1fef91533f02dd2bdc769', + }, + '@timestamp': '2021-10-19T13:57:02.539Z', + ecs: { + version: '1.11.0', + }, + service: { + node: { + name: '015d1127421e2c3d42a0fb031fc75e989813f58973143b6c7e33dca6ccc6f31b', + }, + environment: 'testing', + name: 'opbeans-go', + runtime: { + name: 'gc', + version: 'go1.17.2', + }, + language: { + name: 'go', + version: 'go1.17.2', + }, + version: '2021-10-14 17:49:50', + }, + host: { + os: { + platform: 'linux', + }, + ip: '10.12.0.13', + architecture: 'amd64', + }, + event: { + ingested: '2021-10-19T13:57:08.266888578Z', + }, + transaction: { + id: 'b7801be83bcdc972', + }, + timestamp: { + us: 1634651822536408, + }, + }, + ], +} as TraceAPIResponse; + +export const traceWithErrors = { + traceDocs: [ + { + container: { + id: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', + }, + process: { + pid: 6, + title: '/usr/lib/jvm/java-10-openjdk-amd64/bin/java', + ppid: 1, + }, + agent: { + name: 'java', + ephemeral_id: '99ce8403-5875-4945-b074-d37dc10563eb', + version: '1.14.1-SNAPSHOT', + }, + internal: { + sampler: { + value: 46, + }, + }, + source: { + ip: '172.19.0.13', + }, + processor: { + name: 'transaction', + event: 'transaction', + }, + url: { + path: '/api/orders', + scheme: 'http', + port: 3000, + domain: '172.19.0.9', + full: 'http://172.19.0.9:3000/api/orders', + }, + observer: { + hostname: 'f37f48d8b60b', + id: 'd8522e1f-be8e-43c2-b290-ac6b6c0f171e', + ephemeral_id: '6ed88f14-170e-478d-a4f5-ea5e7f4b16b9', + type: 'apm-server', + version: '8.0.0', + version_major: 8, + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322', + }, + '@timestamp': '2020-03-23T15:04:28.785Z', + ecs: { + version: '1.4.0', + }, + service: { + node: { + name: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', + }, + environment: 'production', + name: 'opbeans-java', + runtime: { + name: 'Java', + version: '10.0.2', + }, + language: { + name: 'Java', + version: '10.0.2', + }, + version: 'None', + }, + host: { + hostname: '4cf84d094553', + os: { + platform: 'Linux', + }, + ip: '172.19.0.9', + name: '4cf84d094553', + architecture: 'amd64', + }, + http: { + request: { + headers: { + Accept: ['*/*'], + 'User-Agent': ['Python/3.7 aiohttp/3.3.2'], + Host: ['172.19.0.9:3000'], + 'Accept-Encoding': ['gzip, deflate'], + }, + method: 'get', + socket: { + encrypted: false, + remote_address: '172.19.0.13', + }, + body: { + original: '[REDACTED]', + }, + }, + response: { + headers: { + 'Transfer-Encoding': ['chunked'], + Date: ['Mon, 23 Mar 2020 15:04:28 GMT'], + 'Content-Type': ['application/json;charset=ISO-8859-1'], + }, + status_code: 200, + finished: true, + headers_sent: true, + }, + version: '1.1', + }, + client: { + ip: '172.19.0.13', + }, + transaction: { + duration: { + us: 18842, + }, + result: 'HTTP 2xx', + name: 'DispatcherServlet#doGet', + id: '49809ad3c26adf74', + span_count: { + dropped: 0, + started: 1, + }, + type: 'request', + sampled: true, + }, + user_agent: { + original: 'Python/3.7 aiohttp/3.3.2', + name: 'Other', + device: { + name: 'Other', + }, + }, + timestamp: { + us: 1584975868785000, + }, + }, + { + parent: { + id: 'fc107f7b556eb49b', + }, + agent: { + name: 'go', + version: '1.7.2', + }, + processor: { + name: 'transaction', + event: 'transaction', + }, + url: { + path: '/api/orders', + scheme: 'http', + port: 3000, + domain: 'opbeans-go', + full: 'http://opbeans-go:3000/api/orders', + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322', + }, + '@timestamp': '2020-03-23T15:04:28.787Z', + service: { + node: { + name: 'e948a08b8f5efe99b5da01f50da48c7d8aee3bbf4701f3da85ebe760c2ffef29', + }, + environment: 'production', + framework: { + name: 'gin', + version: 'v1.4.0', + }, + name: 'opbeans-go', + runtime: { + name: 'gc', + version: 'go1.14.1', + }, + language: { + name: 'go', + version: 'go1.14.1', + }, + version: 'None', + }, + transaction: { + duration: { + us: 16597, + }, + result: 'HTTP 2xx', + name: 'GET /api/orders', + id: '975c8d5bfd1dd20b', + span_count: { + dropped: 0, + started: 1, + }, + type: 'request', + sampled: true, + }, + timestamp: { + us: 1584975868787052, + }, + }, + { + parent: { + id: 'daae24d83c269918', + }, + agent: { + name: 'python', + version: '5.5.2', + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322', + }, + timestamp: { + us: 1584975868788603, + }, + processor: { + name: 'transaction', + event: 'transaction', + }, + url: { + path: '/api/orders', + scheme: 'http', + port: 3000, + domain: 'opbeans-go', + full: 'http://opbeans-go:3000/api/orders', + }, + '@timestamp': '2020-03-23T15:04:28.788Z', + service: { + node: { + name: 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51', + }, + environment: 'production', + framework: { + name: 'django', + version: '2.1.13', + }, + name: 'opbeans-python', + runtime: { + name: 'CPython', + version: '3.6.10', + }, + language: { + name: 'python', + version: '3.6.10', + }, + version: 'None', + }, + transaction: { + result: 'HTTP 2xx', + duration: { + us: 14648, + }, + name: 'GET opbeans.views.orders', + span_count: { + dropped: 0, + started: 1, + }, + id: '6fb0ff7365b87298', + type: 'request', + sampled: true, + }, + }, + { + container: { + id: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', + }, + parent: { + id: '49809ad3c26adf74', + }, + process: { + pid: 6, + title: '/usr/lib/jvm/java-10-openjdk-amd64/bin/java', + ppid: 1, + }, + agent: { + name: 'java', + ephemeral_id: '99ce8403-5875-4945-b074-d37dc10563eb', + version: '1.14.1-SNAPSHOT', + }, + internal: { + sampler: { + value: 44, + }, + }, + destination: { + address: 'opbeans-go', + port: 3000, + }, + processor: { + name: 'transaction', + event: 'span', + }, + observer: { + hostname: 'f37f48d8b60b', + id: 'd8522e1f-be8e-43c2-b290-ac6b6c0f171e', + type: 'apm-server', + ephemeral_id: '6ed88f14-170e-478d-a4f5-ea5e7f4b16b9', + version: '8.0.0', + version_major: 8, + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322', + }, + '@timestamp': '2020-03-23T15:04:28.785Z', + ecs: { + version: '1.4.0', + }, + service: { + node: { + name: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', + }, + environment: 'production', + name: 'opbeans-java', + runtime: { + name: 'Java', + version: '10.0.2', + }, + language: { + name: 'Java', + version: '10.0.2', + }, + version: 'None', + }, + host: { + hostname: '4cf84d094553', + os: { + platform: 'Linux', + }, + ip: '172.19.0.9', + name: '4cf84d094553', + architecture: 'amd64', + }, + connection: { + hash: "{service.environment:'production'}/{service.name:'opbeans-java'}/{span.subtype:'http'}/{destination.address:'opbeans-go'}/{span.type:'external'}", + }, + transaction: { + id: '49809ad3c26adf74', + }, + timestamp: { + us: 1584975868785273, + }, + span: { + duration: { + us: 17530, + }, + subtype: 'http', + name: 'GET opbeans-go', + destination: { + service: { + resource: 'opbeans-go:3000', + name: 'http://opbeans-go:3000', + type: 'external', + }, + }, + http: { + response: { + status_code: 200, + }, + url: { + original: 'http://opbeans-go:3000/api/orders', + }, + }, + id: 'fc107f7b556eb49b', + type: 'external', + }, + }, + { + parent: { + id: '975c8d5bfd1dd20b', + }, + agent: { + name: 'go', + version: '1.7.2', + }, + processor: { + name: 'transaction', + event: 'span', + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322', + }, + '@timestamp': '2020-03-23T15:04:28.787Z', + service: { + node: { + name: 'e948a08b8f5efe99b5da01f50da48c7d8aee3bbf4701f3da85ebe760c2ffef29', + }, + environment: 'production', + name: 'opbeans-go', + runtime: { + name: 'gc', + version: 'go1.14.1', + }, + language: { + name: 'go', + version: 'go1.14.1', + }, + version: 'None', + }, + transaction: { + id: '975c8d5bfd1dd20b', + }, + timestamp: { + us: 1584975868787174, + }, + span: { + duration: { + us: 16250, + }, + subtype: 'http', + destination: { + service: { + resource: 'opbeans-python:3000', + name: 'http://opbeans-python:3000', + type: 'external', + }, + }, + name: 'GET opbeans-python:3000', + http: { + response: { + status_code: 200, + }, + url: { + original: 'http://opbeans-python:3000/api/orders', + }, + }, + id: 'daae24d83c269918', + type: 'external', + }, + }, + { + container: { + id: 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51', + }, + parent: { + id: '6fb0ff7365b87298', + }, + agent: { + name: 'python', + version: '5.5.2', + }, + processor: { + name: 'transaction', + event: 'span', + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322', + }, + '@timestamp': '2020-03-23T15:04:28.790Z', + service: { + node: { + name: 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51', + }, + environment: 'production', + framework: { + name: 'django', + version: '2.1.13', + }, + name: 'opbeans-python', + runtime: { + name: 'CPython', + version: '3.6.10', + }, + language: { + name: 'python', + version: '3.6.10', + }, + version: 'None', + }, + transaction: { + id: '6fb0ff7365b87298', + }, + timestamp: { + us: 1584975868790080, + }, + span: { + duration: { + us: 2519, + }, + subtype: 'postgresql', + name: 'SELECT "n"."id", "n"."address", "n"."city", "n"."company_name", "n"."country", "n"."email", "n"."full_name", "n"."postal_code" FROM "customers" AS "n" WHERE "n"."id" = @__id_0 LIMIT 1', + destination: { + service: { + resource: 'postgresql', + name: 'postgresql', + type: 'db', + }, + }, + action: 'query', + id: 'c9407abb4d08ead1', + type: 'db', + sync: true, + db: { + statement: + 'SELECT "n"."id", "n"."address", "n"."city", "n"."company_name", "n"."country", "n"."email", "n"."full_name", "n"."postal_code" FROM "customers" AS "n" WHERE "n"."id" = @__id_0 LIMIT 1', + type: 'sql', + }, + }, + }, + ], + exceedsMax: false, + errorDocs: [ + { + parent: { + id: '975c8d5bfd1dd20b', + }, + agent: { + name: 'go', + version: '1.7.2', + }, + error: { + culprit: 'logrusMiddleware', + log: { + level: 'error', + message: 'GET //api/products (502)', + }, + id: '1f3cb98206b5c54225cb7c8908a658da', + grouping_key: '4dba2ff58fe6c036a5dee2ce411e512a', + }, + processor: { + name: 'error', + event: 'error', + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322', + }, + '@timestamp': '2020-03-23T16:04:28.787Z', + service: { + node: { + name: 'e948a08b8f5efe99b5da01f50da48c7d8aee3bbf4701f3da85ebe760c2ffef29', + }, + environment: 'production', + name: 'opbeans-go', + runtime: { + name: 'gc', + version: 'go1.14.1', + }, + language: { + name: 'go', + version: 'go1.14.1', + }, + version: 'None', + }, + transaction: { + id: '975c8d5bfd1dd20b', + sampled: false, + }, + timestamp: { + us: 1584975868787052, + }, + }, + { + parent: { + id: '6fb0ff7365b87298', + }, + agent: { + name: 'python', + version: '5.5.2', + }, + error: { + culprit: 'logrusMiddleware', + log: { + level: 'error', + message: 'GET //api/products (502)', + }, + id: '1f3cb98206b5c54225cb7c8908a658d2', + grouping_key: '4dba2ff58fe6c036a5dee2ce411e512a', + }, + processor: { + name: 'error', + event: 'error', + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322', + }, + '@timestamp': '2020-03-23T16:04:28.790Z', + service: { + node: { + name: 'e948a08b8f5efe99b5da01f50da48c7d8aee3bbf4701f3da85ebe760c2ffef29', + }, + environment: 'production', + name: 'opbeans-python', + runtime: { + name: 'gc', + version: 'go1.14.1', + }, + version: 'None', + }, + transaction: { + id: '6fb0ff7365b87298', + sampled: false, + }, + timestamp: { + us: 1584975868790000, + }, + }, + ], +} as unknown as TraceAPIResponse; + +export const traceChildStartBeforeParent = { + traceDocs: [ + { + container: { + id: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', + }, + process: { + pid: 6, + title: '/usr/lib/jvm/java-10-openjdk-amd64/bin/java', + ppid: 1, + }, + agent: { + name: 'java', + ephemeral_id: '99ce8403-5875-4945-b074-d37dc10563eb', + version: '1.14.1-SNAPSHOT', + }, + internal: { + sampler: { + value: 46, + }, + }, + source: { + ip: '172.19.0.13', + }, + processor: { + name: 'transaction', + event: 'transaction', + }, + url: { + path: '/api/orders', + scheme: 'http', + port: 3000, + domain: '172.19.0.9', + full: 'http://172.19.0.9:3000/api/orders', + }, + observer: { + hostname: 'f37f48d8b60b', + id: 'd8522e1f-be8e-43c2-b290-ac6b6c0f171e', + ephemeral_id: '6ed88f14-170e-478d-a4f5-ea5e7f4b16b9', + type: 'apm-server', + version: '8.0.0', + version_major: 8, + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322', + }, + '@timestamp': '2020-03-23T15:04:28.785Z', + ecs: { + version: '1.4.0', + }, + service: { + node: { + name: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', + }, + environment: 'production', + name: 'opbeans-java', + runtime: { + name: 'Java', + version: '10.0.2', + }, + language: { + name: 'Java', + version: '10.0.2', + }, + version: 'None', + }, + host: { + hostname: '4cf84d094553', + os: { + platform: 'Linux', + }, + ip: '172.19.0.9', + name: '4cf84d094553', + architecture: 'amd64', + }, + http: { + request: { + headers: { + Accept: ['*/*'], + 'User-Agent': ['Python/3.7 aiohttp/3.3.2'], + Host: ['172.19.0.9:3000'], + 'Accept-Encoding': ['gzip, deflate'], + }, + method: 'get', + socket: { + encrypted: false, + remote_address: '172.19.0.13', + }, + body: { + original: '[REDACTED]', + }, + }, + response: { + headers: { + 'Transfer-Encoding': ['chunked'], + Date: ['Mon, 23 Mar 2020 15:04:28 GMT'], + 'Content-Type': ['application/json;charset=ISO-8859-1'], + }, + status_code: 200, + finished: true, + headers_sent: true, + }, + version: '1.1', + }, + client: { + ip: '172.19.0.13', + }, + transaction: { + duration: { + us: 18842, + }, + result: 'HTTP 2xx', + name: 'DispatcherServlet#doGet', + id: '49809ad3c26adf74', + span_count: { + dropped: 0, + started: 1, + }, + type: 'request', + sampled: true, + }, + user_agent: { + original: 'Python/3.7 aiohttp/3.3.2', + name: 'Other', + device: { + name: 'Other', + }, + }, + timestamp: { + us: 1584975868785000, + }, + }, + { + parent: { + id: 'fc107f7b556eb49b', + }, + agent: { + name: 'go', + version: '1.7.2', + }, + processor: { + name: 'transaction', + event: 'transaction', + }, + url: { + path: '/api/orders', + scheme: 'http', + port: 3000, + domain: 'opbeans-go', + full: 'http://opbeans-go:3000/api/orders', + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322', + }, + '@timestamp': '2020-03-23T15:04:28.787Z', + service: { + node: { + name: 'e948a08b8f5efe99b5da01f50da48c7d8aee3bbf4701f3da85ebe760c2ffef29', + }, + environment: 'production', + framework: { + name: 'gin', + version: 'v1.4.0', + }, + name: 'opbeans-go', + runtime: { + name: 'gc', + version: 'go1.14.1', + }, + language: { + name: 'go', + version: 'go1.14.1', + }, + version: 'None', + }, + transaction: { + duration: { + us: 16597, + }, + result: 'HTTP 2xx', + name: 'GET /api/orders', + id: '975c8d5bfd1dd20b', + span_count: { + dropped: 0, + started: 1, + }, + type: 'request', + sampled: true, + }, + timestamp: { + us: 1584975868787052, + }, + }, + { + parent: { + id: 'daae24d83c269918', + }, + agent: { + name: 'python', + version: '5.5.2', + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322', + }, + timestamp: { + us: 1584975868780000, + }, + processor: { + name: 'transaction', + event: 'transaction', + }, + url: { + path: '/api/orders', + scheme: 'http', + port: 3000, + domain: 'opbeans-go', + full: 'http://opbeans-go:3000/api/orders', + }, + '@timestamp': '2020-03-23T15:04:28.788Z', + service: { + node: { + name: 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51', + }, + environment: 'production', + framework: { + name: 'django', + version: '2.1.13', + }, + name: 'opbeans-python', + runtime: { + name: 'CPython', + version: '3.6.10', + }, + language: { + name: 'python', + version: '3.6.10', + }, + version: 'None', + }, + transaction: { + result: 'HTTP 2xx', + duration: { + us: 1464, + }, + name: 'I started before my parent 😰', + span_count: { + dropped: 0, + started: 1, + }, + id: '6fb0ff7365b87298', + type: 'request', + sampled: true, + }, + }, + { + container: { + id: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', + }, + parent: { + id: '49809ad3c26adf74', + }, + process: { + pid: 6, + title: '/usr/lib/jvm/java-10-openjdk-amd64/bin/java', + ppid: 1, + }, + agent: { + name: 'java', + ephemeral_id: '99ce8403-5875-4945-b074-d37dc10563eb', + version: '1.14.1-SNAPSHOT', + }, + internal: { + sampler: { + value: 44, + }, + }, + destination: { + address: 'opbeans-go', + port: 3000, + }, + processor: { + name: 'transaction', + event: 'span', + }, + observer: { + hostname: 'f37f48d8b60b', + id: 'd8522e1f-be8e-43c2-b290-ac6b6c0f171e', + type: 'apm-server', + ephemeral_id: '6ed88f14-170e-478d-a4f5-ea5e7f4b16b9', + version: '8.0.0', + version_major: 8, + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322', + }, + '@timestamp': '2020-03-23T15:04:28.785Z', + ecs: { + version: '1.4.0', + }, + service: { + node: { + name: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', + }, + environment: 'production', + name: 'opbeans-java', + runtime: { + name: 'Java', + version: '10.0.2', + }, + language: { + name: 'Java', + version: '10.0.2', + }, + version: 'None', + }, + host: { + hostname: '4cf84d094553', + os: { + platform: 'Linux', + }, + ip: '172.19.0.9', + name: '4cf84d094553', + architecture: 'amd64', + }, + connection: { + hash: "{service.environment:'production'}/{service.name:'opbeans-java'}/{span.subtype:'http'}/{destination.address:'opbeans-go'}/{span.type:'external'}", + }, + transaction: { + id: '49809ad3c26adf74', + }, + timestamp: { + us: 1584975868785273, + }, + span: { + duration: { + us: 17530, + }, + subtype: 'http', + name: 'GET opbeans-go', + destination: { + service: { + resource: 'opbeans-go:3000', + name: 'http://opbeans-go:3000', + type: 'external', + }, + }, + http: { + response: { + status_code: 200, + }, + url: { + original: 'http://opbeans-go:3000/api/orders', + }, + }, + id: 'fc107f7b556eb49b', + type: 'external', + }, + }, + { + parent: { + id: '975c8d5bfd1dd20b', + }, + agent: { + name: 'go', + version: '1.7.2', + }, + processor: { + name: 'transaction', + event: 'span', + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322', + }, + '@timestamp': '2020-03-23T15:04:28.787Z', + service: { + node: { + name: 'e948a08b8f5efe99b5da01f50da48c7d8aee3bbf4701f3da85ebe760c2ffef29', + }, + environment: 'production', + name: 'opbeans-go', + runtime: { + name: 'gc', + version: 'go1.14.1', + }, + language: { + name: 'go', + version: 'go1.14.1', + }, + version: 'None', + }, + transaction: { + id: '975c8d5bfd1dd20b', + }, + timestamp: { + us: 1584975868787174, + }, + span: { + duration: { + us: 16250, + }, + subtype: 'http', + destination: { + service: { + resource: 'opbeans-python:3000', + name: 'http://opbeans-python:3000', + type: 'external', + }, + }, + name: 'I am his 👇🏻 parent 😡', + http: { + response: { + status_code: 200, + }, + url: { + original: 'http://opbeans-python:3000/api/orders', + }, + }, + id: 'daae24d83c269918', + type: 'external', + }, + }, + { + container: { + id: 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51', + }, + parent: { + id: '6fb0ff7365b87298', + }, + agent: { + name: 'python', + version: '5.5.2', + }, + processor: { + name: 'transaction', + event: 'span', + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322', + }, + '@timestamp': '2020-03-23T15:04:28.790Z', + service: { + node: { + name: 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51', + }, + environment: 'production', + framework: { + name: 'django', + version: '2.1.13', + }, + name: 'opbeans-python', + runtime: { + name: 'CPython', + version: '3.6.10', + }, + language: { + name: 'python', + version: '3.6.10', + }, + version: 'None', + }, + transaction: { + id: '6fb0ff7365b87298', + }, + timestamp: { + us: 1584975868781000, + }, + span: { + duration: { + us: 2519, + }, + subtype: 'postgresql', + name: 'I am using my parents skew 😇', + destination: { + service: { + resource: 'postgresql', + name: 'postgresql', + type: 'db', + }, + }, + action: 'query', + id: 'c9407abb4d08ead1', + type: 'db', + sync: true, + db: { + statement: + 'SELECT "opbeans_order"."id", "opbeans_order"."customer_id", "opbeans_customer"."full_name", "opbeans_order"."created_at" FROM "opbeans_order" INNER JOIN "opbeans_customer" ON ("opbeans_order"."customer_id" = "opbeans_customer"."id") LIMIT 1000', + type: 'sql', + }, + }, + }, + ], + exceedsMax: false, + errorDocs: [], +} as TraceAPIResponse; + +export const inferredSpans = { + traceDocs: [ + { + container: { + id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', + }, + agent: { + name: 'java', + ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3', + version: '1.15.1-SNAPSHOT', + }, + process: { + pid: 6, + title: '/opt/java/openjdk/bin/java', + ppid: 1, + }, + source: { + ip: '172.18.0.8', + }, + processor: { + name: 'transaction', + event: 'transaction', + }, + url: { + path: '/api/products/2', + scheme: 'http', + port: 3000, + domain: '172.18.0.7', + full: 'http://172.18.0.7:3000/api/products/2', + }, + observer: { + hostname: '7189f754b5a3', + id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc', + ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954', + type: 'apm-server', + version: '8.0.0', + version_major: 8, + }, + trace: { + id: '3b0dc77f3754e5bcb9da0e4c15e0db97', + }, + '@timestamp': '2020-04-09T11:36:00.786Z', + ecs: { + version: '1.5.0', + }, + service: { + node: { + name: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', + }, + environment: 'production', + name: 'opbeans-java', + runtime: { + name: 'Java', + version: '11.0.6', + }, + language: { + name: 'Java', + version: '11.0.6', + }, + version: 'None', + }, + host: { + hostname: 'fc2ae281f56f', + os: { + platform: 'Linux', + }, + ip: '172.18.0.7', + name: 'fc2ae281f56f', + architecture: 'amd64', + }, + client: { + ip: '172.18.0.8', + }, + http: { + request: { + headers: { + Accept: ['*/*'], + 'User-Agent': ['Python/3.7 aiohttp/3.3.2'], + Host: ['172.18.0.7:3000'], + 'Accept-Encoding': ['gzip, deflate'], + }, + method: 'get', + socket: { + encrypted: false, + remote_address: '172.18.0.8', + }, + }, + response: { + headers: { + 'Transfer-Encoding': ['chunked'], + Date: ['Thu, 09 Apr 2020 11:36:01 GMT'], + 'Content-Type': ['application/json;charset=UTF-8'], + }, + status_code: 200, + finished: true, + headers_sent: true, + }, + version: '1.1', + }, + user_agent: { + original: 'Python/3.7 aiohttp/3.3.2', + name: 'Other', + device: { + name: 'Other', + }, + }, + transaction: { + duration: { + us: 237537, + }, + result: 'HTTP 2xx', + name: 'APIRestController#product', + span_count: { + dropped: 0, + started: 3, + }, + id: 'f2387d37260d00bd', + type: 'request', + sampled: true, + }, + timestamp: { + us: 1586432160786001, + }, + }, + { + container: { + id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', + }, + parent: { + id: 'f2387d37260d00bd', + }, + agent: { + name: 'java', + ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3', + version: '1.15.1-SNAPSHOT', + }, + process: { + pid: 6, + title: '/opt/java/openjdk/bin/java', + ppid: 1, + }, + processor: { + name: 'transaction', + event: 'span', + }, + observer: { + hostname: '7189f754b5a3', + id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc', + ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954', + type: 'apm-server', + version: '8.0.0', + version_major: 8, + }, + trace: { + id: '3b0dc77f3754e5bcb9da0e4c15e0db97', + }, + '@timestamp': '2020-04-09T11:36:00.810Z', + ecs: { + version: '1.5.0', + }, + service: { + node: { + name: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', + }, + environment: 'production', + name: 'opbeans-java', + runtime: { + name: 'Java', + version: '11.0.6', + }, + language: { + name: 'Java', + version: '11.0.6', + }, + version: 'None', + }, + host: { + hostname: 'fc2ae281f56f', + os: { + platform: 'Linux', + }, + ip: '172.18.0.7', + name: 'fc2ae281f56f', + architecture: 'amd64', + }, + transaction: { + id: 'f2387d37260d00bd', + }, + span: { + duration: { + us: 204574, + }, + subtype: 'inferred', + name: 'ServletInvocableHandlerMethod#invokeAndHandle', + id: 'a5df600bd7bd5e38', + type: 'app', + }, + timestamp: { + us: 1586432160810441, + }, + }, + { + container: { + id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', + }, + parent: { + id: 'a5df600bd7bd5e38', + }, + agent: { + name: 'java', + ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3', + version: '1.15.1-SNAPSHOT', + }, + process: { + pid: 6, + title: '/opt/java/openjdk/bin/java', + ppid: 1, + }, + processor: { + name: 'transaction', + event: 'span', + }, + observer: { + hostname: '7189f754b5a3', + id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc', + type: 'apm-server', + ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954', + version: '8.0.0', + version_major: 8, + }, + trace: { + id: '3b0dc77f3754e5bcb9da0e4c15e0db97', + }, + '@timestamp': '2020-04-09T11:36:00.810Z', + ecs: { + version: '1.5.0', + }, + service: { + node: { + name: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', + }, + environment: 'production', + name: 'opbeans-java', + runtime: { + name: 'Java', + version: '11.0.6', + }, + language: { + name: 'Java', + version: '11.0.6', + }, + version: 'None', + }, + host: { + hostname: 'fc2ae281f56f', + os: { + platform: 'Linux', + }, + ip: '172.18.0.7', + name: 'fc2ae281f56f', + architecture: 'amd64', + }, + transaction: { + id: 'f2387d37260d00bd', + }, + timestamp: { + us: 1586432160810441, + }, + span: { + duration: { + us: 102993, + }, + stacktrace: [ + { + library_frame: true, + exclude_from_grouping: false, + filename: 'InvocableHandlerMethod.java', + line: { + number: -1, + }, + function: 'doInvoke', + }, + { + exclude_from_grouping: false, + library_frame: true, + filename: 'InvocableHandlerMethod.java', + line: { + number: -1, + }, + function: 'invokeForRequest', + }, + ], + subtype: 'inferred', + name: 'APIRestController#product', + id: '808dc34fc41ce522', + type: 'app', + }, + }, + { + container: { + id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', + }, + parent: { + id: 'f2387d37260d00bd', + }, + agent: { + name: 'java', + ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3', + version: '1.15.1-SNAPSHOT', + }, + process: { + pid: 6, + title: '/opt/java/openjdk/bin/java', + ppid: 1, + }, + processor: { + name: 'transaction', + event: 'span', + }, + labels: { + productId: '2', + }, + observer: { + hostname: '7189f754b5a3', + id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc', + ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954', + type: 'apm-server', + version: '8.0.0', + version_major: 8, + }, + trace: { + id: '3b0dc77f3754e5bcb9da0e4c15e0db97', + }, + '@timestamp': '2020-04-09T11:36:00.832Z', + ecs: { + version: '1.5.0', + }, + service: { + node: { + name: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', + }, + environment: 'production', + name: 'opbeans-java', + runtime: { + name: 'Java', + version: '11.0.6', + }, + language: { + name: 'Java', + version: '11.0.6', + }, + version: 'None', + }, + host: { + hostname: 'fc2ae281f56f', + os: { + platform: 'Linux', + }, + ip: '172.18.0.7', + name: 'fc2ae281f56f', + architecture: 'amd64', + }, + transaction: { + id: 'f2387d37260d00bd', + }, + timestamp: { + us: 1586432160832300, + }, + span: { + duration: { + us: 99295, + }, + name: 'OpenTracing product span', + id: '41226ae63af4f235', + type: 'unknown', + }, + child: { id: ['8d80de06aa11a6fc'] }, + }, + { + container: { + id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', + }, + parent: { + id: '808dc34fc41ce522', + }, + process: { + pid: 6, + title: '/opt/java/openjdk/bin/java', + ppid: 1, + }, + agent: { + name: 'java', + ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3', + version: '1.15.1-SNAPSHOT', + }, + processor: { + name: 'transaction', + event: 'span', + }, + observer: { + hostname: '7189f754b5a3', + id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc', + ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954', + type: 'apm-server', + version: '8.0.0', + version_major: 8, + }, + trace: { + id: '3b0dc77f3754e5bcb9da0e4c15e0db97', + }, + '@timestamp': '2020-04-09T11:36:00.859Z', + ecs: { + version: '1.5.0', + }, + service: { + node: { + name: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', + }, + environment: 'production', + name: 'opbeans-java', + runtime: { + name: 'Java', + version: '11.0.6', + }, + language: { + name: 'Java', + version: '11.0.6', + }, + version: 'None', + }, + host: { + hostname: 'fc2ae281f56f', + os: { + platform: 'Linux', + }, + ip: '172.18.0.7', + name: 'fc2ae281f56f', + architecture: 'amd64', + }, + transaction: { + id: 'f2387d37260d00bd', + }, + timestamp: { + us: 1586432160859600, + }, + span: { + duration: { + us: 53835, + }, + subtype: 'inferred', + name: 'Loader#executeQueryStatement', + id: '8d80de06aa11a6fc', + type: 'app', + }, + }, + { + container: { + id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', + }, + parent: { + id: '41226ae63af4f235', + }, + agent: { + name: 'java', + ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3', + version: '1.15.1-SNAPSHOT', + }, + process: { + pid: 6, + title: '/opt/java/openjdk/bin/java', + ppid: 1, + }, + destination: { + address: 'postgres', + port: 5432, + }, + processor: { + name: 'transaction', + event: 'span', + }, + observer: { + hostname: '7189f754b5a3', + id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc', + ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954', + type: 'apm-server', + version: '8.0.0', + version_major: 8, + }, + trace: { + id: '3b0dc77f3754e5bcb9da0e4c15e0db97', + }, + '@timestamp': '2020-04-09T11:36:00.903Z', + ecs: { + version: '1.5.0', + }, + service: { + node: { + name: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', + }, + environment: 'production', + name: 'opbeans-java', + runtime: { + name: 'Java', + version: '11.0.6', + }, + language: { + name: 'Java', + version: '11.0.6', + }, + version: 'None', + }, + host: { + hostname: 'fc2ae281f56f', + os: { + platform: 'Linux', + }, + ip: '172.18.0.7', + name: 'fc2ae281f56f', + architecture: 'amd64', + }, + transaction: { + id: 'f2387d37260d00bd', + }, + timestamp: { + us: 1586432160903236, + }, + span: { + duration: { + us: 10211, + }, + subtype: 'postgresql', + destination: { + service: { + resource: 'postgresql', + name: 'postgresql', + type: 'db', + }, + }, + name: 'SELECT FROM products', + action: 'query', + id: '3708d5623658182f', + type: 'db', + db: { + statement: + 'select product0_.id as col_0_0_, product0_.sku as col_1_0_, product0_.name as col_2_0_, product0_.description as col_3_0_, product0_.cost as col_4_0_, product0_.selling_price as col_5_0_, product0_.stock as col_6_0_, producttyp1_.id as col_7_0_, producttyp1_.name as col_8_0_, (select sum(orderline2_.amount) from order_lines orderline2_ where orderline2_.product_id=product0_.id) as col_9_0_ from products product0_ left outer join product_types producttyp1_ on product0_.type_id=producttyp1_.id where product0_.id=?', + type: 'sql', + user: { + name: 'postgres', + }, + }, + }, + }, + { + container: { + id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', + }, + parent: { + id: '41226ae63af4f235', + }, + process: { + pid: 6, + title: '/opt/java/openjdk/bin/java', + ppid: 1, + }, + agent: { + name: 'java', + ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3', + version: '1.15.1-SNAPSHOT', + }, + destination: { + address: 'postgres', + port: 5432, + }, + processor: { + name: 'transaction', + event: 'span', + }, + observer: { + hostname: '7189f754b5a3', + id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc', + ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954', + type: 'apm-server', + version: '8.0.0', + version_major: 8, + }, + trace: { + id: '3b0dc77f3754e5bcb9da0e4c15e0db97', + }, + '@timestamp': '2020-04-09T11:36:00.859Z', + ecs: { + version: '1.5.0', + }, + service: { + node: { + name: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', + }, + environment: 'production', + name: 'opbeans-java', + runtime: { + name: 'Java', + version: '11.0.6', + }, + language: { + name: 'Java', + version: '11.0.6', + }, + version: 'None', + }, + host: { + hostname: 'fc2ae281f56f', + os: { + platform: 'Linux', + }, + ip: '172.18.0.7', + name: 'fc2ae281f56f', + architecture: 'amd64', + }, + transaction: { + id: 'f2387d37260d00bd', + }, + timestamp: { + us: 1586432160859508, + }, + span: { + duration: { + us: 4503, + }, + subtype: 'postgresql', + destination: { + service: { + resource: 'postgresql', + name: 'postgresql', + type: 'db', + }, + }, + name: 'empty query', + action: 'query', + id: '9871cfd612368932', + type: 'db', + db: { + rows_affected: 0, + statement: '(empty query)', + type: 'sql', + user: { + name: 'postgres', + }, + }, + }, + }, + ], + exceedsMax: false, + errorDocs: [], +} as TraceAPIResponse; diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall_container.stories.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall_container.stories.tsx new file mode 100644 index 0000000000000..895b83136a097 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall_container.stories.tsx @@ -0,0 +1,88 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Meta, Story } from '@storybook/react'; +import React, { ComponentProps } from 'react'; +import { MemoryRouter } from 'react-router-dom'; +import { MockApmPluginContextWrapper } from '../../../../../context/apm_plugin/mock_apm_plugin_context'; +import { WaterfallContainer } from './index'; +import { getWaterfall } from './Waterfall/waterfall_helpers/waterfall_helpers'; +import { + inferredSpans, + manyChildrenWithSameLength, + simpleTrace, + traceChildStartBeforeParent, + traceWithErrors, + urlParams as testUrlParams, +} from './waterfall_container.stories.data'; + +type Args = ComponentProps; + +const stories: Meta = { + title: 'app/TransactionDetails/Waterfall', + component: WaterfallContainer, + decorators: [ + (StoryComponent) => ( + + + + + + ), + ], +}; +export default stories; + +export const Example: Story = ({ urlParams, waterfall }) => { + return ; +}; +Example.args = { + urlParams: testUrlParams, + waterfall: getWaterfall(simpleTrace, '975c8d5bfd1dd20b'), +}; + +export const WithErrors: Story = ({ urlParams, waterfall }) => { + return ; +}; +WithErrors.args = { + urlParams: testUrlParams, + waterfall: getWaterfall(traceWithErrors, '975c8d5bfd1dd20b'), +}; + +export const ChildStartsBeforeParent: Story = ({ + urlParams, + waterfall, +}) => { + return ; +}; +ChildStartsBeforeParent.args = { + urlParams: testUrlParams, + waterfall: getWaterfall(traceChildStartBeforeParent, '975c8d5bfd1dd20b'), +}; + +export const InferredSpans: Story = ({ urlParams, waterfall }) => { + return ; +}; +InferredSpans.args = { + urlParams: testUrlParams, + waterfall: getWaterfall(inferredSpans, 'f2387d37260d00bd'), +}; + +export const ManyChildrenWithSameLength: Story = ({ + urlParams, + waterfall, +}) => { + return ; +}; +ManyChildrenWithSameLength.args = { + urlParams: testUrlParams, + waterfall: getWaterfall(manyChildrenWithSameLength, '9a7f717439921d39'), +}; diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall_container.test.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall_container.test.tsx new file mode 100644 index 0000000000000..47610569dfa06 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall_container.test.tsx @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { composeStories } from '@storybook/testing-react'; +import { render } from '@testing-library/react'; +import React from 'react'; +import { disableConsoleWarning } from '../../../../../utils/testHelpers'; +import * as stories from './waterfall_container.stories'; + +const { Example } = composeStories(stories); + +describe('WaterfallContainer', () => { + let consoleMock: jest.SpyInstance; + + beforeAll(() => { + consoleMock = disableConsoleWarning('Warning: componentWillReceiveProps'); + }); + + afterAll(() => { + consoleMock.mockRestore(); + }); + + it('renders', () => { + expect(() => render()).not.toThrowError(); + }); + + it('expands and contracts the accordion', () => { + const { getAllByRole } = render(); + const buttons = getAllByRole('button'); + const parentItem = buttons[2]; + const childItem = buttons[3]; + + parentItem.click(); + + expect(parentItem).toHaveAttribute('aria-expanded', 'false'); + expect(childItem).toHaveAttribute('aria-expanded', 'true'); + }); +}); From ab16b485cd3a31ab7f5d1dbee35f6ac90cb0f66a Mon Sep 17 00:00:00 2001 From: Tyler Smalley Date: Tue, 19 Oct 2021 14:12:31 -0700 Subject: [PATCH 094/204] [ci] Disable Bazel cache on CI (#115428) This is a short-term solution to reduce our hit count in Buildkite due to the increase in bootstraps so we can move forward with enabling Buildkite across all PRs. --- .bazelrc | 3 -- .bazelrc.common | 4 +- .buildkite/pipelines/on_merge.yml | 2 + .buildkite/scripts/bootstrap.sh | 1 + .../scripts/common/persist_bazel_cache.sh | 15 -------- .buildkite/scripts/common/setup_bazel.sh | 38 +++++++++++++++++++ .buildkite/scripts/common/util.sh | 2 +- .../steps/on_merge_build_and_metrics.sh | 3 -- src/dev/ci_setup/.bazelrc-ci | 15 -------- 9 files changed, 44 insertions(+), 39 deletions(-) delete mode 100755 .buildkite/scripts/common/persist_bazel_cache.sh create mode 100755 .buildkite/scripts/common/setup_bazel.sh delete mode 100644 src/dev/ci_setup/.bazelrc-ci diff --git a/.bazelrc b/.bazelrc index 524542a02a4aa..91c4870ebd126 100644 --- a/.bazelrc +++ b/.bazelrc @@ -10,9 +10,6 @@ build --remote_timeout=30 build --remote_header=x-buildbuddy-api-key=3EYk49W2NefOx2n3yMze build --remote_accept_cached=true -# BuildBuddy -## Metadata settings -build --workspace_status_command="node ./src/dev/bazel_workspace_status.js" # Enable this in case you want to share your build info # build --build_metadata=VISIBILITY=PUBLIC build --build_metadata=TEST_GROUPS=//packages diff --git a/.bazelrc.common b/.bazelrc.common index 3de2bceaad3a6..c401a90507982 100644 --- a/.bazelrc.common +++ b/.bazelrc.common @@ -120,8 +120,8 @@ test --incompatible_strict_action_env # collect coverage information from test targets coverage --instrument_test_targets -# Settings for CI -# Bazel flags for CI are in /src/dev/ci_setup/.bazelrc-ci +# Metadata settings +build --workspace_status_command="node ./src/dev/bazel_workspace_status.js" # Load any settings specific to the current user. # .bazelrc.user should appear in .gitignore so that settings are not shared with team members diff --git a/.buildkite/pipelines/on_merge.yml b/.buildkite/pipelines/on_merge.yml index bceb1796479a2..dc0541c6397d9 100644 --- a/.buildkite/pipelines/on_merge.yml +++ b/.buildkite/pipelines/on_merge.yml @@ -9,6 +9,8 @@ steps: - command: .buildkite/scripts/steps/on_merge_build_and_metrics.sh label: Default Build and Metrics + env: + BAZEL_CACHE_MODE: read-write agents: queue: c2-8 diff --git a/.buildkite/scripts/bootstrap.sh b/.buildkite/scripts/bootstrap.sh index 3c6283a4fe3fd..df38c105d2fd3 100755 --- a/.buildkite/scripts/bootstrap.sh +++ b/.buildkite/scripts/bootstrap.sh @@ -3,6 +3,7 @@ set -euo pipefail source .buildkite/scripts/common/util.sh +source .buildkite/scripts/common/setup_bazel.sh echo "--- yarn install and bootstrap" retry 2 15 yarn kbn bootstrap diff --git a/.buildkite/scripts/common/persist_bazel_cache.sh b/.buildkite/scripts/common/persist_bazel_cache.sh deleted file mode 100755 index 357805c11acec..0000000000000 --- a/.buildkite/scripts/common/persist_bazel_cache.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash - -source .buildkite/scripts/common/util.sh - -KIBANA_BUILDBUDDY_CI_API_KEY=$(retry 5 5 vault read -field=value secret/kibana-issues/dev/kibana-buildbuddy-ci-api-key) -export KIBANA_BUILDBUDDY_CI_API_KEY - -# overwrites the file checkout .bazelrc file with the one intended for CI env -cp "$KIBANA_DIR/src/dev/ci_setup/.bazelrc-ci" "$KIBANA_DIR/.bazelrc" - -### -### append auth token to buildbuddy into "$KIBANA_DIR/.bazelrc"; -### -echo "# Appended by .buildkite/scripts/persist_bazel_cache.sh" >> "$KIBANA_DIR/.bazelrc" -echo "build --remote_header=x-buildbuddy-api-key=$KIBANA_BUILDBUDDY_CI_API_KEY" >> "$KIBANA_DIR/.bazelrc" diff --git a/.buildkite/scripts/common/setup_bazel.sh b/.buildkite/scripts/common/setup_bazel.sh new file mode 100755 index 0000000000000..bff44c7ba8dd3 --- /dev/null +++ b/.buildkite/scripts/common/setup_bazel.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +source .buildkite/scripts/common/util.sh + +KIBANA_BUILDBUDDY_CI_API_KEY=$(retry 5 5 vault read -field=value secret/kibana-issues/dev/kibana-buildbuddy-ci-api-key) +export KIBANA_BUILDBUDDY_CI_API_KEY + +echo "[bazel] writing .bazelrc" +cat < $KIBANA_DIR/.bazelrc + # Generated by .buildkite/scripts/common/setup_bazel.sh + + import %workspace%/.bazelrc.common + + build --build_metadata=ROLE=CI +EOF + +if [[ "${BAZEL_CACHE_MODE:-none}" == read* ]]; then + echo "[bazel] enabling caching" +cat <> $KIBANA_DIR/.bazelrc + build --bes_results_url=https://app.buildbuddy.io/invocation/ + build --bes_backend=grpcs://cloud.buildbuddy.io + build --remote_cache=grpcs://cloud.buildbuddy.io + build --remote_timeout=3600 + build --remote_header=x-buildbuddy-api-key=$KIBANA_BUILDBUDDY_CI_API_KEY +EOF +fi + +if [[ "${BAZEL_CACHE_MODE:-none}" == "read" ]]; then + echo "[bazel] cache set to read-only" +cat <> $KIBANA_DIR/.bazelrc + build --noremote_upload_local_results +EOF +fi + +if [[ "${BAZEL_CACHE_MODE:-none}" != @(read|read-write|none|) ]]; then + echo "invalid value for BAZEL_CACHE_MODE received ($BAZEL_CACHE_MODE), expected one of [read,read-write,none]" + exit 1 +fi diff --git a/.buildkite/scripts/common/util.sh b/.buildkite/scripts/common/util.sh index a884a147577c9..0e5fb1c40eb6f 100755 --- a/.buildkite/scripts/common/util.sh +++ b/.buildkite/scripts/common/util.sh @@ -18,7 +18,7 @@ verify_no_git_changes() { RED='\033[0;31m' C_RESET='\033[0m' # Reset color - GIT_CHANGES="$(git ls-files --modified)" + GIT_CHANGES="$(git ls-files --modified -- . ':!:.bazelrc')" if [ "$GIT_CHANGES" ]; then echo -e "\n${RED}ERROR: '$1' caused changes to the following files:${C_RESET}\n" echo -e "$GIT_CHANGES\n" diff --git a/.buildkite/scripts/steps/on_merge_build_and_metrics.sh b/.buildkite/scripts/steps/on_merge_build_and_metrics.sh index 315ba08f8719b..1f1e492f87bec 100755 --- a/.buildkite/scripts/steps/on_merge_build_and_metrics.sh +++ b/.buildkite/scripts/steps/on_merge_build_and_metrics.sh @@ -2,9 +2,6 @@ set -euo pipefail -# Write Bazel cache for Linux -.buildkite/scripts/common/persist_bazel_cache.sh - .buildkite/scripts/bootstrap.sh .buildkite/scripts/build_kibana.sh .buildkite/scripts/post_build_kibana.sh diff --git a/src/dev/ci_setup/.bazelrc-ci b/src/dev/ci_setup/.bazelrc-ci deleted file mode 100644 index 9aee657f37bcb..0000000000000 --- a/src/dev/ci_setup/.bazelrc-ci +++ /dev/null @@ -1,15 +0,0 @@ -# Used in the on-merge job to persist the Bazel cache to BuildBuddy -# from: .buildkite/scripts/common/persist_bazel_cache.sh - -import %workspace%/.bazelrc.common - -# BuildBuddy settings -build --bes_results_url=https://app.buildbuddy.io/invocation/ -build --bes_backend=grpcs://cloud.buildbuddy.io -build --remote_cache=grpcs://cloud.buildbuddy.io -build --remote_timeout=3600 -# --remote_header=x-buildbuddy-api-key= # appended in CI script - -# Metadata settings -build --build_metadata=ROLE=CI -build --workspace_status_command="node ./src/dev/bazel_workspace_status.js" From b1dd89e173e5c7416d9b74d387edd7f22b0cba86 Mon Sep 17 00:00:00 2001 From: Ashokaditya Date: Tue, 19 Oct 2021 23:29:03 +0200 Subject: [PATCH 095/204] [Security Solution][Endpoint] Update host isolation pending status API to work with new endpoint (#115441) * get the whole response when fetching responses by agentIds fixes elastic/security-team/issues/1705 * search new response index with actionId fixes elastic/security-team/issues/1705 * Find matching responses in new response index if fleet action response has an `ack` * review changes * hasIndexedDoc fix fetch logic * remove file * simplify code * add some acks to generator responses * meaningful names review changes Co-authored-by: Esteban Beltran --- .../data_loaders/index_fleet_actions.ts | 6 +- .../common/endpoint/types/actions.ts | 1 + .../server/endpoint/services/actions.ts | 126 ++++++++++++++++-- 3 files changed, 119 insertions(+), 14 deletions(-) diff --git a/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_fleet_actions.ts b/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_fleet_actions.ts index 246cf059f4cbd..5cc564ee3d41d 100644 --- a/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_fleet_actions.ts +++ b/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_fleet_actions.ts @@ -66,7 +66,11 @@ export const indexFleetActionsForHost = async ( const actionResponse = fleetActionGenerator.generateResponse({ action_id: action.action_id, agent_id: agentId, - action_data: action.data, + action_data: { + ...action.data, + // add ack to 4/5th of fleet response + ack: fleetActionGenerator.randomFloat() < 0.8 ? true : undefined, + }, }); esClient diff --git a/x-pack/plugins/security_solution/common/endpoint/types/actions.ts b/x-pack/plugins/security_solution/common/endpoint/types/actions.ts index fb29297eb5929..d7ad417fc7d3f 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types/actions.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types/actions.ts @@ -64,6 +64,7 @@ export interface LogsEndpointActionResponse { export interface EndpointActionData { command: ISOLATION_ACTIONS; comment?: string; + ack?: boolean; } export interface EndpointAction { diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions.ts index d59ecb674196c..b25b599517300 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions.ts @@ -9,6 +9,7 @@ import { ElasticsearchClient, Logger } from 'kibana/server'; import { SearchHit, SearchResponse } from '@elastic/elasticsearch/api/types'; import { ApiResponse } from '@elastic/elasticsearch'; import { AGENT_ACTIONS_INDEX, AGENT_ACTIONS_RESULTS_INDEX } from '../../../../fleet/common'; +import { ENDPOINT_ACTION_RESPONSES_INDEX } from '../../../common/endpoint/constants'; import { SecuritySolutionRequestHandlerContext } from '../../types'; import { ActivityLog, @@ -146,6 +147,41 @@ const getActivityLog = async ({ return sortedData; }; +const hasAckInResponse = (response: EndpointActionResponse): boolean => { + return typeof response.action_data.ack !== 'undefined'; +}; + +// return TRUE if for given action_id/agent_id +// there is no doc in .logs-endpoint.action.response-default +const hasNoEndpointResponse = ({ + action, + agentId, + indexedActionIds, +}: { + action: EndpointAction; + agentId: string; + indexedActionIds: string[]; +}): boolean => { + return action.agents.includes(agentId) && !indexedActionIds.includes(action.action_id); +}; + +// return TRUE if for given action_id/agent_id +// there is no doc in .fleet-actions-results +const hasNoFleetResponse = ({ + action, + agentId, + agentResponses, +}: { + action: EndpointAction; + agentId: string; + agentResponses: EndpointActionResponse[]; +}): boolean => { + return ( + action.agents.includes(agentId) && + !agentResponses.map((e) => e.action_id).includes(action.action_id) + ); +}; + export const getPendingActionCounts = async ( esClient: ElasticsearchClient, metadataService: EndpointMetadataService, @@ -179,21 +215,45 @@ export const getPendingActionCounts = async ( .catch(catchAndWrapError); // retrieve any responses to those action IDs from these agents - const responses = await fetchActionResponseIds( + const responses = await fetchActionResponses( esClient, metadataService, recentActions.map((a) => a.action_id), agentIDs ); - const pending: EndpointPendingActions[] = []; + // + + const pending: EndpointPendingActions[] = []; for (const agentId of agentIDs) { - const responseIDsFromAgent = responses[agentId]; + const agentResponses = responses[agentId]; + + // get response actionIds for responses with ACKs + const ackResponseActionIdList: string[] = agentResponses + .filter(hasAckInResponse) + .map((response) => response.action_id); + + // actions Ids that are indexed in new response index + const indexedActionIds = await hasEndpointResponseDoc({ + agentId, + actionIds: ackResponseActionIdList, + esClient, + }); + + const pendingActions: EndpointAction[] = recentActions.filter((action) => { + return ackResponseActionIdList.includes(action.action_id) // if has ack + ? hasNoEndpointResponse({ action, agentId, indexedActionIds }) // then find responses in new index + : hasNoFleetResponse({ + // else use the legacy way + action, + agentId, + agentResponses, + }); + }); pending.push({ agent_id: agentId, - pending_actions: recentActions - .filter((a) => a.agents.includes(agentId) && !responseIDsFromAgent.includes(a.action_id)) + pending_actions: pendingActions .map((a) => a.data.command) .reduce((acc, cur) => { if (cur in acc) { @@ -209,6 +269,43 @@ export const getPendingActionCounts = async ( return pending; }; +/** + * Returns a boolean for search result + * + * @param esClient + * @param actionIds + * @param agentIds + */ +const hasEndpointResponseDoc = async ({ + actionIds, + agentId, + esClient, +}: { + actionIds: string[]; + agentId: string; + esClient: ElasticsearchClient; +}): Promise => { + const response = await esClient + .search( + { + index: ENDPOINT_ACTION_RESPONSES_INDEX, + body: { + query: { + bool: { + filter: [{ terms: { action_id: actionIds } }, { term: { agent_id: agentId } }], + }, + }, + }, + }, + { ignore: [404] } + ) + .then( + (result) => result.body?.hits?.hits?.map((a) => a._source?.EndpointActions.action_id) || [] + ) + .catch(catchAndWrapError); + return response.filter((action): action is string => action !== undefined); +}; + /** * Returns back a map of elastic Agent IDs to array of Action IDs that have received a response. * @@ -217,16 +314,19 @@ export const getPendingActionCounts = async ( * @param actionIds * @param agentIds */ -const fetchActionResponseIds = async ( +const fetchActionResponses = async ( esClient: ElasticsearchClient, metadataService: EndpointMetadataService, actionIds: string[], agentIds: string[] -): Promise> => { - const actionResponsesByAgentId: Record = agentIds.reduce((acc, agentId) => { - acc[agentId] = []; - return acc; - }, {} as Record); +): Promise> => { + const actionResponsesByAgentId: Record = agentIds.reduce( + (acc, agentId) => { + acc[agentId] = []; + return acc; + }, + {} as Record + ); const actionResponses = await esClient .search( @@ -255,7 +355,7 @@ const fetchActionResponseIds = async ( return actionResponsesByAgentId; } - // Get the latest docs from the metadata datastream for the Elastic Agent IDs in the action responses + // Get the latest docs from the metadata data-stream for the Elastic Agent IDs in the action responses // This will be used determine if we should withhold the action id from the returned list in cases where // the Endpoint might not yet have sent an updated metadata document (which would be representative of // the state of the endpoint post-action) @@ -288,7 +388,7 @@ const fetchActionResponseIds = async ( enoughTimeHasLapsed || lastEndpointMetadataEventTimestamp > actionCompletedAtTimestamp ) { - actionResponsesByAgentId[actionResponse.agent_id].push(actionResponse.action_id); + actionResponsesByAgentId[actionResponse.agent_id].push(actionResponse); } } From fa1a233d232ce261699d082c289478949138a0b7 Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Tue, 19 Oct 2021 17:01:52 -0500 Subject: [PATCH 096/204] Development and build support for ARM based Apple devices (#114879) --- .bazeliskversion | 2 +- .bazelversion | 2 +- WORKSPACE.bazel | 1 + src/dev/build/lib/config.test.ts | 3 ++- src/dev/build/lib/platform.test.ts | 3 +++ src/dev/build/lib/platform.ts | 1 + src/dev/build/tasks/install_chromium.js | 6 ++++- .../nodejs/download_node_builds_task.test.ts | 9 +++++++ .../nodejs/extract_node_builds_task.test.ts | 7 ++++++ .../verify_existing_node_builds_task.test.ts | 24 +++++++++++++++++++ .../build/tasks/patch_native_modules_task.ts | 4 ++++ 11 files changed, 58 insertions(+), 4 deletions(-) diff --git a/.bazeliskversion b/.bazeliskversion index 6a126f402d53d..4dae2985b58cc 100644 --- a/.bazeliskversion +++ b/.bazeliskversion @@ -1 +1 @@ -1.7.5 +1.10.1 diff --git a/.bazelversion b/.bazelversion index fcdb2e109f68c..fae6e3d04b2ca 100644 --- a/.bazelversion +++ b/.bazelversion @@ -1 +1 @@ -4.0.0 +4.2.1 diff --git a/WORKSPACE.bazel b/WORKSPACE.bazel index 287b376037abe..d3c44eab2a526 100644 --- a/WORKSPACE.bazel +++ b/WORKSPACE.bazel @@ -28,6 +28,7 @@ check_rules_nodejs_version(minimum_version_string = "3.8.0") node_repositories( node_repositories = { "16.11.1-darwin_amd64": ("node-v16.11.1-darwin-x64.tar.gz", "node-v16.11.1-darwin-x64", "ba54b8ed504bd934d03eb860fefe991419b4209824280d4274f6a911588b5e45"), + "16.11.1-darwin_arm64": ("node-v16.11.1-darwin-arm64.tar.gz", "node-v16.11.1-darwin-arm64", "5e772e478390fab3001b7148a923e4f22fca50170000f18b28475337d3a97248"), "16.11.1-linux_arm64": ("node-v16.11.1-linux-arm64.tar.xz", "node-v16.11.1-linux-arm64", "083fc51f0ea26de9041aaf9821874651a9fd3b20d1cf57071ce6b523a0436f17"), "16.11.1-linux_s390x": ("node-v16.11.1-linux-s390x.tar.xz", "node-v16.11.1-linux-s390x", "855b5c83c2ccb05273d50bb04376335c68d47df57f3187cdebe1f22b972d2825"), "16.11.1-linux_amd64": ("node-v16.11.1-linux-x64.tar.xz", "node-v16.11.1-linux-x64", "493bcc9b660eff983a6de65a0f032eb2717f57207edf74c745bcb86e360310b3"), diff --git a/src/dev/build/lib/config.test.ts b/src/dev/build/lib/config.test.ts index 8bc5eb70c9437..b2afe3337230d 100644 --- a/src/dev/build/lib/config.test.ts +++ b/src/dev/build/lib/config.test.ts @@ -107,6 +107,7 @@ describe('#getTargetPlatforms()', () => { .sort() ).toMatchInlineSnapshot(` Array [ + "darwin-arm64", "darwin-x64", "linux-arm64", "linux-x64", @@ -132,7 +133,7 @@ describe('#getNodePlatforms()', () => { .getTargetPlatforms() .map((p) => p.getNodeArch()) .sort() - ).toEqual(['darwin-x64', 'linux-arm64', 'linux-x64', 'win32-x64']); + ).toEqual(['darwin-arm64', 'darwin-x64', 'linux-arm64', 'linux-x64', 'win32-x64']); }); it('returns this platform and linux, when targetAllPlatforms = false', async () => { diff --git a/src/dev/build/lib/platform.test.ts b/src/dev/build/lib/platform.test.ts index 79b07cac09cfc..193579d1a35c1 100644 --- a/src/dev/build/lib/platform.test.ts +++ b/src/dev/build/lib/platform.test.ts @@ -31,6 +31,7 @@ describe('isWindows()', () => { expect(new Platform('win32', 'x64', 'foo').isWindows()).toBe(true); expect(new Platform('linux', 'x64', 'foo').isWindows()).toBe(false); expect(new Platform('darwin', 'x64', 'foo').isWindows()).toBe(false); + expect(new Platform('darwin', 'arm64', 'foo').isWindows()).toBe(false); }); }); @@ -39,6 +40,7 @@ describe('isLinux()', () => { expect(new Platform('win32', 'x64', 'foo').isLinux()).toBe(false); expect(new Platform('linux', 'x64', 'foo').isLinux()).toBe(true); expect(new Platform('darwin', 'x64', 'foo').isLinux()).toBe(false); + expect(new Platform('darwin', 'arm64', 'foo').isLinux()).toBe(false); }); }); @@ -47,5 +49,6 @@ describe('isMac()', () => { expect(new Platform('win32', 'x64', 'foo').isMac()).toBe(false); expect(new Platform('linux', 'x64', 'foo').isMac()).toBe(false); expect(new Platform('darwin', 'x64', 'foo').isMac()).toBe(true); + expect(new Platform('darwin', 'arm64', 'foo').isMac()).toBe(true); }); }); diff --git a/src/dev/build/lib/platform.ts b/src/dev/build/lib/platform.ts index 2df7801ffc10e..4c4ec271318d6 100644 --- a/src/dev/build/lib/platform.ts +++ b/src/dev/build/lib/platform.ts @@ -49,5 +49,6 @@ export const ALL_PLATFORMS = [ new Platform('linux', 'x64', 'linux-x86_64'), new Platform('linux', 'arm64', 'linux-aarch64'), new Platform('darwin', 'x64', 'darwin-x86_64'), + new Platform('darwin', 'arm64', 'darwin-aarch64'), new Platform('win32', 'x64', 'windows-x86_64'), ]; diff --git a/src/dev/build/tasks/install_chromium.js b/src/dev/build/tasks/install_chromium.js index 95e0df8984f9d..ad60019ea81a4 100644 --- a/src/dev/build/tasks/install_chromium.js +++ b/src/dev/build/tasks/install_chromium.js @@ -16,7 +16,11 @@ export const InstallChromium = { async run(config, log, build) { for (const platform of config.getNodePlatforms()) { - log.info(`Installing Chromium for ${platform.getName()}-${platform.getArchitecture()}`); + const target = `${platform.getName()}-${platform.getArchitecture()}`; + log.info(`Installing Chromium for ${target}`); + + // revert after https://github.com/elastic/kibana/issues/109949 + if (target === 'darwin-arm64') continue; const { binaryPath$ } = installBrowser( log, diff --git a/src/dev/build/tasks/nodejs/download_node_builds_task.test.ts b/src/dev/build/tasks/nodejs/download_node_builds_task.test.ts index ca43f78a40cfd..31374d2050971 100644 --- a/src/dev/build/tasks/nodejs/download_node_builds_task.test.ts +++ b/src/dev/build/tasks/nodejs/download_node_builds_task.test.ts @@ -98,6 +98,15 @@ it('downloads node builds for each platform', async () => { "url": "darwin:url", }, ], + Array [ + Object { + "destination": "darwin:downloadPath", + "log": , + "retries": 3, + "sha256": "darwin:sha256", + "url": "darwin:url", + }, + ], Array [ Object { "destination": "win32:downloadPath", diff --git a/src/dev/build/tasks/nodejs/extract_node_builds_task.test.ts b/src/dev/build/tasks/nodejs/extract_node_builds_task.test.ts index 37a017ed083d0..9f869b99c18ae 100644 --- a/src/dev/build/tasks/nodejs/extract_node_builds_task.test.ts +++ b/src/dev/build/tasks/nodejs/extract_node_builds_task.test.ts @@ -105,6 +105,13 @@ it('runs expected fs operations', async () => { "strip": 1, }, ], + Array [ + /.node_binaries//node-v-darwin-arm64.tar.gz, + /.node_binaries//darwin-arm64, + Object { + "strip": 1, + }, + ], ], } `); diff --git a/src/dev/build/tasks/nodejs/verify_existing_node_builds_task.test.ts b/src/dev/build/tasks/nodejs/verify_existing_node_builds_task.test.ts index b097deb46f61c..c636db145694c 100644 --- a/src/dev/build/tasks/nodejs/verify_existing_node_builds_task.test.ts +++ b/src/dev/build/tasks/nodejs/verify_existing_node_builds_task.test.ts @@ -98,6 +98,7 @@ it('checks shasums for each downloaded node build', async () => { Object { "type": "return", "value": Object { + "darwin:darwin-arm64:downloadName": "valid shasum", "darwin:darwin-x64:downloadName": "valid shasum", "linux:linux-arm64:downloadName": "valid shasum", "linux:linux-x64:downloadName": "valid shasum", @@ -134,6 +135,14 @@ it('checks shasums for each downloaded node build', async () => { "name": "darwin", }, ], + Array [ + , + Platform { + "architecture": "arm64", + "buildName": "darwin-aarch64", + "name": "darwin", + }, + ], Array [ , Platform { @@ -165,6 +174,13 @@ it('checks shasums for each downloaded node build', async () => { "downloadPath": "darwin:darwin-x64:downloadPath", }, }, + Object { + "type": "return", + "value": Object { + "downloadName": "darwin:darwin-arm64:downloadName", + "downloadPath": "darwin:darwin-arm64:downloadPath", + }, + }, Object { "type": "return", "value": Object { @@ -190,6 +206,10 @@ it('checks shasums for each downloaded node build', async () => { "darwin:darwin-x64:downloadPath", "sha256", ], + Array [ + "darwin:darwin-arm64:downloadPath", + "sha256", + ], Array [ "win32:win32-x64:downloadPath", "sha256", @@ -212,6 +232,10 @@ it('checks shasums for each downloaded node build', async () => { "type": "return", "value": "valid shasum", }, + Object { + "type": "return", + "value": "valid shasum", + }, ], } `); diff --git a/src/dev/build/tasks/patch_native_modules_task.ts b/src/dev/build/tasks/patch_native_modules_task.ts index bb2b9cc96b677..37cb729053785 100644 --- a/src/dev/build/tasks/patch_native_modules_task.ts +++ b/src/dev/build/tasks/patch_native_modules_task.ts @@ -58,6 +58,10 @@ const packages: Package[] = [ url: 'https://storage.googleapis.com/kibana-ci-proxy-cache/node-re2/uhop/node-re2/releases/download/1.16.0/linux-arm64-93.gz', sha256: '7a786e0b75985e5aafdefa9af55cad8e85e69a3326f16d8c63d21d6b5b3bff1b', }, + 'darwin-arm64': { + url: 'https://storage.googleapis.com/kibana-ci-proxy-cache/node-re2/uhop/node-re2/releases/download/1.16.0/darwin-arm64-93.gz', + sha256: '28b540cdddf13578f1bd28a03e29ffdc26a7f00ec859c369987b8d51ec6357c8', + }, 'win32-x64': { url: 'https://github.com/uhop/node-re2/releases/download/1.16.0/win32-x64-93.gz', sha256: '37245ceb59a086b5e7e9de8746a3cdf148c383be9ae2580f92baea90d0d39947', From 94e1144ba7b82b878ee0ef2dad0265a947c64bdd Mon Sep 17 00:00:00 2001 From: Scotty Bollinger Date: Tue, 19 Oct 2021 17:09:40 -0500 Subject: [PATCH 097/204] [App Search] Update copy on product selector page (#115637) https://github.com/elastic/enterprise-search-team/issues/759#issuecomment-947032787 --- x-pack/plugins/enterprise_search/common/constants.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/enterprise_search/common/constants.ts b/x-pack/plugins/enterprise_search/common/constants.ts index bc60c917094f8..f84668068f413 100644 --- a/x-pack/plugins/enterprise_search/common/constants.ts +++ b/x-pack/plugins/enterprise_search/common/constants.ts @@ -32,7 +32,8 @@ export const APP_SEARCH_PLUGIN = { 'Leverage dashboards, analytics, and APIs for advanced application search made simple.', }), CARD_DESCRIPTION: i18n.translate('xpack.enterpriseSearch.appSearch.productCardDescription', { - defaultMessage: 'Design and deploy a powerful search to your websites and apps.', + defaultMessage: + 'Design, deploy, and manage powerful search experiences for your websites and web/mobile apps.', }), URL: '/app/enterprise_search/app_search', SUPPORT_URL: 'https://discuss.elastic.co/c/enterprise-search/app-search/', From 0c9e4b2decfad52d29f6bbbccfd8f0cde63a6151 Mon Sep 17 00:00:00 2001 From: Tyler Smalley Date: Tue, 19 Oct 2021 15:29:17 -0700 Subject: [PATCH 098/204] [ci] Re-add removed file still used in Jenkins Signed-off-by: Tyler Smalley --- src/dev/ci_setup/.bazelrc-ci | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 src/dev/ci_setup/.bazelrc-ci diff --git a/src/dev/ci_setup/.bazelrc-ci b/src/dev/ci_setup/.bazelrc-ci new file mode 100644 index 0000000000000..a0a0c3de73405 --- /dev/null +++ b/src/dev/ci_setup/.bazelrc-ci @@ -0,0 +1,5 @@ +# Generated by .buildkite/scripts/common/setup_bazel.sh + +import %workspace%/.bazelrc.common + +build --build_metadata=ROLE=CI From 4f1e07117c720e104184fece229117d5728520f0 Mon Sep 17 00:00:00 2001 From: Michael Olorunnisola Date: Tue, 19 Oct 2021 18:51:06 -0400 Subject: [PATCH 099/204] [Security Solution][Timelines] - Resolve UI (#114350) --- .../template_wrapper/bottom_bar/index.tsx | 2 + .../common/components/url_state/index.tsx | 1 + .../url_state/initialize_redux_by_url.tsx | 2 +- ...query_timeline_by_id_on_url_change.test.ts | 140 +++++++++++++++ .../query_timeline_by_id_on_url_change.ts | 84 +++++++++ .../common/components/url_state/types.ts | 1 + .../components/url_state/use_url_state.tsx | 16 +- .../hooks/use_resolve_conflict.test.tsx | 166 ++++++++++++++++++ .../common/hooks/use_resolve_conflict.tsx | 100 +++++++++++ .../common/hooks/use_resolve_redirect.test.ts | 140 +++++++++++++++ .../common/hooks/use_resolve_redirect.ts | 89 ++++++++++ .../open_timeline/__mocks__/index.ts | 6 +- .../components/open_timeline/helpers.test.ts | 28 +-- .../components/open_timeline/helpers.ts | 21 ++- .../components/open_timeline/types.ts | 7 + .../components/timeline/index.test.tsx | 6 + .../timelines/components/timeline/index.tsx | 4 +- .../public/timelines/containers/api.ts | 13 -- .../timelines/store/timeline/actions.ts | 2 + .../timelines/store/timeline/defaults.ts | 121 ++++++------- .../timelines/store/timeline/helpers.ts | 4 + .../public/timelines/store/timeline/model.ts | 2 + .../timelines/store/timeline/reducer.ts | 9 +- .../plugins/security_solution/public/types.ts | 2 +- .../plugins/security_solution/tsconfig.json | 5 +- 25 files changed, 868 insertions(+), 103 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/common/components/url_state/query_timeline_by_id_on_url_change.test.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/url_state/query_timeline_by_id_on_url_change.ts create mode 100644 x-pack/plugins/security_solution/public/common/hooks/use_resolve_conflict.test.tsx create mode 100644 x-pack/plugins/security_solution/public/common/hooks/use_resolve_conflict.tsx create mode 100644 x-pack/plugins/security_solution/public/common/hooks/use_resolve_redirect.test.ts create mode 100644 x-pack/plugins/security_solution/public/common/hooks/use_resolve_redirect.ts diff --git a/x-pack/plugins/security_solution/public/app/home/template_wrapper/bottom_bar/index.tsx b/x-pack/plugins/security_solution/public/app/home/template_wrapper/bottom_bar/index.tsx index eb606cd8ff583..8e972b92c2fa1 100644 --- a/x-pack/plugins/security_solution/public/app/home/template_wrapper/bottom_bar/index.tsx +++ b/x-pack/plugins/security_solution/public/app/home/template_wrapper/bottom_bar/index.tsx @@ -16,6 +16,7 @@ import { useSourcererScope, getScopeFromPath } from '../../../../common/containe import { TimelineId } from '../../../../../common/types/timeline'; import { AutoSaveWarningMsg } from '../../../../timelines/components/timeline/auto_save_warning'; import { Flyout } from '../../../../timelines/components/flyout'; +import { useResolveRedirect } from '../../../../common/hooks/use_resolve_redirect'; export const BOTTOM_BAR_CLASSNAME = 'timeline-bottom-bar'; @@ -26,6 +27,7 @@ export const SecuritySolutionBottomBar = React.memo( const [showTimeline] = useShowTimeline(); const { indicesExist } = useSourcererScope(getScopeFromPath(pathname)); + useResolveRedirect(); return indicesExist && showTimeline ? ( <> diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/index.tsx b/x-pack/plugins/security_solution/public/common/components/url_state/index.tsx index 281db88ebd057..2e04bbc5f1daf 100644 --- a/x-pack/plugins/security_solution/public/common/components/url_state/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/url_state/index.tsx @@ -25,6 +25,7 @@ export const UseUrlStateMemo = React.memo( prevProps.pathName === nextProps.pathName && deepEqual(prevProps.urlState, nextProps.urlState) && deepEqual(prevProps.indexPattern, nextProps.indexPattern) && + prevProps.search === nextProps.search && deepEqual(prevProps.navTabs, nextProps.navTabs) ); diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/initialize_redux_by_url.tsx b/x-pack/plugins/security_solution/public/common/components/url_state/initialize_redux_by_url.tsx index 4a448e9064090..f04cf30da61f5 100644 --- a/x-pack/plugins/security_solution/public/common/components/url_state/initialize_redux_by_url.tsx +++ b/x-pack/plugins/security_solution/public/common/components/url_state/initialize_redux_by_url.tsx @@ -124,7 +124,7 @@ export const useSetInitialStateFromUrl = () => { [dispatch, updateTimeline, updateTimelineIsLoading] ); - return setInitialStateFromUrl; + return Object.freeze({ setInitialStateFromUrl, updateTimeline, updateTimelineIsLoading }); }; const updateTimerange = (newUrlStateString: string, dispatch: Dispatch) => { diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/query_timeline_by_id_on_url_change.test.ts b/x-pack/plugins/security_solution/public/common/components/url_state/query_timeline_by_id_on_url_change.test.ts new file mode 100644 index 0000000000000..5cc4f8e8b80f9 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/url_state/query_timeline_by_id_on_url_change.test.ts @@ -0,0 +1,140 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { queryTimelineById } from '../../../timelines/components/open_timeline/helpers'; +import { queryTimelineByIdOnUrlChange } from './query_timeline_by_id_on_url_change'; +import * as urlHelpers from './helpers'; + +jest.mock('../../../timelines/components/open_timeline/helpers'); + +describe('queryTimelineByIdOnUrlChange', () => { + const oldTestTimelineId = '04e8ffb0-2c2a-11ec-949c-39005af91f70'; + const newTestTimelineId = `${oldTestTimelineId}-newId`; + const oldTimelineRisonSearchString = `?timeline=(activeTab:query,graphEventId:%27%27,id:%27${oldTestTimelineId}%27,isOpen:!t)`; + const newTimelineRisonSearchString = `?timeline=(activeTab:query,graphEventId:%27%27,id:%27${newTestTimelineId}%27,isOpen:!t)`; + const mockUpdateTimeline = jest.fn(); + const mockUpdateTimelineIsLoading = jest.fn(); + const mockQueryTimelineById = jest.fn(); + beforeEach(() => { + (queryTimelineById as jest.Mock).mockImplementation(mockQueryTimelineById); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('when search strings are empty', () => { + it('should not call queryTimelineById', () => { + queryTimelineByIdOnUrlChange({ + oldSearch: '', + search: '', + timelineIdFromReduxStore: 'current-timeline-id', + updateTimeline: mockUpdateTimeline, + updateTimelineIsLoading: mockUpdateTimelineIsLoading, + }); + + expect(queryTimelineById).not.toBeCalled(); + }); + }); + + describe('when search string has not changed', () => { + it('should not call queryTimelineById', () => { + queryTimelineByIdOnUrlChange({ + oldSearch: oldTimelineRisonSearchString, + search: oldTimelineRisonSearchString, + timelineIdFromReduxStore: 'timeline-id', + updateTimeline: mockUpdateTimeline, + updateTimelineIsLoading: mockUpdateTimelineIsLoading, + }); + + expect(queryTimelineById).not.toBeCalled(); + }); + }); + + describe('when decode rison fails', () => { + it('should not call queryTimelineById', () => { + jest.spyOn(urlHelpers, 'decodeRisonUrlState').mockImplementationOnce(() => { + throw new Error('Unable to decode'); + }); + + queryTimelineByIdOnUrlChange({ + oldSearch: oldTimelineRisonSearchString, + search: newTimelineRisonSearchString, + timelineIdFromReduxStore: '', + updateTimeline: mockUpdateTimeline, + updateTimelineIsLoading: mockUpdateTimelineIsLoading, + }); + + expect(queryTimelineById).not.toBeCalled(); + }); + }); + + describe('when new id is not provided', () => { + it('should not call queryTimelineById', () => { + queryTimelineByIdOnUrlChange({ + oldSearch: oldTimelineRisonSearchString, + search: '?timeline=(activeTab:query)', // no id + timelineIdFromReduxStore: newTestTimelineId, + updateTimeline: mockUpdateTimeline, + updateTimelineIsLoading: mockUpdateTimelineIsLoading, + }); + + expect(queryTimelineById).not.toBeCalled(); + }); + }); + + describe('when new id matches the data in redux', () => { + it('should not call queryTimelineById', () => { + queryTimelineByIdOnUrlChange({ + oldSearch: oldTimelineRisonSearchString, + search: newTimelineRisonSearchString, + timelineIdFromReduxStore: newTestTimelineId, + updateTimeline: mockUpdateTimeline, + updateTimelineIsLoading: mockUpdateTimelineIsLoading, + }); + + expect(queryTimelineById).not.toBeCalled(); + }); + }); + + // You can only redirect or run into conflict scenarios when already viewing a timeline + describe('when not actively on a page with timeline in the search field', () => { + it('should not call queryTimelineById', () => { + queryTimelineByIdOnUrlChange({ + oldSearch: '?random=foo', + search: newTimelineRisonSearchString, + timelineIdFromReduxStore: oldTestTimelineId, + updateTimeline: mockUpdateTimeline, + updateTimelineIsLoading: mockUpdateTimelineIsLoading, + }); + + expect(queryTimelineById).not.toBeCalled(); + }); + }); + + describe('when an old timeline id exists, but a new id is given', () => { + it('should call queryTimelineById', () => { + queryTimelineByIdOnUrlChange({ + oldSearch: oldTimelineRisonSearchString, + search: newTimelineRisonSearchString, + timelineIdFromReduxStore: oldTestTimelineId, + updateTimeline: mockUpdateTimeline, + updateTimelineIsLoading: mockUpdateTimelineIsLoading, + }); + + expect(queryTimelineById).toBeCalledWith({ + activeTimelineTab: 'query', + duplicate: false, + graphEventId: '', + timelineId: newTestTimelineId, + openTimeline: true, + updateIsLoading: mockUpdateTimelineIsLoading, + updateTimeline: mockUpdateTimeline, + }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/query_timeline_by_id_on_url_change.ts b/x-pack/plugins/security_solution/public/common/components/url_state/query_timeline_by_id_on_url_change.ts new file mode 100644 index 0000000000000..2778cefdc7953 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/url_state/query_timeline_by_id_on_url_change.ts @@ -0,0 +1,84 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Action } from 'typescript-fsa'; +import { DispatchUpdateTimeline } from '../../../timelines/components/open_timeline/types'; +import { queryTimelineById } from '../../../timelines/components/open_timeline/helpers'; +import { TimelineTabs } from '../../../../common/types/timeline'; +import { + decodeRisonUrlState, + getQueryStringFromLocation, + getParamFromQueryString, +} from './helpers'; +import { TimelineUrl } from '../../../timelines/store/timeline/model'; +import { CONSTANTS } from './constants'; + +const getQueryStringKeyValue = ({ search, urlKey }: { search: string; urlKey: string }) => + getParamFromQueryString(getQueryStringFromLocation(search), urlKey); + +interface QueryTimelineIdOnUrlChange { + oldSearch?: string; + search: string; + timelineIdFromReduxStore: string; + updateTimeline: DispatchUpdateTimeline; + updateTimelineIsLoading: (status: { id: string; isLoading: boolean }) => Action<{ + id: string; + isLoading: boolean; + }>; +} + +/** + * After the initial load of the security solution, timeline is not updated when the timeline url search value is changed + * This is because those state changes happen in place and doesn't lead to a requerying of data for the new id. + * To circumvent this for the sake of the redirects needed for the saved object Id changes happening in 8.0 + * We are actively pulling the id changes that take place for timeline in the url and calling the query below + * to request the new data. + */ +export const queryTimelineByIdOnUrlChange = ({ + oldSearch, + search, + timelineIdFromReduxStore, + updateTimeline, + updateTimelineIsLoading, +}: QueryTimelineIdOnUrlChange) => { + const oldUrlStateString = getQueryStringKeyValue({ + urlKey: CONSTANTS.timeline, + search: oldSearch ?? '', + }); + + const newUrlStateString = getQueryStringKeyValue({ urlKey: CONSTANTS.timeline, search }); + + if (oldUrlStateString != null && newUrlStateString != null) { + let newTimeline = null; + let oldTimeline = null; + try { + newTimeline = decodeRisonUrlState(newUrlStateString); + } catch (error) { + // do nothing as timeline is defaulted to null + } + + try { + oldTimeline = decodeRisonUrlState(oldUrlStateString); + } catch (error) { + // do nothing as timeline is defaulted to null + } + const newId = newTimeline?.id; + const oldId = oldTimeline?.id; + + if (newId && newId !== oldId && newId !== timelineIdFromReduxStore) { + queryTimelineById({ + activeTimelineTab: newTimeline?.activeTab ?? TimelineTabs.query, + duplicate: false, + graphEventId: newTimeline?.graphEventId, + timelineId: newId, + openTimeline: true, + updateIsLoading: updateTimelineIsLoading, + updateTimeline, + }); + } + } +}; diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/types.ts b/x-pack/plugins/security_solution/public/common/components/url_state/types.ts index e803c091423be..06ed33ac69f6e 100644 --- a/x-pack/plugins/security_solution/public/common/components/url_state/types.ts +++ b/x-pack/plugins/security_solution/public/common/components/url_state/types.ts @@ -79,6 +79,7 @@ export interface PreviousLocationUrlState { pathName: string | undefined; pageName: string | undefined; urlState: UrlState; + search: string | undefined; } export interface UrlStateToRedux { diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/use_url_state.tsx b/x-pack/plugins/security_solution/public/common/components/url_state/use_url_state.tsx index bc47ba9d8ae99..3245d647227ad 100644 --- a/x-pack/plugins/security_solution/public/common/components/url_state/use_url_state.tsx +++ b/x-pack/plugins/security_solution/public/common/components/url_state/use_url_state.tsx @@ -39,6 +39,7 @@ import { } from './types'; import { TimelineUrl } from '../../../timelines/store/timeline/model'; import { UrlInputsModel } from '../../store/inputs/model'; +import { queryTimelineByIdOnUrlChange } from './query_timeline_by_id_on_url_change'; function usePrevious(value: PreviousLocationUrlState) { const ref = useRef(value); @@ -60,9 +61,10 @@ export const useUrlStateHooks = ({ const [isFirstPageLoad, setIsFirstPageLoad] = useState(true); const { filterManager, savedQueries } = useKibana().services.data.query; const { pathname: browserPathName } = useLocation(); - const prevProps = usePrevious({ pathName, pageName, urlState }); + const prevProps = usePrevious({ pathName, pageName, urlState, search }); - const setInitialStateFromUrl = useSetInitialStateFromUrl(); + const { setInitialStateFromUrl, updateTimeline, updateTimelineIsLoading } = + useSetInitialStateFromUrl(); const handleInitialize = useCallback( (type: UrlStateType) => { @@ -190,6 +192,16 @@ export const useUrlStateHooks = ({ document.title = `${getTitle(pageName, navTabs)} - Kibana`; }, [pageName, navTabs]); + useEffect(() => { + queryTimelineByIdOnUrlChange({ + oldSearch: prevProps.search, + search, + timelineIdFromReduxStore: urlState.timeline.id, + updateTimeline, + updateTimelineIsLoading, + }); + }, [search, prevProps.search, urlState.timeline.id, updateTimeline, updateTimelineIsLoading]); + return null; }; diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_resolve_conflict.test.tsx b/x-pack/plugins/security_solution/public/common/hooks/use_resolve_conflict.test.tsx new file mode 100644 index 0000000000000..bafbe078cdbdb --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/hooks/use_resolve_conflict.test.tsx @@ -0,0 +1,166 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { useLocation } from 'react-router-dom'; +import { renderHook } from '@testing-library/react-hooks'; +import { useDeepEqualSelector } from './use_selector'; +import { useKibana } from '../lib/kibana'; +import { useResolveConflict } from './use_resolve_conflict'; +import * as urlHelpers from '../components/url_state/helpers'; + +jest.mock('react-router-dom', () => { + const original = jest.requireActual('react-router-dom'); + + return { + ...original, + useLocation: jest.fn(), + }; +}); +jest.mock('../lib/kibana'); +jest.mock('./use_selector'); +jest.mock('../../timelines/store/timeline/', () => ({ + timelineSelectors: { + getTimelineByIdSelector: () => jest.fn(), + }, +})); + +describe('useResolveConflict', () => { + const mockGetLegacyUrlConflict = jest.fn().mockReturnValue('Test!'); + beforeEach(() => { + jest.resetAllMocks(); + // Mock rison format in actual url + (useLocation as jest.Mock).mockReturnValue({ + pathname: 'my/cool/path', + search: + 'timeline=(activeTab:query,graphEventId:%27%27,id:%2704e8ffb0-2c2a-11ec-949c-39005af91f70%27,isOpen:!t)', + }); + (useKibana as jest.Mock).mockReturnValue({ + services: { + spaces: { + ui: { + components: { + getLegacyUrlConflict: mockGetLegacyUrlConflict, + }, + }, + }, + }, + }); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('resolve object is not provided', () => { + it('should not show the conflict message', async () => { + (useDeepEqualSelector as jest.Mock).mockImplementation(() => ({ + savedObjectId: 'current-saved-object-id', + activeTab: 'some-tab', + graphEventId: 'current-graph-event-id', + show: false, + })); + const { result } = renderHook<{}, JSX.Element | null>(() => useResolveConflict()); + expect(mockGetLegacyUrlConflict).not.toHaveBeenCalled(); + expect(result.current).toEqual(null); + }); + }); + + describe('outcome is exactMatch', () => { + it('should not show the conflict message', async () => { + (useDeepEqualSelector as jest.Mock).mockImplementation(() => ({ + resolveTimelineConfig: { + outcome: 'exactMatch', + }, + savedObjectId: 'current-saved-object-id', + activeTab: 'some-tab', + graphEventId: 'current-graph-event-id', + show: false, + })); + const { result } = renderHook<{}, JSX.Element | null>(() => useResolveConflict()); + expect(mockGetLegacyUrlConflict).not.toHaveBeenCalled(); + expect(result.current).toEqual(null); + }); + }); + + describe('outcome is aliasMatch', () => { + it('should not show the conflict message', async () => { + (useDeepEqualSelector as jest.Mock).mockImplementation(() => ({ + resolveTimelineConfig: { + outcome: 'aliasMatch', + alias_target_id: 'new-id', + }, + })); + const { result } = renderHook<{}, JSX.Element | null>(() => useResolveConflict()); + expect(mockGetLegacyUrlConflict).not.toHaveBeenCalled(); + expect(result.current).toEqual(null); + }); + }); + + describe('outcome is conflict', () => { + const mockTextContent = 'I am the visible conflict message'; + it('should show the conflict message', async () => { + (useDeepEqualSelector as jest.Mock).mockImplementation(() => ({ + resolveTimelineConfig: { + outcome: 'conflict', + alias_target_id: 'new-id', + }, + })); + mockGetLegacyUrlConflict.mockImplementation(() => mockTextContent); + const { result } = renderHook<{}, JSX.Element | null>(() => useResolveConflict()); + expect(mockGetLegacyUrlConflict).toHaveBeenCalledWith({ + objectNoun: 'timeline', + currentObjectId: '04e8ffb0-2c2a-11ec-949c-39005af91f70', + otherObjectId: 'new-id', + otherObjectPath: + 'my/cool/path?timeline=%28activeTab%3Aquery%2CgraphEventId%3A%27%27%2Cid%3Anew-id%2CisOpen%3A%21t%29', + }); + expect(result.current).toMatchInlineSnapshot(` + + I am the visible conflict message + + + `); + }); + + describe('rison is unable to be decoded', () => { + it('should use timeline values from redux to create the otherObjectPath', async () => { + jest.spyOn(urlHelpers, 'decodeRisonUrlState').mockImplementation(() => { + throw new Error('Unable to decode'); + }); + (useLocation as jest.Mock).mockReturnValue({ + pathname: 'my/cool/path', + search: '?foo=bar', + }); + (useDeepEqualSelector as jest.Mock).mockImplementation(() => ({ + resolveTimelineConfig: { + outcome: 'conflict', + alias_target_id: 'new-id', + }, + savedObjectId: 'current-saved-object-id', + activeTab: 'some-tab', + graphEventId: 'current-graph-event-id', + show: false, + })); + mockGetLegacyUrlConflict.mockImplementation(() => mockTextContent); + renderHook(() => useResolveConflict()); + const { result } = renderHook<{}, JSX.Element | null>(() => useResolveConflict()); + expect(mockGetLegacyUrlConflict).toHaveBeenCalledWith({ + objectNoun: 'timeline', + currentObjectId: 'current-saved-object-id', + otherObjectId: 'new-id', + otherObjectPath: + 'my/cool/path?foo=bar&timeline=%28activeTab%3Asome-tab%2CgraphEventId%3Acurrent-graph-event-id%2Cid%3Anew-id%2CisOpen%3A%21f%29', + }); + expect(result.current).toMatchInlineSnapshot(` + + I am the visible conflict message + + + `); + }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_resolve_conflict.tsx b/x-pack/plugins/security_solution/public/common/hooks/use_resolve_conflict.tsx new file mode 100644 index 0000000000000..6a493d944ecda --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/hooks/use_resolve_conflict.tsx @@ -0,0 +1,100 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback, useMemo } from 'react'; +import { useLocation } from 'react-router-dom'; +import { EuiSpacer } from '@elastic/eui'; +import { useDeepEqualSelector } from './use_selector'; +import { TimelineId } from '../../../common/types/timeline'; +import { timelineSelectors } from '../../timelines/store/timeline'; +import { TimelineUrl } from '../../timelines/store/timeline/model'; +import { timelineDefaults } from '../../timelines/store/timeline/defaults'; +import { decodeRisonUrlState, encodeRisonUrlState } from '../components/url_state/helpers'; +import { useKibana } from '../lib/kibana'; +import { CONSTANTS } from '../components/url_state/constants'; + +/** + * Unfortunately the url change initiated when clicking the button to otherObjectPath doesn't seem to be + * respected by the useSetInitialStateFromUrl here: x-pack/plugins/security_solution/public/common/components/url_state/initialize_redux_by_url.tsx + * + * FYI: It looks like the routing causes replaceStateInLocation to be called instead: + * x-pack/plugins/security_solution/public/common/components/url_state/helpers.ts + * + * Potentially why the markdown component needs a click handler as well for timeline? + * see: /x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/timeline/processor.tsx + */ +export const useResolveConflict = () => { + const { search, pathname } = useLocation(); + const { spaces } = useKibana().services; + const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []); + const { resolveTimelineConfig, savedObjectId, show, graphEventId, activeTab } = + useDeepEqualSelector((state) => getTimeline(state, TimelineId.active) ?? timelineDefaults); + + const getLegacyUrlConflictCallout = useCallback(() => { + // This function returns a callout component *if* we have encountered a "legacy URL conflict" scenario + if ( + !spaces || + resolveTimelineConfig?.outcome !== 'conflict' || + resolveTimelineConfig?.alias_target_id == null + ) { + return null; + } + + const searchQuery = new URLSearchParams(search); + const timelineRison = searchQuery.get(CONSTANTS.timeline) ?? undefined; + // Try to get state on URL, but default to what's in Redux in case of decodeRisonFailure + const currentTimelineState = { + id: savedObjectId ?? '', + isOpen: !!show, + activeTab, + graphEventId, + }; + let timelineSearch: TimelineUrl = currentTimelineState; + try { + timelineSearch = decodeRisonUrlState(timelineRison) ?? currentTimelineState; + } catch (error) { + // do nothing as it's already defaulted on line 77 + } + // We have resolved to one object, but another object has a legacy URL alias associated with this ID/page. We should display a + // callout with a warning for the user, and provide a way for them to navigate to the other object. + const currentObjectId = timelineSearch?.id; + const newSavedObjectId = resolveTimelineConfig?.alias_target_id ?? ''; // This is always defined if outcome === 'conflict' + + const newTimelineSearch: TimelineUrl = { + ...timelineSearch, + id: newSavedObjectId, + }; + const newTimelineRison = encodeRisonUrlState(newTimelineSearch); + searchQuery.set(CONSTANTS.timeline, newTimelineRison); + + const newPath = `${pathname}?${searchQuery.toString()}${window.location.hash}`; + + return ( + <> + {spaces.ui.components.getLegacyUrlConflict({ + objectNoun: CONSTANTS.timeline, + currentObjectId, + otherObjectId: newSavedObjectId, + otherObjectPath: newPath, + })} + + + ); + }, [ + activeTab, + graphEventId, + pathname, + resolveTimelineConfig?.alias_target_id, + resolveTimelineConfig?.outcome, + savedObjectId, + search, + show, + spaces, + ]); + + return useMemo(() => getLegacyUrlConflictCallout(), [getLegacyUrlConflictCallout]); +}; diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_resolve_redirect.test.ts b/x-pack/plugins/security_solution/public/common/hooks/use_resolve_redirect.test.ts new file mode 100644 index 0000000000000..c9a0eedefd0af --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/hooks/use_resolve_redirect.test.ts @@ -0,0 +1,140 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useLocation } from 'react-router-dom'; +import { renderHook } from '@testing-library/react-hooks'; +import { useDeepEqualSelector } from './use_selector'; +import { useKibana } from '../lib/kibana'; +import { useResolveRedirect } from './use_resolve_redirect'; +import * as urlHelpers from '../components/url_state/helpers'; + +jest.mock('react-router-dom', () => { + const original = jest.requireActual('react-router-dom'); + + return { + ...original, + useLocation: jest.fn(), + }; +}); +jest.mock('../lib/kibana'); +jest.mock('./use_selector'); +jest.mock('../../timelines/store/timeline/', () => ({ + timelineSelectors: { + getTimelineByIdSelector: () => jest.fn(), + }, +})); + +describe('useResolveRedirect', () => { + const mockRedirectLegacyUrl = jest.fn(); + beforeEach(() => { + jest.resetAllMocks(); + // Mock rison format in actual url + (useLocation as jest.Mock).mockReturnValue({ + pathname: 'my/cool/path', + search: + 'timeline=(activeTab:query,graphEventId:%27%27,id:%2704e8ffb0-2c2a-11ec-949c-39005af91f70%27,isOpen:!t)', + }); + (useKibana as jest.Mock).mockReturnValue({ + services: { + spaces: { + ui: { + redirectLegacyUrl: mockRedirectLegacyUrl, + }, + }, + }, + }); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('resolve object is not provided', () => { + it('should not redirect', async () => { + (useDeepEqualSelector as jest.Mock).mockImplementation(() => ({ + savedObjectId: 'current-saved-object-id', + activeTab: 'some-tab', + graphEventId: 'current-graph-event-id', + show: false, + })); + renderHook(() => useResolveRedirect()); + expect(mockRedirectLegacyUrl).not.toHaveBeenCalled(); + }); + }); + + describe('outcome is exactMatch', () => { + it('should not redirect', async () => { + (useDeepEqualSelector as jest.Mock).mockImplementation(() => ({ + resolveTimelineConfig: { + outcome: 'exactMatch', + }, + savedObjectId: 'current-saved-object-id', + activeTab: 'some-tab', + graphEventId: 'current-graph-event-id', + show: false, + })); + renderHook(() => useResolveRedirect()); + expect(mockRedirectLegacyUrl).not.toHaveBeenCalled(); + }); + }); + + describe('outcome is aliasMatch', () => { + it('should redirect to url with id:new-id if outcome is aliasMatch', async () => { + (useDeepEqualSelector as jest.Mock).mockImplementation(() => ({ + resolveTimelineConfig: { + outcome: 'aliasMatch', + alias_target_id: 'new-id', + }, + })); + renderHook(() => useResolveRedirect()); + expect(mockRedirectLegacyUrl).toHaveBeenCalledWith( + 'my/cool/path?timeline=%28activeTab%3Aquery%2CgraphEventId%3A%27%27%2Cid%3Anew-id%2CisOpen%3A%21t%29', + 'timeline' + ); + }); + + describe('rison is unable to be decoded', () => { + it('should use timeline values from redux to create the redirect path', async () => { + jest.spyOn(urlHelpers, 'decodeRisonUrlState').mockImplementation(() => { + throw new Error('Unable to decode'); + }); + (useLocation as jest.Mock).mockReturnValue({ + pathname: 'my/cool/path', + search: '?foo=bar', + }); + (useDeepEqualSelector as jest.Mock).mockImplementation(() => ({ + resolveTimelineConfig: { + outcome: 'aliasMatch', + alias_target_id: 'new-id', + }, + savedObjectId: 'current-saved-object-id', + activeTab: 'some-tab', + graphEventId: 'current-graph-event-id', + show: false, + })); + renderHook(() => useResolveRedirect()); + expect(mockRedirectLegacyUrl).toHaveBeenCalledWith( + 'my/cool/path?foo=bar&timeline=%28activeTab%3Asome-tab%2CgraphEventId%3Acurrent-graph-event-id%2Cid%3Anew-id%2CisOpen%3A%21f%29', + 'timeline' + ); + }); + }); + }); + + describe('outcome is conflict', () => { + it('should not redirect', async () => { + (useDeepEqualSelector as jest.Mock).mockImplementation(() => ({ + resolveTimelineConfig: { + outcome: 'conflict', + alias_target_id: 'new-id', + }, + })); + renderHook(() => useResolveRedirect()); + expect(mockRedirectLegacyUrl).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_resolve_redirect.ts b/x-pack/plugins/security_solution/public/common/hooks/use_resolve_redirect.ts new file mode 100644 index 0000000000000..a6ba0b24828e7 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/hooks/use_resolve_redirect.ts @@ -0,0 +1,89 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useLocation } from 'react-router-dom'; +import { useDeepEqualSelector } from './use_selector'; +import { TimelineId } from '../../../common/types/timeline'; +import { timelineSelectors } from '../../timelines/store/timeline/'; +import { timelineDefaults } from '../../timelines/store/timeline/defaults'; +import { decodeRisonUrlState, encodeRisonUrlState } from '../components/url_state/helpers'; +import { useKibana } from '../lib/kibana'; +import { TimelineUrl } from '../../timelines/store/timeline/model'; +import { CONSTANTS } from '../components/url_state/constants'; + +/** + * This hooks is specifically for use with the resolve api that was introduced as part of 7.16 + * If a deep link id has been migrated to a new id, this hook will cause a redirect to a url with + * the new ID. + */ + +export const useResolveRedirect = () => { + const { search, pathname } = useLocation(); + const [hasRedirected, updateHasRedirected] = useState(false); + const { spaces } = useKibana().services; + + const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []); + const { resolveTimelineConfig, savedObjectId, show, activeTab, graphEventId } = + useDeepEqualSelector((state) => getTimeline(state, TimelineId.active) ?? timelineDefaults); + + const redirect = useCallback(() => { + const searchQuery = new URLSearchParams(search); + const timelineRison = searchQuery.get(CONSTANTS.timeline) ?? undefined; + + // Try to get state on URL, but default to what's in Redux in case of decodeRisonFailure + const currentTimelineState = { + id: savedObjectId ?? '', + isOpen: !!show, + activeTab, + graphEventId, + }; + let timelineSearch: TimelineUrl = currentTimelineState; + try { + timelineSearch = decodeRisonUrlState(timelineRison) ?? currentTimelineState; + } catch (error) { + // do nothing as it's already defaulted on line 77 + } + + if ( + hasRedirected || + !spaces || + resolveTimelineConfig?.outcome !== 'aliasMatch' || + resolveTimelineConfig?.alias_target_id == null + ) { + return null; + } + + // We found this object by a legacy URL alias from its old ID; redirect the user to the page with its new ID, preserving any URL hash + const newObjectId = resolveTimelineConfig?.alias_target_id ?? ''; // This is always defined if outcome === 'aliasMatch' + const newTimelineSearch = { + ...timelineSearch, + id: newObjectId, + }; + const newTimelineRison = encodeRisonUrlState(newTimelineSearch); + searchQuery.set(CONSTANTS.timeline, newTimelineRison); + const newPath = `${pathname}?${searchQuery.toString()}`; + spaces.ui.redirectLegacyUrl(newPath, CONSTANTS.timeline); + // Prevent the effect from being called again as the url change takes place in location rather than a true redirect + updateHasRedirected(true); + }, [ + activeTab, + graphEventId, + hasRedirected, + pathname, + resolveTimelineConfig?.outcome, + resolveTimelineConfig?.alias_target_id, + savedObjectId, + search, + show, + spaces, + ]); + + useEffect(() => { + redirect(); + }, [redirect]); +}; diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/__mocks__/index.ts b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/__mocks__/index.ts index f1e1c42539eff..2521d14481ca8 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/__mocks__/index.ts @@ -9,7 +9,7 @@ import { TimelineStatus, TimelineType } from '../../../../../common/types/timeli export const mockTimeline = { data: { - getOneTimeline: { + timeline: { savedObjectId: 'eb2781c0-1df5-11eb-8589-2f13958b79f7', columns: [ { @@ -163,6 +163,7 @@ export const mockTimeline = { version: 'WzQ4NSwxXQ==', __typename: 'TimelineResult', }, + outcome: 'exactMatch', }, loading: false, networkStatus: 7, @@ -171,7 +172,7 @@ export const mockTimeline = { export const mockTemplate = { data: { - getOneTimeline: { + timeline: { savedObjectId: '0c70a200-1de0-11eb-885c-6fc13fca1850', columns: [ { @@ -416,6 +417,7 @@ export const mockTemplate = { version: 'WzQwMywxXQ==', __typename: 'TimelineResult', }, + outcome: 'exactMatch', }, loading: false, networkStatus: 7, diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts index 5d52d2c8a4d48..1b93f1556a95c 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts @@ -50,7 +50,7 @@ import { mockTimeline as mockSelectedTimeline, mockTemplate as mockSelectedTemplate, } from './__mocks__'; -import { getTimeline } from '../../containers/api'; +import { resolveTimeline } from '../../containers/api'; import { defaultHeaders } from '../timeline/body/column_headers/default_headers'; jest.mock('../../../common/store/inputs/actions'); @@ -951,7 +951,7 @@ describe('helpers', () => { }; beforeAll(async () => { - (getTimeline as jest.Mock).mockRejectedValue(mockError); + (resolveTimeline as jest.Mock).mockRejectedValue(mockError); queryTimelineById<{}>(args as unknown as QueryTimelineById<{}>); }); @@ -986,7 +986,7 @@ describe('helpers', () => { }; beforeAll(async () => { - (getTimeline as jest.Mock).mockResolvedValue(selectedTimeline); + (resolveTimeline as jest.Mock).mockResolvedValue(selectedTimeline); await queryTimelineById<{}>(args as unknown as QueryTimelineById<{}>); }); @@ -1002,7 +1002,7 @@ describe('helpers', () => { }); test('get timeline by Id', () => { - expect(getTimeline).toHaveBeenCalled(); + expect(resolveTimeline).toHaveBeenCalled(); }); test('it does not call onError when an error does not occur', () => { @@ -1011,7 +1011,7 @@ describe('helpers', () => { test('Do not override daterange if TimelineStatus is active', () => { const { timeline } = formatTimelineResultToModel( - omitTypenameInTimeline(getOr({}, 'data.getOneTimeline', selectedTimeline)), + omitTypenameInTimeline(getOr({}, 'data.timeline', selectedTimeline)), args.duplicate, args.timelineType ); @@ -1044,7 +1044,7 @@ describe('helpers', () => { }; beforeAll(async () => { - (getTimeline as jest.Mock).mockResolvedValue(selectedTimeline); + (resolveTimeline as jest.Mock).mockResolvedValue(selectedTimeline); await queryTimelineById<{}>(args as unknown as QueryTimelineById<{}>); }); @@ -1060,12 +1060,12 @@ describe('helpers', () => { }); test('get timeline by Id', () => { - expect(getTimeline).toHaveBeenCalled(); + expect(resolveTimeline).toHaveBeenCalled(); }); test('should not override daterange if TimelineStatus is active', () => { const { timeline } = formatTimelineResultToModel( - omitTypenameInTimeline(getOr({}, 'data.getOneTimeline', selectedTimeline)), + omitTypenameInTimeline(getOr({}, 'data.timeline', selectedTimeline)), args.duplicate, args.timelineType ); @@ -1085,6 +1085,10 @@ describe('helpers', () => { to: '2020-07-08T08:20:18.966Z', notes: [], id: TimelineId.active, + resolveTimelineConfig: { + outcome: 'exactMatch', + alias_target_id: undefined, + }, }); }); @@ -1112,12 +1116,12 @@ describe('helpers', () => { }; beforeAll(async () => { - (getTimeline as jest.Mock).mockResolvedValue(template); + (resolveTimeline as jest.Mock).mockResolvedValue(template); await queryTimelineById<{}>(args as unknown as QueryTimelineById<{}>); }); afterAll(() => { - (getTimeline as jest.Mock).mockReset(); + (resolveTimeline as jest.Mock).mockReset(); jest.clearAllMocks(); }); @@ -1129,12 +1133,12 @@ describe('helpers', () => { }); test('get timeline by Id', () => { - expect(getTimeline).toHaveBeenCalled(); + expect(resolveTimeline).toHaveBeenCalled(); }); test('override daterange if TimelineStatus is immutable', () => { const { timeline } = formatTimelineResultToModel( - omitTypenameInTimeline(getOr({}, 'data.getOneTimeline', template)), + omitTypenameInTimeline(getOr({}, 'data.timeline', template)), args.duplicate, args.timelineType ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts index 1fbddf61f8cd3..f325ab34e88d5 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts @@ -20,6 +20,7 @@ import { TimelineType, TimelineTabs, TimelineResult, + SingleTimelineResolveResponse, ColumnHeaderResult, FilterTimelineResult, DataProviderResult, @@ -65,7 +66,7 @@ import { DEFAULT_FROM_MOMENT, DEFAULT_TO_MOMENT, } from '../../../common/utils/default_date_settings'; -import { getTimeline } from '../../containers/api'; +import { resolveTimeline } from '../../containers/api'; import { PinnedEvent } from '../../../../common/types/timeline/pinned_event'; import { NoteResult } from '../../../../common/types/timeline/note'; @@ -346,11 +347,12 @@ export const queryTimelineById = ({ updateTimeline, }: QueryTimelineById) => { updateIsLoading({ id: TimelineId.active, isLoading: true }); - Promise.resolve(getTimeline(timelineId)) + Promise.resolve(resolveTimeline(timelineId)) .then((result) => { - const timelineToOpen: TimelineResult = omitTypenameInTimeline( - getOr({}, 'data.getOneTimeline', result) - ); + const data: SingleTimelineResolveResponse['data'] | null = getOr(null, 'data', result); + if (!data) return; + + const timelineToOpen = omitTypenameInTimeline(data.timeline); const { timeline, notes } = formatTimelineResultToModel( timelineToOpen, @@ -370,6 +372,10 @@ export const queryTimelineById = ({ from, id: TimelineId.active, notes, + resolveTimelineConfig: { + outcome: data.outcome, + alias_target_id: data.alias_target_id, + }, timeline: { ...timeline, activeTab: activeTimelineTab, @@ -399,6 +405,7 @@ export const dispatchUpdateTimeline = forceNotes = false, from, notes, + resolveTimelineConfig, timeline, to, ruleNote, @@ -429,7 +436,9 @@ export const dispatchUpdateTimeline = } else { dispatch(dispatchSetTimelineRangeDatePicker({ from, to })); } - dispatch(dispatchAddTimeline({ id, timeline, savedTimeline: duplicate })); + dispatch( + dispatchAddTimeline({ id, timeline, resolveTimelineConfig, savedTimeline: duplicate }) + ); if ( timeline.kqlQuery != null && timeline.kqlQuery.filterQuery != null && diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/types.ts b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/types.ts index 79a700856c00f..4c9ce991252dc 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/types.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/types.ts @@ -16,6 +16,7 @@ import { TemplateTimelineTypeLiteral, RowRendererId, TimelineStatusLiteralWithNull, + SingleTimelineResolveResponse, } from '../../../../common/types/timeline'; /** The users who added a timeline to favorites */ @@ -194,12 +195,17 @@ export interface OpenTimelineProps { hideActions?: ActionTimelineToShow[]; } +export interface ResolveTimelineConfig { + alias_target_id: SingleTimelineResolveResponse['data']['alias_target_id']; + outcome: SingleTimelineResolveResponse['data']['outcome']; +} export interface UpdateTimeline { duplicate: boolean; id: string; forceNotes?: boolean; from: string; notes: NoteResult[] | null | undefined; + resolveTimelineConfig?: ResolveTimelineConfig; timeline: TimelineModel; to: string; ruleNote?: string; @@ -210,6 +216,7 @@ export type DispatchUpdateTimeline = ({ id, from, notes, + resolveTimelineConfig, timeline, to, ruleNote, diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.test.tsx index db7a3cc3c9900..c91673e5f931c 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.test.tsx @@ -40,6 +40,12 @@ const mockUseResizeObserver: jest.Mock = useResizeObserver as jest.Mock; jest.mock('use-resize-observer/polyfilled'); mockUseResizeObserver.mockImplementation(() => ({})); +jest.mock('../../../common/hooks/use_resolve_conflict', () => { + return { + useResolveConflict: jest.fn().mockImplementation(() => null), + }; +}); + jest.mock('react-router-dom', () => { const original = jest.requireActual('react-router-dom'); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx index a199dd5aa55f8..ca883529b5ce6 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx @@ -28,6 +28,7 @@ import { TabsContent } from './tabs_content'; import { HideShowContainer, TimelineContainer } from './styles'; import { useTimelineFullScreen } from '../../../common/containers/use_full_screen'; import { EXIT_FULL_SCREEN_CLASS_NAME } from '../../../common/components/exit_full_screen'; +import { useResolveConflict } from '../../../common/hooks/use_resolve_conflict'; const TimelineTemplateBadge = styled.div` background: ${({ theme }) => theme.eui.euiColorVis3_behindText}; @@ -119,6 +120,7 @@ const StatefulTimelineComponent: React.FC = ({ [containerElement, onSkipFocusBeforeEventsTable, onSkipFocusAfterEventsTable] ); const timelineContext = useMemo(() => ({ timelineId }), [timelineId]); + const resolveConflictComponent = useResolveConflict(); return ( @@ -132,7 +134,7 @@ const StatefulTimelineComponent: React.FC = ({ {timelineType === TimelineType.template && ( {i18n.TIMELINE_TEMPLATE} )} - + {resolveConflictComponent} { return decodeSingleTimelineResponse(response); }; -export const getResolvedTimelineTemplate = async (templateTimelineId: string) => { - const response = await KibanaServices.get().http.get( - TIMELINE_RESOLVE_URL, - { - query: { - template_timeline_id: templateTimelineId, - }, - } - ); - - return decodeResolvedSingleTimelineResponse(response); -}; - export const getAllTimelines = async (args: GetTimelinesArgs, abortSignal: AbortSignal) => { const response = await KibanaServices.get().http.fetch(TIMELINES_URL, { method: 'GET', diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts index 95ad6c5d44ca3..f3a70bd1390ae 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts @@ -25,6 +25,7 @@ import type { SerializedFilterQuery, } from '../../../../common/types/timeline'; import { tGridActions } from '../../../../../timelines/public'; +import { ResolveTimelineConfig } from '../../components/open_timeline/types'; export const { applyDeltaToColumnWidth, clearEventsDeleted, @@ -91,6 +92,7 @@ export const updateTimeline = actionCreator<{ export const addTimeline = actionCreator<{ id: string; timeline: TimelineModel; + resolveTimelineConfig?: ResolveTimelineConfig; savedTimeline?: boolean; }>('ADD_TIMELINE'); diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/defaults.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/defaults.ts index 4691872bfb927..4c2b8d2992d3d 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/defaults.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/defaults.ts @@ -14,64 +14,65 @@ import { SubsetTimelineModel, TimelineModel } from './model'; // normalizeTimeRange uses getTimeRangeSettings which cannot be used outside Kibana context if the uiSettings is not false const { from: start, to: end } = normalizeTimeRange({ from: '', to: '' }, false); -export const timelineDefaults: SubsetTimelineModel & Pick = - { - activeTab: TimelineTabs.query, - prevActiveTab: TimelineTabs.query, - columns: defaultHeaders, - documentType: '', - defaultColumns: defaultHeaders, - dataProviders: [], - dateRange: { start, end }, - deletedEventIds: [], - description: '', - eqlOptions: { - eventCategoryField: 'event.category', - tiebreakerField: '', - timestampField: '@timestamp', - query: '', - size: 100, +export const timelineDefaults: SubsetTimelineModel & + Pick = { + activeTab: TimelineTabs.query, + prevActiveTab: TimelineTabs.query, + columns: defaultHeaders, + documentType: '', + defaultColumns: defaultHeaders, + dataProviders: [], + dateRange: { start, end }, + deletedEventIds: [], + description: '', + eqlOptions: { + eventCategoryField: 'event.category', + tiebreakerField: '', + timestampField: '@timestamp', + query: '', + size: 100, + }, + eventType: 'all', + eventIdToNoteIds: {}, + excludedRowRendererIds: [], + expandedDetail: {}, + highlightedDropAndProviderId: '', + historyIds: [], + filters: [], + indexNames: [], + isFavorite: false, + isLive: false, + isSelectAllChecked: false, + isLoading: false, + isSaving: false, + itemsPerPage: 25, + itemsPerPageOptions: [10, 25, 50, 100], + kqlMode: 'filter', + kqlQuery: { + filterQuery: null, + }, + loadingEventIds: [], + resolveTimelineConfig: undefined, + queryFields: [], + title: '', + timelineType: TimelineType.default, + templateTimelineId: null, + templateTimelineVersion: null, + noteIds: [], + pinnedEventIds: {}, + pinnedEventsSaveObject: {}, + savedObjectId: null, + selectAll: false, + selectedEventIds: {}, + show: false, + showCheckboxes: false, + sort: [ + { + columnId: '@timestamp', + columnType: 'number', + sortDirection: 'desc', }, - eventType: 'all', - eventIdToNoteIds: {}, - excludedRowRendererIds: [], - expandedDetail: {}, - highlightedDropAndProviderId: '', - historyIds: [], - filters: [], - indexNames: [], - isFavorite: false, - isLive: false, - isSelectAllChecked: false, - isLoading: false, - isSaving: false, - itemsPerPage: 25, - itemsPerPageOptions: [10, 25, 50, 100], - kqlMode: 'filter', - kqlQuery: { - filterQuery: null, - }, - loadingEventIds: [], - queryFields: [], - title: '', - timelineType: TimelineType.default, - templateTimelineId: null, - templateTimelineVersion: null, - noteIds: [], - pinnedEventIds: {}, - pinnedEventsSaveObject: {}, - savedObjectId: null, - selectAll: false, - selectedEventIds: {}, - show: false, - showCheckboxes: false, - sort: [ - { - columnId: '@timestamp', - columnType: 'number', - sortDirection: 'desc', - }, - ], - status: TimelineStatus.draft, - version: null, - }; + ], + status: TimelineStatus.draft, + version: null, +}; diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.ts index 6ee844958aeed..b7af561ae2a04 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.ts @@ -47,6 +47,7 @@ import { RESIZED_COLUMN_MIN_WITH, } from '../../components/timeline/body/constants'; import { activeTimeline } from '../../containers/active_timeline_context'; +import { ResolveTimelineConfig } from '../../components/open_timeline/types'; export const isNotNull = (value: T | null): value is T => value !== null; @@ -124,6 +125,7 @@ export const addTimelineNoteToEvent = ({ interface AddTimelineParams { id: string; + resolveTimelineConfig?: ResolveTimelineConfig; timeline: TimelineModel; timelineById: TimelineById; } @@ -145,6 +147,7 @@ export const shouldResetActiveTimelineContext = ( */ export const addTimelineToStore = ({ id, + resolveTimelineConfig, timeline, timelineById, }: AddTimelineParams): TimelineById => { @@ -159,6 +162,7 @@ export const addTimelineToStore = ({ filterManager: timelineById[id].filterManager, isLoading: timelineById[id].isLoading, initialized: timelineById[id].initialized, + resolveTimelineConfig, dateRange: timeline.status === TimelineStatus.immutable && timeline.timelineType === TimelineType.template diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/model.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/model.ts index b53da997c08cb..29b49197ef797 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/model.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/model.ts @@ -15,6 +15,7 @@ import type { } from '../../../../common/types/timeline'; import { PinnedEvent } from '../../../../common/types/timeline/pinned_event'; import type { TGridModelForTimeline } from '../../../../../timelines/public'; +import { ResolveTimelineConfig } from '../../components/open_timeline/types'; export const DEFAULT_PAGE_COUNT = 2; // Eui Pager will not render unless this is a minimum of 2 pages export type KqlMode = 'filter' | 'search'; @@ -59,6 +60,7 @@ export type TimelineModel = TGridModelForTimeline & { /** Events pinned to this timeline */ pinnedEventIds: Record; pinnedEventsSaveObject: Record; + resolveTimelineConfig?: ResolveTimelineConfig; showSaveModal?: boolean; savedQueryId?: string | null; /** When true, show the timeline flyover */ diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts index 97fa72667a3c6..e997bbd848d50 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts @@ -94,9 +94,14 @@ export const initialTimelineState: TimelineState = { /** The reducer for all timeline actions */ export const timelineReducer = reducerWithInitialState(initialTimelineState) - .case(addTimeline, (state, { id, timeline }) => ({ + .case(addTimeline, (state, { id, timeline, resolveTimelineConfig }) => ({ ...state, - timelineById: addTimelineToStore({ id, timeline, timelineById: state.timelineById }), + timelineById: addTimelineToStore({ + id, + timeline, + resolveTimelineConfig, + timelineById: state.timelineById, + }), })) .case(createTimeline, (state, { id, timelineType = TimelineType.default, ...timelineProps }) => { return { diff --git a/x-pack/plugins/security_solution/public/types.ts b/x-pack/plugins/security_solution/public/types.ts index bee0e9b3a3d1d..61813d1a122b4 100644 --- a/x-pack/plugins/security_solution/public/types.ts +++ b/x-pack/plugins/security_solution/public/types.ts @@ -9,7 +9,6 @@ import { CoreStart } from '../../../../src/core/public'; import { HomePublicPluginSetup } from '../../../../src/plugins/home/public'; import { DataPublicPluginStart } from '../../../../src/plugins/data/public'; import { EmbeddableStart } from '../../../../src/plugins/embeddable/public'; -import { SpacesPluginStart } from '../../../plugins/spaces/public'; import { LensPublicStart } from '../../../plugins/lens/public'; import { NewsfeedPublicPluginStart } from '../../../../src/plugins/newsfeed/public'; import { Start as InspectorStart } from '../../../../src/plugins/inspector/public'; @@ -18,6 +17,7 @@ import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/p import { Storage } from '../../../../src/plugins/kibana_utils/public'; import { FleetStart } from '../../fleet/public'; import { PluginStart as ListsPluginStart } from '../../lists/public'; +import { SpacesPluginStart } from '../../spaces/public'; import { TriggersAndActionsUIPublicPluginSetup as TriggersActionsSetup, TriggersAndActionsUIPublicPluginStart as TriggersActionsStart, diff --git a/x-pack/plugins/security_solution/tsconfig.json b/x-pack/plugins/security_solution/tsconfig.json index 5c87d58199df4..8f4d602e26461 100644 --- a/x-pack/plugins/security_solution/tsconfig.json +++ b/x-pack/plugins/security_solution/tsconfig.json @@ -1,4 +1,3 @@ - { "extends": "../../../tsconfig.base.json", "compilerOptions": { @@ -40,7 +39,7 @@ { "path": "../maps/tsconfig.json" }, { "path": "../ml/tsconfig.json" }, { "path": "../spaces/tsconfig.json" }, - { "path": "../security/tsconfig.json"}, - { "path": "../timelines/tsconfig.json"}, + { "path": "../security/tsconfig.json" }, + { "path": "../timelines/tsconfig.json" } ] } From 1a5570baf45551bc8bacc6786a0ab513d3b9af79 Mon Sep 17 00:00:00 2001 From: Brian Seeders Date: Tue, 19 Oct 2021 18:55:02 -0400 Subject: [PATCH 100/204] [buildkite] Add timeouts to all steps missing them (#115656) --- .buildkite/pipelines/es_snapshots/verify.yml | 4 ++++ .buildkite/pipelines/hourly.yml | 6 ++++++ .buildkite/pipelines/on_merge.yml | 4 ++++ .buildkite/pipelines/pull_request/base.yml | 7 ++++++- .buildkite/pipelines/update_demo_env.yml | 2 ++ 5 files changed, 22 insertions(+), 1 deletion(-) diff --git a/.buildkite/pipelines/es_snapshots/verify.yml b/.buildkite/pipelines/es_snapshots/verify.yml index b9aa0e0e3727a..9cddade0b7482 100755 --- a/.buildkite/pipelines/es_snapshots/verify.yml +++ b/.buildkite/pipelines/es_snapshots/verify.yml @@ -13,6 +13,7 @@ steps: - command: .buildkite/scripts/lifecycle/pre_build.sh label: Pre-Build + timeout_in_minutes: 10 - wait @@ -22,6 +23,7 @@ steps: queue: c2-16 key: build if: "build.env('KIBANA_BUILD_ID') == null || build.env('KIBANA_BUILD_ID') == ''" + timeout_in_minutes: 60 - command: .buildkite/scripts/steps/functional/xpack_cigroup.sh label: 'Default CI Group' @@ -81,6 +83,7 @@ steps: - command: .buildkite/scripts/steps/es_snapshots/trigger_promote.sh label: Trigger promotion + timeout_in_minutes: 10 depends_on: - default-cigroup - default-cigroup-docker @@ -93,3 +96,4 @@ steps: - command: .buildkite/scripts/lifecycle/post_build.sh label: Post-Build + timeout_in_minutes: 10 diff --git a/.buildkite/pipelines/hourly.yml b/.buildkite/pipelines/hourly.yml index 0edba11836fcd..3337cfb5dfcdd 100644 --- a/.buildkite/pipelines/hourly.yml +++ b/.buildkite/pipelines/hourly.yml @@ -3,6 +3,7 @@ env: steps: - command: .buildkite/scripts/lifecycle/pre_build.sh label: Pre-Build + timeout_in_minutes: 10 - wait @@ -12,6 +13,7 @@ steps: queue: c2-16 key: build if: "build.env('KIBANA_BUILD_ID') == null || build.env('KIBANA_BUILD_ID') == ''" + timeout_in_minutes: 60 - command: .buildkite/scripts/steps/functional/xpack_cigroup.sh label: 'Default CI Group' @@ -143,21 +145,25 @@ steps: agents: queue: n2-2 key: linting + timeout_in_minutes: 90 - command: .buildkite/scripts/steps/checks.sh label: 'Checks' agents: queue: c2-4 key: checks + timeout_in_minutes: 120 - command: .buildkite/scripts/steps/storybooks/build_and_upload.sh label: 'Build Storybooks' agents: queue: c2-4 key: storybooks + timeout_in_minutes: 60 - wait: ~ continue_on_failure: true - command: .buildkite/scripts/lifecycle/post_build.sh label: Post-Build + timeout_in_minutes: 10 diff --git a/.buildkite/pipelines/on_merge.yml b/.buildkite/pipelines/on_merge.yml index dc0541c6397d9..2aba49bfa6460 100644 --- a/.buildkite/pipelines/on_merge.yml +++ b/.buildkite/pipelines/on_merge.yml @@ -4,6 +4,7 @@ env: steps: - command: .buildkite/scripts/lifecycle/pre_build.sh label: Pre-Build + timeout_in_minutes: 10 - wait @@ -13,14 +14,17 @@ steps: BAZEL_CACHE_MODE: read-write agents: queue: c2-8 + timeout_in_minutes: 60 - command: .buildkite/scripts/steps/on_merge_ts_refs_api_docs.sh label: Build TS Refs and Check Public API Docs agents: queue: c2-4 + timeout_in_minutes: 80 - wait: ~ continue_on_failure: true - command: .buildkite/scripts/lifecycle/post_build.sh label: Post-Build + timeout_in_minutes: 10 diff --git a/.buildkite/pipelines/pull_request/base.yml b/.buildkite/pipelines/pull_request/base.yml index 404bfb273b6f7..1013a841dfd27 100644 --- a/.buildkite/pipelines/pull_request/base.yml +++ b/.buildkite/pipelines/pull_request/base.yml @@ -1,6 +1,7 @@ steps: - command: .buildkite/scripts/lifecycle/pre_build.sh label: Pre-Build + timeout_in_minutes: 10 - wait @@ -10,6 +11,7 @@ steps: queue: c2-16 key: build if: "build.env('KIBANA_BUILD_ID') == null || build.env('KIBANA_BUILD_ID') == ''" + timeout_in_minutes: 60 - command: .buildkite/scripts/steps/functional/xpack_cigroup.sh label: 'Default CI Group' @@ -17,7 +19,7 @@ steps: agents: queue: ci-group-6 depends_on: build - timeout_in_minutes: 120 + timeout_in_minutes: 150 key: default-cigroup retry: automatic: @@ -141,15 +143,18 @@ steps: agents: queue: n2-2 key: linting + timeout_in_minutes: 90 - command: .buildkite/scripts/steps/checks.sh label: 'Checks' agents: queue: c2-4 key: checks + timeout_in_minutes: 120 - command: .buildkite/scripts/steps/storybooks/build_and_upload.sh label: 'Build Storybooks' agents: queue: c2-4 key: storybooks + timeout_in_minutes: 60 diff --git a/.buildkite/pipelines/update_demo_env.yml b/.buildkite/pipelines/update_demo_env.yml index 1c15b227a2e4a..e2dfdd782fd41 100644 --- a/.buildkite/pipelines/update_demo_env.yml +++ b/.buildkite/pipelines/update_demo_env.yml @@ -1,8 +1,10 @@ steps: - command: .buildkite/scripts/steps/demo_env/es_and_init.sh label: Initialize Environment and Deploy ES + timeout_in_minutes: 10 - command: .buildkite/scripts/steps/demo_env/kibana.sh label: Build and Deploy Kibana agents: queue: c2-8 + timeout_in_minutes: 60 From 67d6355b2e8fe3f40acca7e669cec30fbd227bc5 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Tue, 19 Oct 2021 18:36:24 -0500 Subject: [PATCH 101/204] [data view management] Copy improvements (#114413) * better copy --- .../empty_index_list_prompt.test.tsx.snap | 14 ++++++++------ .../empty_index_list_prompt.tsx | 12 ++++++------ .../index_header/index_header.tsx | 4 ++-- .../__snapshots__/field_editor.test.tsx.snap | 10 +++++----- .../components/field_editor/field_editor.tsx | 3 +-- .../plugins/translations/translations/ja-JP.json | 1 - .../plugins/translations/translations/zh-CN.json | 1 - 7 files changed, 22 insertions(+), 23 deletions(-) diff --git a/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_list_prompt/__snapshots__/empty_index_list_prompt.test.tsx.snap b/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_list_prompt/__snapshots__/empty_index_list_prompt.test.tsx.snap index a104c36e3a8a0..47cad9c7a8216 100644 --- a/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_list_prompt/__snapshots__/empty_index_list_prompt.test.tsx.snap +++ b/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_list_prompt/__snapshots__/empty_index_list_prompt.test.tsx.snap @@ -120,10 +120,12 @@ exports[`EmptyIndexListPrompt should render normally 1`] = `
- + diff --git a/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_list_prompt/empty_index_list_prompt.tsx b/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_list_prompt/empty_index_list_prompt.tsx index d00f9e2368e21..a550209095898 100644 --- a/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_list_prompt/empty_index_list_prompt.tsx +++ b/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_list_prompt/empty_index_list_prompt.tsx @@ -46,14 +46,14 @@ export const EmptyIndexListPrompt = ({ const createAnywayLink = ( createAnyway()} data-test-subj="createAnyway"> ), @@ -153,8 +153,8 @@ export const EmptyIndexListPrompt = ({
- - + + - + = ({ diff --git a/src/plugins/index_pattern_management/public/components/field_editor/__snapshots__/field_editor.test.tsx.snap b/src/plugins/index_pattern_management/public/components/field_editor/__snapshots__/field_editor.test.tsx.snap index b9d1b3992778f..87aa20c4617c1 100644 --- a/src/plugins/index_pattern_management/public/components/field_editor/__snapshots__/field_editor.test.tsx.snap +++ b/src/plugins/index_pattern_management/public/components/field_editor/__snapshots__/field_editor.test.tsx.snap @@ -122,7 +122,7 @@ exports[`FieldEditor should render create new scripted field correctly 1`] = ` @@ -365,7 +365,7 @@ exports[`FieldEditor should render edit scripted field correctly 1`] = ` @@ -658,7 +658,7 @@ exports[`FieldEditor should show conflict field warning 1`] = ` @@ -994,7 +994,7 @@ exports[`FieldEditor should show deprecated lang warning 1`] = ` @@ -1343,7 +1343,7 @@ exports[`FieldEditor should show multiple type field warning with a table contai diff --git a/src/plugins/index_pattern_management/public/components/field_editor/field_editor.tsx b/src/plugins/index_pattern_management/public/components/field_editor/field_editor.tsx index 695d02d0744fb..9509f4fb46e0b 100644 --- a/src/plugins/index_pattern_management/public/components/field_editor/field_editor.tsx +++ b/src/plugins/index_pattern_management/public/components/field_editor/field_editor.tsx @@ -506,8 +506,7 @@ export class FieldEditor extends PureComponent } > diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 781ef74a872eb..c54512203677d 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -3722,7 +3722,6 @@ "indexPatternEditor.aliasLabel": "エイリアス", "indexPatternEditor.createIndex.noMatch": "名前は1つ以上のデータストリーム、インデックス、またはインデックスエイリアスと一致する必要があります。", "indexPatternEditor.createIndexPattern.emptyState.checkDataButton": "新規データを確認", - "indexPatternEditor.createIndexPattern.emptyState.createAnyway": "一部のインデックスは表示されない場合があります。{link}してください。", "indexPatternEditor.createIndexPattern.emptyState.createAnywayLink": "インデックスパターンを作成します", "indexPatternEditor.createIndexPattern.emptyState.haveData": "すでにデータがある場合", "indexPatternEditor.createIndexPattern.emptyState.integrationCardDescription": "さまざまなソースからデータを追加します。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 92646cc1f9cd9..fbc65161a47f8 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -3755,7 +3755,6 @@ "indexPatternEditor.aliasLabel": "别名", "indexPatternEditor.createIndex.noMatch": "名称必须匹配一个或多个数据流、索引或索引别名。", "indexPatternEditor.createIndexPattern.emptyState.checkDataButton": "检查新数据", - "indexPatternEditor.createIndexPattern.emptyState.createAnyway": "部分索引可能已隐藏。仍然尝试{link}。", "indexPatternEditor.createIndexPattern.emptyState.createAnywayLink": "创建索引模式", "indexPatternEditor.createIndexPattern.emptyState.haveData": "假设您已有数据?", "indexPatternEditor.createIndexPattern.emptyState.integrationCardDescription": "从各种源添加数据。", From 5d1b5104fcdb96e799131a12cc54fa7b8cf7edc7 Mon Sep 17 00:00:00 2001 From: Georgii Gorbachev Date: Wed, 20 Oct 2021 03:16:31 +0200 Subject: [PATCH 102/204] Fix adding prepackaged rules via fleet plugin (#114467) Co-authored-by: Garrett Spong Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- api_docs/security_solution.json | 12 +- .../server/alert_data_client/alerts_client.ts | 6 +- .../alerts_client_factory.test.ts | 5 +- .../alerts_client_factory.ts | 6 +- .../tests/bulk_update.test.ts | 5 +- .../tests/find_alerts.test.ts | 5 +- .../alert_data_client/tests/get.test.ts | 5 +- .../alert_data_client/tests/update.test.ts | 5 +- x-pack/plugins/rule_registry/server/index.ts | 2 +- x-pack/plugins/rule_registry/server/mocks.ts | 9 +- x-pack/plugins/rule_registry/server/plugin.ts | 8 +- .../rule_data_plugin_service.mock.ts | 18 +- .../rule_data_plugin_service.ts | 89 ++++--- .../security_solution/server/config.mock.ts | 62 +++++ .../security_solution/server/config.ts | 24 +- .../endpoint/endpoint_app_context_services.ts | 81 +++---- .../server/endpoint/errors.ts | 6 + .../server/endpoint/mocks.ts | 18 +- .../endpoint/routes/actions/audit_log.test.ts | 2 + .../endpoint/routes/actions/isolation.test.ts | 4 + .../endpoint/routes/actions/status.test.ts | 2 + .../endpoint/routes/metadata/metadata.test.ts | 3 + .../endpoint/routes/policy/handlers.test.ts | 3 + .../fleet_integration.test.ts | 15 +- .../fleet_integration/fleet_integration.ts | 28 +-- .../handlers/install_prepackaged_rules.ts | 36 +-- .../plugins/security_solution/server/index.ts | 6 +- .../routes/__mocks__/index.ts | 33 +-- .../routes/__mocks__/request_context.ts | 131 +++++++---- .../routes/__mocks__/request_responses.ts | 28 ++- .../routes/index/create_index_route.ts | 42 +--- .../privileges/read_privileges_route.test.ts | 6 +- .../rules/add_prepackaged_rules_route.test.ts | 54 ++--- .../rules/add_prepackaged_rules_route.ts | 67 ++---- .../rules/create_rules_bulk_route.test.ts | 8 +- .../routes/rules/create_rules_route.test.ts | 8 +- .../routes/rules/import_rules_route.test.ts | 8 +- .../routes/signals/open_close_signals.test.ts | 4 +- .../server/lib/framework/types.ts | 5 +- .../create_timelines/helpers.test.ts | 17 +- .../server/lib/timeline/utils/common.ts | 7 +- .../security_solution/server/plugin.ts | 222 +++++++----------- .../server/plugin_contract.ts | 102 ++++++++ .../server/request_context_factory.mock.ts | 23 ++ .../server/request_context_factory.ts | 90 +++++++ .../security_solution/server/routes/index.ts | 6 +- .../plugins/security_solution/server/types.ts | 29 ++- 47 files changed, 794 insertions(+), 561 deletions(-) create mode 100644 x-pack/plugins/security_solution/server/config.mock.ts create mode 100644 x-pack/plugins/security_solution/server/plugin_contract.ts create mode 100644 x-pack/plugins/security_solution/server/request_context_factory.mock.ts create mode 100644 x-pack/plugins/security_solution/server/request_context_factory.ts diff --git a/api_docs/security_solution.json b/api_docs/security_solution.json index b16ae8334f1b0..e159e936e8f42 100644 --- a/api_docs/security_solution.json +++ b/api_docs/security_solution.json @@ -772,17 +772,17 @@ "interfaces": [ { "parentPluginId": "securitySolution", - "id": "def-server.AppRequestContext", + "id": "def-server.SecuritySolutionApiRequestHandlerContext", "type": "Interface", "tags": [], - "label": "AppRequestContext", + "label": "SecuritySolutionApiRequestHandlerContext", "description": [], "path": "x-pack/plugins/security_solution/server/types.ts", "deprecated": false, "children": [ { "parentPluginId": "securitySolution", - "id": "def-server.AppRequestContext.getAppClient", + "id": "def-server.SecuritySolutionApiRequestHandlerContext.getAppClient", "type": "Function", "tags": [], "label": "getAppClient", @@ -804,7 +804,7 @@ }, { "parentPluginId": "securitySolution", - "id": "def-server.AppRequestContext.getSpaceId", + "id": "def-server.SecuritySolutionApiRequestHandlerContext.getSpaceId", "type": "Function", "tags": [], "label": "getSpaceId", @@ -819,7 +819,7 @@ }, { "parentPluginId": "securitySolution", - "id": "def-server.AppRequestContext.getExecutionLogClient", + "id": "def-server.SecuritySolutionApiRequestHandlerContext.getExecutionLogClient", "type": "Function", "tags": [], "label": "getExecutionLogClient", @@ -31438,4 +31438,4 @@ } ] } -} \ No newline at end of file +} diff --git a/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client.ts b/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client.ts index d35d18d3b5958..16447e6b0f539 100644 --- a/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client.ts +++ b/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client.ts @@ -41,7 +41,7 @@ import { SPACE_IDS, } from '../../common/technical_rule_data_field_names'; import { ParsedTechnicalFields } from '../../common/parse_technical_fields'; -import { Dataset, RuleDataPluginService } from '../rule_data_plugin_service'; +import { Dataset, IRuleDataService } from '../rule_data_plugin_service'; const getEsQueryConfig: typeof getEsQueryConfigTyped = getEsQueryConfigNonTyped; const getSafeSortIds: typeof getSafeSortIdsTyped = getSafeSortIdsNonTyped; @@ -71,7 +71,7 @@ export interface ConstructorOptions { authorization: PublicMethodsOf; auditLogger?: AuditLogger; esClient: ElasticsearchClient; - ruleDataService: RuleDataPluginService; + ruleDataService: IRuleDataService; } export interface UpdateOptions { @@ -116,7 +116,7 @@ export class AlertsClient { private readonly authorization: PublicMethodsOf; private readonly esClient: ElasticsearchClient; private readonly spaceId: string | undefined; - private readonly ruleDataService: RuleDataPluginService; + private readonly ruleDataService: IRuleDataService; constructor(options: ConstructorOptions) { this.logger = options.logger; diff --git a/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client_factory.test.ts b/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client_factory.test.ts index 41ef5e4edb0d1..276ea070d6f87 100644 --- a/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client_factory.test.ts +++ b/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client_factory.test.ts @@ -13,8 +13,7 @@ import { loggingSystemMock } from 'src/core/server/mocks'; import { securityMock } from '../../../security/server/mocks'; import { AuditLogger } from '../../../security/server'; import { alertingAuthorizationMock } from '../../../alerting/server/authorization/alerting_authorization.mock'; -import { ruleDataPluginServiceMock } from '../rule_data_plugin_service/rule_data_plugin_service.mock'; -import { RuleDataPluginService } from '../rule_data_plugin_service'; +import { ruleDataServiceMock } from '../rule_data_plugin_service/rule_data_plugin_service.mock'; jest.mock('./alerts_client'); @@ -26,7 +25,7 @@ const alertsClientFactoryParams: AlertsClientFactoryProps = { getAlertingAuthorization: (_: KibanaRequest) => alertingAuthMock, securityPluginSetup, esClient: {} as ElasticsearchClient, - ruleDataService: ruleDataPluginServiceMock.create() as unknown as RuleDataPluginService, + ruleDataService: ruleDataServiceMock.create(), }; const fakeRequest = { diff --git a/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client_factory.ts b/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client_factory.ts index c1ff6d5d56ea9..8225394c2dba7 100644 --- a/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client_factory.ts +++ b/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client_factory.ts @@ -9,7 +9,7 @@ import { PublicMethodsOf } from '@kbn/utility-types'; import { ElasticsearchClient, KibanaRequest, Logger } from 'src/core/server'; import { AlertingAuthorization } from '../../../alerting/server'; import { SecurityPluginSetup } from '../../../security/server'; -import { RuleDataPluginService } from '../rule_data_plugin_service'; +import { IRuleDataService } from '../rule_data_plugin_service'; import { AlertsClient } from './alerts_client'; export interface AlertsClientFactoryProps { @@ -17,7 +17,7 @@ export interface AlertsClientFactoryProps { esClient: ElasticsearchClient; getAlertingAuthorization: (request: KibanaRequest) => PublicMethodsOf; securityPluginSetup: SecurityPluginSetup | undefined; - ruleDataService: RuleDataPluginService | null; + ruleDataService: IRuleDataService | null; } export class AlertsClientFactory { @@ -28,7 +28,7 @@ export class AlertsClientFactory { request: KibanaRequest ) => PublicMethodsOf; private securityPluginSetup!: SecurityPluginSetup | undefined; - private ruleDataService!: RuleDataPluginService | null; + private ruleDataService!: IRuleDataService | null; public initialize(options: AlertsClientFactoryProps) { /** diff --git a/x-pack/plugins/rule_registry/server/alert_data_client/tests/bulk_update.test.ts b/x-pack/plugins/rule_registry/server/alert_data_client/tests/bulk_update.test.ts index 2be1f6875cd7e..8868d7959621d 100644 --- a/x-pack/plugins/rule_registry/server/alert_data_client/tests/bulk_update.test.ts +++ b/x-pack/plugins/rule_registry/server/alert_data_client/tests/bulk_update.test.ts @@ -19,8 +19,7 @@ import { elasticsearchClientMock } from 'src/core/server/elasticsearch/client/mo import { alertingAuthorizationMock } from '../../../../alerting/server/authorization/alerting_authorization.mock'; import { AuditLogger } from '../../../../security/server'; import { AlertingAuthorizationEntity } from '../../../../alerting/server'; -import { ruleDataPluginServiceMock } from '../../rule_data_plugin_service/rule_data_plugin_service.mock'; -import { RuleDataPluginService } from '../../rule_data_plugin_service'; +import { ruleDataServiceMock } from '../../rule_data_plugin_service/rule_data_plugin_service.mock'; const alertingAuthMock = alertingAuthorizationMock.create(); const esClientMock = elasticsearchClientMock.createElasticsearchClient(); @@ -33,7 +32,7 @@ const alertsClientParams: jest.Mocked = { authorization: alertingAuthMock, esClient: esClientMock, auditLogger, - ruleDataService: ruleDataPluginServiceMock.create() as unknown as RuleDataPluginService, + ruleDataService: ruleDataServiceMock.create(), }; const DEFAULT_SPACE = 'test_default_space_id'; diff --git a/x-pack/plugins/rule_registry/server/alert_data_client/tests/find_alerts.test.ts b/x-pack/plugins/rule_registry/server/alert_data_client/tests/find_alerts.test.ts index b94a3b96312e4..5f9a20c14ea5b 100644 --- a/x-pack/plugins/rule_registry/server/alert_data_client/tests/find_alerts.test.ts +++ b/x-pack/plugins/rule_registry/server/alert_data_client/tests/find_alerts.test.ts @@ -18,8 +18,7 @@ import { elasticsearchClientMock } from 'src/core/server/elasticsearch/client/mo import { alertingAuthorizationMock } from '../../../../alerting/server/authorization/alerting_authorization.mock'; import { AuditLogger } from '../../../../security/server'; import { AlertingAuthorizationEntity } from '../../../../alerting/server'; -import { ruleDataPluginServiceMock } from '../../rule_data_plugin_service/rule_data_plugin_service.mock'; -import { RuleDataPluginService } from '../../rule_data_plugin_service'; +import { ruleDataServiceMock } from '../../rule_data_plugin_service/rule_data_plugin_service.mock'; const alertingAuthMock = alertingAuthorizationMock.create(); const esClientMock = elasticsearchClientMock.createElasticsearchClient(); @@ -32,7 +31,7 @@ const alertsClientParams: jest.Mocked = { authorization: alertingAuthMock, esClient: esClientMock, auditLogger, - ruleDataService: ruleDataPluginServiceMock.create() as unknown as RuleDataPluginService, + ruleDataService: ruleDataServiceMock.create(), }; const DEFAULT_SPACE = 'test_default_space_id'; diff --git a/x-pack/plugins/rule_registry/server/alert_data_client/tests/get.test.ts b/x-pack/plugins/rule_registry/server/alert_data_client/tests/get.test.ts index 320e9f8a5fb1c..eaf6c0089ce12 100644 --- a/x-pack/plugins/rule_registry/server/alert_data_client/tests/get.test.ts +++ b/x-pack/plugins/rule_registry/server/alert_data_client/tests/get.test.ts @@ -19,8 +19,7 @@ import { elasticsearchClientMock } from 'src/core/server/elasticsearch/client/mo import { alertingAuthorizationMock } from '../../../../alerting/server/authorization/alerting_authorization.mock'; import { AuditLogger } from '../../../../security/server'; import { AlertingAuthorizationEntity } from '../../../../alerting/server'; -import { ruleDataPluginServiceMock } from '../../rule_data_plugin_service/rule_data_plugin_service.mock'; -import { RuleDataPluginService } from '../../rule_data_plugin_service'; +import { ruleDataServiceMock } from '../../rule_data_plugin_service/rule_data_plugin_service.mock'; const alertingAuthMock = alertingAuthorizationMock.create(); const esClientMock = elasticsearchClientMock.createElasticsearchClient(); @@ -33,7 +32,7 @@ const alertsClientParams: jest.Mocked = { authorization: alertingAuthMock, esClient: esClientMock, auditLogger, - ruleDataService: ruleDataPluginServiceMock.create() as unknown as RuleDataPluginService, + ruleDataService: ruleDataServiceMock.create(), }; const DEFAULT_SPACE = 'test_default_space_id'; diff --git a/x-pack/plugins/rule_registry/server/alert_data_client/tests/update.test.ts b/x-pack/plugins/rule_registry/server/alert_data_client/tests/update.test.ts index 922011dcb5271..85527e26a9cd3 100644 --- a/x-pack/plugins/rule_registry/server/alert_data_client/tests/update.test.ts +++ b/x-pack/plugins/rule_registry/server/alert_data_client/tests/update.test.ts @@ -18,8 +18,7 @@ import { elasticsearchClientMock } from 'src/core/server/elasticsearch/client/mo import { alertingAuthorizationMock } from '../../../../alerting/server/authorization/alerting_authorization.mock'; import { AuditLogger } from '../../../../security/server'; import { AlertingAuthorizationEntity } from '../../../../alerting/server'; -import { ruleDataPluginServiceMock } from '../../rule_data_plugin_service/rule_data_plugin_service.mock'; -import { RuleDataPluginService } from '../../rule_data_plugin_service'; +import { ruleDataServiceMock } from '../../rule_data_plugin_service/rule_data_plugin_service.mock'; const alertingAuthMock = alertingAuthorizationMock.create(); const esClientMock = elasticsearchClientMock.createElasticsearchClient(); @@ -32,7 +31,7 @@ const alertsClientParams: jest.Mocked = { authorization: alertingAuthMock, esClient: esClientMock, auditLogger, - ruleDataService: ruleDataPluginServiceMock.create() as unknown as RuleDataPluginService, + ruleDataService: ruleDataServiceMock.create(), }; const DEFAULT_SPACE = 'test_default_space_id'; diff --git a/x-pack/plugins/rule_registry/server/index.ts b/x-pack/plugins/rule_registry/server/index.ts index 5331ab86be982..d6c5b61706415 100644 --- a/x-pack/plugins/rule_registry/server/index.ts +++ b/x-pack/plugins/rule_registry/server/index.ts @@ -12,7 +12,7 @@ import { PluginInitializerContext } from 'src/core/server'; import { RuleRegistryPlugin } from './plugin'; export type { RuleRegistryPluginSetupContract, RuleRegistryPluginStartContract } from './plugin'; -export { RuleDataPluginService } from './rule_data_plugin_service'; +export { IRuleDataService, RuleDataPluginService } from './rule_data_plugin_service'; export { RuleDataClient } from './rule_data_client'; export { IRuleDataClient } from './rule_data_client/types'; export type { diff --git a/x-pack/plugins/rule_registry/server/mocks.ts b/x-pack/plugins/rule_registry/server/mocks.ts index e9ec25ddcdaba..023de6aa6029c 100644 --- a/x-pack/plugins/rule_registry/server/mocks.ts +++ b/x-pack/plugins/rule_registry/server/mocks.ts @@ -7,12 +7,17 @@ import { alertsClientMock } from './alert_data_client/alerts_client.mock'; import { createRuleDataClientMock } from './rule_data_client/rule_data_client.mock'; -import { ruleDataPluginServiceMock } from './rule_data_plugin_service/rule_data_plugin_service.mock'; +import { + ruleDataServiceMock, + RuleDataServiceMock, +} from './rule_data_plugin_service/rule_data_plugin_service.mock'; import { createLifecycleAlertServicesMock } from './utils/lifecycle_alert_services_mock'; export const ruleRegistryMocks = { createLifecycleAlertServices: createLifecycleAlertServicesMock, - createRuleDataPluginService: ruleDataPluginServiceMock.create, + createRuleDataService: ruleDataServiceMock.create, createRuleDataClient: createRuleDataClientMock, createAlertsClientMock: alertsClientMock, }; + +export { RuleDataServiceMock }; diff --git a/x-pack/plugins/rule_registry/server/plugin.ts b/x-pack/plugins/rule_registry/server/plugin.ts index b68f3eeb10669..334216ce41361 100644 --- a/x-pack/plugins/rule_registry/server/plugin.ts +++ b/x-pack/plugins/rule_registry/server/plugin.ts @@ -20,7 +20,7 @@ import { PluginStartContract as AlertingStart } from '../../alerting/server'; import { SecurityPluginSetup } from '../../security/server'; import { RuleRegistryPluginConfig } from './config'; -import { RuleDataPluginService } from './rule_data_plugin_service'; +import { IRuleDataService, RuleDataService } from './rule_data_plugin_service'; import { AlertsClientFactory } from './alert_data_client/alerts_client_factory'; import { AlertsClient } from './alert_data_client/alerts_client'; import { RacApiRequestHandlerContext, RacRequestHandlerContext } from './types'; @@ -35,7 +35,7 @@ export interface RuleRegistryPluginStartDependencies { } export interface RuleRegistryPluginSetupContract { - ruleDataService: RuleDataPluginService; + ruleDataService: IRuleDataService; } export interface RuleRegistryPluginStartContract { @@ -57,7 +57,7 @@ export class RuleRegistryPlugin private readonly logger: Logger; private readonly kibanaVersion: string; private readonly alertsClientFactory: AlertsClientFactory; - private ruleDataService: RuleDataPluginService | null; + private ruleDataService: IRuleDataService | null; private security: SecurityPluginSetup | undefined; constructor(initContext: PluginInitializerContext) { @@ -100,7 +100,7 @@ export class RuleRegistryPlugin } }; - this.ruleDataService = new RuleDataPluginService({ + this.ruleDataService = new RuleDataService({ logger, kibanaVersion, isWriteEnabled: isWriteEnabled(this.config, this.legacyConfig), diff --git a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.mock.ts b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.mock.ts index c50a982741b0c..43e727e79b76b 100644 --- a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.mock.ts +++ b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.mock.ts @@ -5,13 +5,10 @@ * 2.0. */ -import type { PublicMethodsOf } from '@kbn/utility-types'; -import { RuleDataPluginService } from './rule_data_plugin_service'; +import { IRuleDataService } from './rule_data_plugin_service'; -type Schema = PublicMethodsOf; - -const createRuleDataPluginService = () => { - const mocked: jest.Mocked = { +export const ruleDataServiceMock = { + create: (): jest.Mocked => ({ getResourcePrefix: jest.fn(), getResourceName: jest.fn(), isWriteEnabled: jest.fn(), @@ -19,10 +16,9 @@ const createRuleDataPluginService = () => { initializeIndex: jest.fn(), findIndexByName: jest.fn(), findIndicesByFeature: jest.fn(), - }; - return mocked; + }), }; -export const ruleDataPluginServiceMock = { - create: createRuleDataPluginService, -}; +export const RuleDataServiceMock = jest + .fn, []>() + .mockImplementation(ruleDataServiceMock.create); diff --git a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.ts b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.ts index 0617bc0a820ac..c5ec38ec8534e 100644 --- a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.ts +++ b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/rule_data_plugin_service.ts @@ -17,6 +17,59 @@ import { Dataset, IndexOptions } from './index_options'; import { ResourceInstaller } from './resource_installer'; import { joinWithDash } from './utils'; +/** + * A service for creating and using Elasticsearch indices for alerts-as-data. + */ +export interface IRuleDataService { + /** + * Returns a prefix used in the naming scheme of index aliases, templates + * and other Elasticsearch resources that this service creates + * for alerts-as-data indices. + */ + getResourcePrefix(): string; + + /** + * Prepends a relative resource name with the resource prefix. + * @returns Full name of the resource. + * @example 'security.alerts' => '.alerts-security.alerts' + */ + getResourceName(relativeName: string): string; + + /** + * If write is enabled, everything works as usual. + * If it's disabled, writing to all alerts-as-data indices will be disabled, + * and also Elasticsearch resources associated with the indices will not be + * installed. + */ + isWriteEnabled(): boolean; + + /** + * Installs common Elasticsearch resources used by all alerts-as-data indices. + */ + initializeService(): void; + + /** + * Initializes alerts-as-data index and starts index bootstrapping right away. + * @param indexOptions Index parameters: names and resources. + * @returns Client for reading and writing data to this index. + */ + initializeIndex(indexOptions: IndexOptions): IRuleDataClient; + + /** + * Looks up the index information associated with the given registration context and dataset. + */ + findIndexByName(registrationContext: string, dataset: Dataset): IndexInfo | null; + + /** + * Looks up the index information associated with the given Kibana "feature". + * Note: features are used in RBAC. + */ + findIndicesByFeature(featureId: ValidFeatureId, dataset?: Dataset): IndexInfo[]; +} + +// TODO: This is a leftover. Remove its usage from the "observability" plugin and delete it. +export type RuleDataPluginService = IRuleDataService; + interface ConstructorOptions { getClusterClient: () => Promise; logger: Logger; @@ -24,10 +77,7 @@ interface ConstructorOptions { isWriteEnabled: boolean; } -/** - * A service for creating and using Elasticsearch indices for alerts-as-data. - */ -export class RuleDataPluginService { +export class RuleDataService implements IRuleDataService { private readonly indicesByBaseName: Map; private readonly indicesByFeatureId: Map; private readonly resourceInstaller: ResourceInstaller; @@ -49,37 +99,18 @@ export class RuleDataPluginService { this.isInitialized = false; } - /** - * Returns a prefix used in the naming scheme of index aliases, templates - * and other Elasticsearch resources that this service creates - * for alerts-as-data indices. - */ public getResourcePrefix(): string { return INDEX_PREFIX; } - /** - * Prepends a relative resource name with the resource prefix. - * @returns Full name of the resource. - * @example 'security.alerts' => '.alerts-security.alerts' - */ public getResourceName(relativeName: string): string { return joinWithDash(this.getResourcePrefix(), relativeName); } - /** - * If write is enabled, everything works as usual. - * If it's disabled, writing to all alerts-as-data indices will be disabled, - * and also Elasticsearch resources associated with the indices will not be - * installed. - */ public isWriteEnabled(): boolean { return this.options.isWriteEnabled; } - /** - * Installs common Elasticsearch resources used by all alerts-as-data indices. - */ public initializeService(): void { // Run the installation of common resources and handle exceptions. this.installCommonResources = this.resourceInstaller @@ -93,11 +124,6 @@ export class RuleDataPluginService { this.isInitialized = true; } - /** - * Initializes alerts-as-data index and starts index bootstrapping right away. - * @param indexOptions Index parameters: names and resources. - * @returns Client for reading and writing data to this index. - */ public initializeIndex(indexOptions: IndexOptions): IRuleDataClient { if (!this.isInitialized) { throw new Error( @@ -156,18 +182,11 @@ export class RuleDataPluginService { }); } - /** - * Looks up the index information associated with the given registration context and dataset. - */ public findIndexByName(registrationContext: string, dataset: Dataset): IndexInfo | null { const baseName = this.getResourceName(`${registrationContext}.${dataset}`); return this.indicesByBaseName.get(baseName) ?? null; } - /** - * Looks up the index information associated with the given Kibana "feature". - * Note: features are used in RBAC. - */ public findIndicesByFeature(featureId: ValidFeatureId, dataset?: Dataset): IndexInfo[] { const foundIndices = this.indicesByFeatureId.get(featureId) ?? []; return dataset ? foundIndices.filter((i) => i.indexOptions.dataset === dataset) : foundIndices; diff --git a/x-pack/plugins/security_solution/server/config.mock.ts b/x-pack/plugins/security_solution/server/config.mock.ts new file mode 100644 index 0000000000000..c1d1e02ca35f4 --- /dev/null +++ b/x-pack/plugins/security_solution/server/config.mock.ts @@ -0,0 +1,62 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { DEFAULT_SIGNALS_INDEX, SIGNALS_INDEX_KEY } from '../common/constants'; +import { + ExperimentalFeatures, + parseExperimentalConfigValue, +} from '../common/experimental_features'; +import { ConfigType } from './config'; +import { UnderlyingLogClient } from './lib/detection_engine/rule_execution_log/types'; + +export const createMockConfig = (): ConfigType => { + const enableExperimental: string[] = []; + + return { + [SIGNALS_INDEX_KEY]: DEFAULT_SIGNALS_INDEX, + maxRuleImportExportSize: 10000, + maxRuleImportPayloadBytes: 10485760, + maxTimelineImportExportSize: 10000, + maxTimelineImportPayloadBytes: 10485760, + enableExperimental, + endpointResultListDefaultFirstPageIndex: 0, + endpointResultListDefaultPageSize: 10, + packagerTaskInterval: '60s', + alertMergeStrategy: 'missingFields', + alertIgnoreFields: [], + prebuiltRulesFromFileSystem: true, + prebuiltRulesFromSavedObjects: false, + ruleExecutionLog: { + underlyingClient: UnderlyingLogClient.savedObjects, + }, + + kibanaIndex: '.kibana', + experimentalFeatures: parseExperimentalConfigValue(enableExperimental), + }; +}; + +const withExperimentalFeature = ( + config: ConfigType, + feature: keyof ExperimentalFeatures +): ConfigType => { + const enableExperimental = config.enableExperimental.concat(feature); + return { + ...config, + enableExperimental, + experimentalFeatures: parseExperimentalConfigValue(enableExperimental), + }; +}; + +const withRuleRegistryEnabled = (config: ConfigType, isEnabled: boolean): ConfigType => { + return isEnabled ? withExperimentalFeature(config, 'ruleRegistryEnabled') : config; +}; + +export const configMock = { + createDefault: createMockConfig, + withExperimentalFeature, + withRuleRegistryEnabled, +}; diff --git a/x-pack/plugins/security_solution/server/config.ts b/x-pack/plugins/security_solution/server/config.ts index 61cbb5641c5f6..072e23b7a773c 100644 --- a/x-pack/plugins/security_solution/server/config.ts +++ b/x-pack/plugins/security_solution/server/config.ts @@ -9,8 +9,10 @@ import { schema, TypeOf } from '@kbn/config-schema'; import { PluginInitializerContext } from '../../../../src/core/server'; import { SIGNALS_INDEX_KEY, DEFAULT_SIGNALS_INDEX } from '../common/constants'; import { + ExperimentalFeatures, getExperimentalAllowedValues, isValidExperimentalValue, + parseExperimentalConfigValue, } from '../common/experimental_features'; import { UnderlyingLogClient } from './lib/detection_engine/rule_execution_log/types'; @@ -134,7 +136,23 @@ export const configSchema = schema.object({ prebuiltRulesFromSavedObjects: schema.boolean({ defaultValue: true }), }); -export const createConfig = (context: PluginInitializerContext) => - context.config.get>(); +export type ConfigSchema = TypeOf; -export type ConfigType = TypeOf; +export type ConfigType = ConfigSchema & { + kibanaIndex: string; + experimentalFeatures: ExperimentalFeatures; +}; + +export const createConfig = (context: PluginInitializerContext): ConfigType => { + const globalConfig = context.config.legacy.get(); + const pluginConfig = context.config.get>(); + + const kibanaIndex = globalConfig.kibana.index; + const experimentalFeatures = parseExperimentalConfigValue(pluginConfig.enableExperimental); + + return { + ...pluginConfig, + kibanaIndex, + experimentalFeatures, + }; +}; diff --git a/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts b/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts index 5a47c8a616c00..caea18da75ae4 100644 --- a/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts +++ b/x-pack/plugins/security_solution/server/endpoint/endpoint_app_context_services.ts @@ -27,13 +27,18 @@ import { import { ManifestManager } from './services/artifacts'; import { AppClientFactory } from '../client'; import { ConfigType } from '../config'; +import { IRequestContextFactory } from '../request_context_factory'; import { LicenseService } from '../../common/license'; -import { - ExperimentalFeatures, - parseExperimentalConfigValue, -} from '../../common/experimental_features'; +import { ExperimentalFeatures } from '../../common/experimental_features'; import { EndpointMetadataService } from './services/metadata'; -import { EndpointAppContentServicesNotStartedError } from './errors'; +import { + EndpointAppContentServicesNotSetUpError, + EndpointAppContentServicesNotStartedError, +} from './errors'; + +export interface EndpointAppContextServiceSetupContract { + securitySolutionRequestContextFactory: IRequestContextFactory; +} export type EndpointAppContextServiceStartContract = Partial< Pick< @@ -59,40 +64,29 @@ export type EndpointAppContextServiceStartContract = Partial< * of the plugin lifecycle. And stop during the stop phase, if needed. */ export class EndpointAppContextService { - private agentService: AgentService | undefined; - private manifestManager: ManifestManager | undefined; - private packagePolicyService: PackagePolicyServiceInterface | undefined; - private agentPolicyService: AgentPolicyServiceInterface | undefined; - private config: ConfigType | undefined; - private license: LicenseService | undefined; + private setupDependencies: EndpointAppContextServiceSetupContract | null = null; + private startDependencies: EndpointAppContextServiceStartContract | null = null; public security: SecurityPluginStart | undefined; - private cases: CasesPluginStartContract | undefined; - private endpointMetadataService: EndpointMetadataService | undefined; - private experimentalFeatures: ExperimentalFeatures | undefined; + + public setup(dependencies: EndpointAppContextServiceSetupContract) { + this.setupDependencies = dependencies; + } public start(dependencies: EndpointAppContextServiceStartContract) { - this.agentService = dependencies.agentService; - this.packagePolicyService = dependencies.packagePolicyService; - this.agentPolicyService = dependencies.agentPolicyService; - this.manifestManager = dependencies.manifestManager; - this.config = dependencies.config; - this.license = dependencies.licenseService; + if (!this.setupDependencies) { + throw new EndpointAppContentServicesNotSetUpError(); + } + + this.startDependencies = dependencies; this.security = dependencies.security; - this.cases = dependencies.cases; - this.endpointMetadataService = dependencies.endpointMetadataService; - this.experimentalFeatures = parseExperimentalConfigValue(this.config.enableExperimental); - if (this.manifestManager && dependencies.registerIngestCallback) { + if (dependencies.registerIngestCallback && dependencies.manifestManager) { dependencies.registerIngestCallback( 'packagePolicyCreate', getPackagePolicyCreateCallback( dependencies.logger, - this.manifestManager, - dependencies.appClientFactory, - dependencies.config.maxTimelineImportExportSize, - dependencies.config.prebuiltRulesFromFileSystem, - dependencies.config.prebuiltRulesFromSavedObjects, - dependencies.security, + dependencies.manifestManager, + this.setupDependencies.securitySolutionRequestContextFactory, dependencies.alerting, dependencies.licenseService, dependencies.exceptionListsClient @@ -106,7 +100,10 @@ export class EndpointAppContextService { dependencies.registerIngestCallback( 'postPackagePolicyDelete', - getPackagePolicyDeleteCallback(dependencies.exceptionListsClient, this.experimentalFeatures) + getPackagePolicyDeleteCallback( + dependencies.exceptionListsClient, + dependencies.config.experimentalFeatures + ) ); } } @@ -114,43 +111,43 @@ export class EndpointAppContextService { public stop() {} public getExperimentalFeatures(): Readonly | undefined { - return this.experimentalFeatures; + return this.startDependencies?.config.experimentalFeatures; } public getEndpointMetadataService(): EndpointMetadataService { - if (!this.endpointMetadataService) { + if (this.startDependencies == null) { throw new EndpointAppContentServicesNotStartedError(); } - return this.endpointMetadataService; + return this.startDependencies.endpointMetadataService; } public getAgentService(): AgentService | undefined { - return this.agentService; + return this.startDependencies?.agentService; } public getPackagePolicyService(): PackagePolicyServiceInterface | undefined { - return this.packagePolicyService; + return this.startDependencies?.packagePolicyService; } public getAgentPolicyService(): AgentPolicyServiceInterface | undefined { - return this.agentPolicyService; + return this.startDependencies?.agentPolicyService; } public getManifestManager(): ManifestManager | undefined { - return this.manifestManager; + return this.startDependencies?.manifestManager; } public getLicenseService(): LicenseService { - if (!this.license) { + if (this.startDependencies == null) { throw new EndpointAppContentServicesNotStartedError(); } - return this.license; + return this.startDependencies.licenseService; } public async getCasesClient(req: KibanaRequest): Promise { - if (!this.cases) { + if (this.startDependencies?.cases == null) { throw new EndpointAppContentServicesNotStartedError(); } - return this.cases.getCasesClientWithRequest(req); + return this.startDependencies.cases.getCasesClientWithRequest(req); } } diff --git a/x-pack/plugins/security_solution/server/endpoint/errors.ts b/x-pack/plugins/security_solution/server/endpoint/errors.ts index fae15984d9c44..7260d6055b310 100644 --- a/x-pack/plugins/security_solution/server/endpoint/errors.ts +++ b/x-pack/plugins/security_solution/server/endpoint/errors.ts @@ -17,6 +17,12 @@ export class EndpointError extends Error { export class NotFoundError extends EndpointError {} +export class EndpointAppContentServicesNotSetUpError extends EndpointError { + constructor() { + super('EndpointAppContextService has not been set up (EndpointAppContextService.setup())'); + } +} + export class EndpointAppContentServicesNotStartedError extends EndpointError { constructor() { super('EndpointAppContextService has not been started (EndpointAppContextService.start())'); diff --git a/x-pack/plugins/security_solution/server/endpoint/mocks.ts b/x-pack/plugins/security_solution/server/endpoint/mocks.ts index 39833b6e995d1..190770f3d860d 100644 --- a/x-pack/plugins/security_solution/server/endpoint/mocks.ts +++ b/x-pack/plugins/security_solution/server/endpoint/mocks.ts @@ -22,6 +22,7 @@ import { AppClientFactory } from '../client'; import { createMockConfig } from '../lib/detection_engine/routes/__mocks__'; import { EndpointAppContextService, + EndpointAppContextServiceSetupContract, EndpointAppContextServiceStartContract, } from './endpoint_app_context_services'; import { ManifestManager } from './services/artifacts/manifest_manager/manifest_manager'; @@ -37,6 +38,7 @@ import { parseExperimentalConfigValue } from '../../common/experimental_features // a restricted path. // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { createCasesClientMock } from '../../../cases/server/client/mocks'; +import { requestContextFactoryMock } from '../request_context_factory.mock'; import { EndpointMetadataService } from './services/metadata'; /** @@ -69,13 +71,25 @@ export const createMockEndpointAppContextService = ( } as unknown as jest.Mocked; }; +/** + * Creates a mocked input contract for the `EndpointAppContextService#setup()` method + */ +export const createMockEndpointAppContextServiceSetupContract = + (): jest.Mocked => { + return { + securitySolutionRequestContextFactory: requestContextFactoryMock.create(), + }; + }; + /** * Creates a mocked input contract for the `EndpointAppContextService#start()` method */ export const createMockEndpointAppContextServiceStartContract = (): jest.Mocked => { - const factory = new AppClientFactory(); const config = createMockConfig(); + const factory = new AppClientFactory(); + factory.setup({ getSpaceId: () => 'mockSpace', config }); + const casesClientMock = createCasesClientMock(); const savedObjectsStart = savedObjectsServiceMock.createStartContract(); const agentService = createMockAgentService(); @@ -86,8 +100,6 @@ export const createMockEndpointAppContextServiceStartContract = agentPolicyService ); - factory.setup({ getSpaceId: () => 'mockSpace', config }); - return { agentService, agentPolicyService, diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/audit_log.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/audit_log.test.ts index 5ce7962000788..c6df8c9183917 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/audit_log.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/audit_log.test.ts @@ -25,6 +25,7 @@ import { parseExperimentalConfigValue } from '../../../../common/experimental_fe import { createMockConfig } from '../../../lib/detection_engine/routes/__mocks__'; import { EndpointAppContextService } from '../../endpoint_app_context_services'; import { + createMockEndpointAppContextServiceSetupContract, createMockEndpointAppContextServiceStartContract, createRouteHandlerContext, } from '../../mocks'; @@ -130,6 +131,7 @@ describe('Action Log API', () => { const esClientMock = elasticsearchServiceMock.createScopedClusterClient(); const routerMock = httpServiceMock.createRouter(); endpointAppContextService = new EndpointAppContextService(); + endpointAppContextService.setup(createMockEndpointAppContextServiceSetupContract()); endpointAppContextService.start(createMockEndpointAppContextServiceStartContract()); registerActionAuditLogRoutes(routerMock, { diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.test.ts index ee3bc5e1f21e3..a483a33ea4c8d 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/isolation.test.ts @@ -18,6 +18,7 @@ import { parseExperimentalConfigValue } from '../../../../common/experimental_fe import { SecuritySolutionRequestHandlerContext } from '../../../types'; import { EndpointAppContextService } from '../../endpoint_app_context_services'; import { + createMockEndpointAppContextServiceSetupContract, createMockEndpointAppContextServiceStartContract, createMockPackageService, createRouteHandlerContext, @@ -157,9 +158,12 @@ describe('Host Isolation', () => { keep_policies_up_to_date: false, }) ); + licenseEmitter = new Subject(); licenseService = new LicenseService(); licenseService.start(licenseEmitter); + + endpointAppContextService.setup(createMockEndpointAppContextServiceSetupContract()); endpointAppContextService.start({ ...startContract, licenseService, diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/actions/status.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/actions/status.test.ts index facd53643bc4f..2f8ba30936f25 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/actions/status.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/actions/status.test.ts @@ -21,6 +21,7 @@ import { parseExperimentalConfigValue } from '../../../../common/experimental_fe import { createMockConfig } from '../../../lib/detection_engine/routes/__mocks__'; import { EndpointAppContextService } from '../../endpoint_app_context_services'; import { + createMockEndpointAppContextServiceSetupContract, createMockEndpointAppContextServiceStartContract, createRouteHandlerContext, } from '../../mocks'; @@ -67,6 +68,7 @@ describe('Endpoint Action Status', () => { const esClientMock = elasticsearchServiceMock.createScopedClusterClient(); const routerMock = httpServiceMock.createRouter(); endpointAppContextService = new EndpointAppContextService(); + endpointAppContextService.setup(createMockEndpointAppContextServiceSetupContract()); endpointAppContextService.start(createMockEndpointAppContextServiceStartContract()); registerActionStatusRoutes(routerMock, { diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts index 3e5050c05814a..7c2e5de928484 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/metadata.test.ts @@ -22,6 +22,7 @@ import { HostInfo, HostResultList, HostStatus } from '../../../../common/endpoin import { parseExperimentalConfigValue } from '../../../../common/experimental_features'; import { registerEndpointRoutes } from './index'; import { + createMockEndpointAppContextServiceSetupContract, createMockEndpointAppContextServiceStartContract, createMockPackageService, createRouteHandlerContext, @@ -134,6 +135,7 @@ describe('test endpoint route', () => { keep_policies_up_to_date: false, }) ); + endpointAppContextService.setup(createMockEndpointAppContextServiceSetupContract()); endpointAppContextService.start({ ...startContract, packageService: mockPackageService }); mockAgentService = startContract.agentService!; @@ -394,6 +396,7 @@ describe('test endpoint route', () => { keep_policies_up_to_date: false, }) ); + endpointAppContextService.setup(createMockEndpointAppContextServiceSetupContract()); endpointAppContextService.start({ ...startContract, packageService: mockPackageService }); mockAgentService = startContract.agentService!; diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/policy/handlers.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/policy/handlers.test.ts index 90cda7ceb05d4..f25171c6734c8 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/policy/handlers.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/policy/handlers.test.ts @@ -7,6 +7,7 @@ import { EndpointAppContextService } from '../../endpoint_app_context_services'; import { + createMockEndpointAppContextServiceSetupContract, createMockEndpointAppContextServiceStartContract, createRouteHandlerContext, } from '../../mocks'; @@ -45,6 +46,7 @@ describe('test policy response handler', () => { mockSavedObjectClient = savedObjectsClientMock.create(); mockResponse = httpServerMock.createResponseFactory(); endpointAppContextService = new EndpointAppContextService(); + endpointAppContextService.setup(createMockEndpointAppContextServiceSetupContract()); endpointAppContextService.start(createMockEndpointAppContextServiceStartContract()); }); @@ -161,6 +163,7 @@ describe('test policy response handler', () => { page: 1, perPage: 1, }; + endpointAppContextService.setup(createMockEndpointAppContextServiceSetupContract()); endpointAppContextService.start({ ...createMockEndpointAppContextServiceStartContract(), ...{ agentService: mockAgentService }, diff --git a/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts b/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts index 2f31f54143f74..71c093e0781b0 100644 --- a/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts +++ b/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.test.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { ExceptionListSchema } from '@kbn/securitysolution-io-ts-list-types'; + import { httpServerMock, loggingSystemMock } from 'src/core/server/mocks'; import { createNewPackagePolicyMock, deletePackagePolicyMock } from '../../../fleet/common/mocks'; import { @@ -18,7 +20,8 @@ import { getPackagePolicyUpdateCallback, } from './fleet_integration'; import { KibanaRequest } from 'kibana/server'; -import { createMockConfig, requestContextMock } from '../lib/detection_engine/routes/__mocks__'; +import { requestContextMock } from '../lib/detection_engine/routes/__mocks__'; +import { requestContextFactoryMock } from '../request_context_factory.mock'; import { EndpointAppContextServiceStartContract } from '../endpoint/endpoint_app_context_services'; import { createMockEndpointAppContextServiceStartContract } from '../endpoint/mocks'; import { licenseMock } from '../../../licensing/common/licensing.mock'; @@ -42,16 +45,12 @@ import { ExperimentalFeatures, } from '../../common/experimental_features'; import { DeletePackagePoliciesResponse } from '../../../fleet/common'; -import { ExceptionListSchema } from '@kbn/securitysolution-io-ts-list-types'; describe('ingest_integration tests ', () => { let endpointAppContextMock: EndpointAppContextServiceStartContract; let req: KibanaRequest; let ctx: SecuritySolutionRequestHandlerContext; const exceptionListClient: ExceptionListClient = getExceptionListClientMock(); - const maxTimelineImportExportSize = createMockConfig().maxTimelineImportExportSize; - const prebuiltRulesFromFileSystem = createMockConfig().prebuiltRulesFromFileSystem; - const prebuiltRulesFromSavedObjects = createMockConfig().prebuiltRulesFromSavedObjects; let licenseEmitter: Subject; let licenseService: LicenseService; const Platinum = licenseMock.createLicense({ license: { type: 'platinum', mode: 'platinum' } }); @@ -88,11 +87,7 @@ describe('ingest_integration tests ', () => { const callback = getPackagePolicyCreateCallback( logger, manifestManager, - endpointAppContextMock.appClientFactory, - maxTimelineImportExportSize, - prebuiltRulesFromFileSystem, - prebuiltRulesFromSavedObjects, - endpointAppContextMock.security, + requestContextFactoryMock.create(), endpointAppContextMock.alerting, licenseService, exceptionListClient diff --git a/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.ts b/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.ts index 09810a6c88c3d..a53d5d43de524 100644 --- a/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.ts +++ b/x-pack/plugins/security_solution/server/fleet_integration/fleet_integration.ts @@ -8,7 +8,6 @@ import { KibanaRequest, Logger, RequestHandlerContext } from 'kibana/server'; import { ExceptionListClient } from '../../../lists/server'; import { PluginStartContract as AlertsStartContract } from '../../../alerting/server'; -import { SecurityPluginStart } from '../../../security/server'; import { PostPackagePolicyCreateCallback, PostPackagePolicyDeleteCallback, @@ -18,15 +17,15 @@ import { import { NewPackagePolicy, UpdatePackagePolicy } from '../../../fleet/common'; import { NewPolicyData, PolicyConfig } from '../../common/endpoint/types'; -import { ManifestManager } from '../endpoint/services'; -import { AppClientFactory } from '../client'; +import { ExperimentalFeatures } from '../../common/experimental_features'; import { LicenseService } from '../../common/license'; +import { ManifestManager } from '../endpoint/services'; +import { IRequestContextFactory } from '../request_context_factory'; import { installPrepackagedRules } from './handlers/install_prepackaged_rules'; import { createPolicyArtifactManifest } from './handlers/create_policy_artifact_manifest'; import { createDefaultPolicy } from './handlers/create_default_policy'; import { validatePolicyAgainstLicense } from './handlers/validate_policy_against_license'; import { removePolicyFromTrustedApps } from './handlers/remove_policy_from_trusted_apps'; -import { ExperimentalFeatures } from '../../common/experimental_features'; const isEndpointPackagePolicy = ( packagePolicy: T @@ -40,11 +39,7 @@ const isEndpointPackagePolicy = ( export const getPackagePolicyCreateCallback = ( logger: Logger, manifestManager: ManifestManager, - appClientFactory: AppClientFactory, - maxTimelineImportExportSize: number, - prebuiltRulesFromFileSystem: boolean, - prebuiltRulesFromSavedObjects: boolean, - securityStart: SecurityPluginStart, + securitySolutionRequestContextFactory: IRequestContextFactory, alerts: AlertsStartContract, licenseService: LicenseService, exceptionsClient: ExceptionListClient | undefined @@ -59,20 +54,23 @@ export const getPackagePolicyCreateCallback = ( return newPackagePolicy; } + // In this callback we are handling an HTTP request to the fleet plugin. Since we use + // code from the security_solution plugin to handle it (installPrepackagedRules), + // we need to build the context that is native to security_solution and pass it there. + const securitySolutionContext = await securitySolutionRequestContextFactory.create( + context, + request + ); + // perform these operations in parallel in order to help in not delaying the API response too much const [, manifestValue] = await Promise.all([ // Install Detection Engine prepackaged rules exceptionsClient && installPrepackagedRules({ logger, - appClientFactory, - context, + context: securitySolutionContext, request, - securityStart, alerts, - maxTimelineImportExportSize, - prebuiltRulesFromFileSystem, - prebuiltRulesFromSavedObjects, exceptionsClient, }), diff --git a/x-pack/plugins/security_solution/server/fleet_integration/handlers/install_prepackaged_rules.ts b/x-pack/plugins/security_solution/server/fleet_integration/handlers/install_prepackaged_rules.ts index d8adf4ea6a1ca..01368ccb22c62 100644 --- a/x-pack/plugins/security_solution/server/fleet_integration/handlers/install_prepackaged_rules.ts +++ b/x-pack/plugins/security_solution/server/fleet_integration/handlers/install_prepackaged_rules.ts @@ -5,25 +5,18 @@ * 2.0. */ -import { KibanaRequest, Logger, RequestHandlerContext } from 'kibana/server'; +import { KibanaRequest, Logger } from 'kibana/server'; import { ExceptionListClient } from '../../../../lists/server'; import { PluginStartContract as AlertsStartContract } from '../../../../alerting/server'; -import { SecurityPluginStart } from '../../../../security/server'; -import { AppClientFactory } from '../../client'; import { createDetectionIndex } from '../../lib/detection_engine/routes/index/create_index_route'; import { createPrepackagedRules } from '../../lib/detection_engine/routes/rules/add_prepackaged_rules_route'; -import { buildFrameworkRequest } from '../../lib/timeline/utils/common'; +import { SecuritySolutionApiRequestHandlerContext } from '../../types'; export interface InstallPrepackagedRulesProps { logger: Logger; - appClientFactory: AppClientFactory; - context: RequestHandlerContext; + context: SecuritySolutionApiRequestHandlerContext; request: KibanaRequest; - securityStart: SecurityPluginStart; alerts: AlertsStartContract; - maxTimelineImportExportSize: number; - prebuiltRulesFromFileSystem: boolean; - prebuiltRulesFromSavedObjects: boolean; exceptionsClient: ExceptionListClient; } @@ -33,29 +26,14 @@ export interface InstallPrepackagedRulesProps { */ export const installPrepackagedRules = async ({ logger, - appClientFactory, context, request, - securityStart, alerts, - maxTimelineImportExportSize, - prebuiltRulesFromFileSystem, - prebuiltRulesFromSavedObjects, exceptionsClient, }: InstallPrepackagedRulesProps): Promise => { - // prep for detection rules creation - const appClient = appClientFactory.create(request); - - // This callback is called by fleet plugin. - // It doesn't have access to SecuritySolutionRequestHandlerContext in runtime. - // Muting the error to have green CI. - // @ts-expect-error - const frameworkRequest = await buildFrameworkRequest(context, securityStart, request); - // Create detection index & rules (if necessary). move past any failure, this is just a convenience try { - // @ts-expect-error - await createDetectionIndex(context, appClient); + await createDetectionIndex(context); } catch (err) { if (err.statusCode !== 409) { // 409 -> detection index already exists, which is fine @@ -68,14 +46,8 @@ export const installPrepackagedRules = async ({ // this checks to make sure index exists first, safe to try in case of failure above // may be able to recover from minor errors await createPrepackagedRules( - // @ts-expect-error context, - appClient, alerts.getRulesClientWithRequest(request), - frameworkRequest, - maxTimelineImportExportSize, - prebuiltRulesFromFileSystem, - prebuiltRulesFromSavedObjects, exceptionsClient ); } catch (err) { diff --git a/x-pack/plugins/security_solution/server/index.ts b/x-pack/plugins/security_solution/server/index.ts index 7e3da726f6ebe..0adcd25f5e246 100644 --- a/x-pack/plugins/security_solution/server/index.ts +++ b/x-pack/plugins/security_solution/server/index.ts @@ -7,7 +7,7 @@ import { PluginInitializerContext, PluginConfigDescriptor } from '../../../../src/core/server'; import { Plugin, PluginSetup, PluginStart } from './plugin'; -import { configSchema, ConfigType } from './config'; +import { configSchema, ConfigSchema, ConfigType } from './config'; import { SIGNALS_INDEX_KEY } from '../common/constants'; import { AppClient } from './types'; @@ -15,7 +15,7 @@ export const plugin = (context: PluginInitializerContext) => { return new Plugin(context); }; -export const config: PluginConfigDescriptor = { +export const config: PluginConfigDescriptor = { exposeToBrowser: { enableExperimental: true, }, @@ -47,5 +47,5 @@ export const config: PluginConfigDescriptor = { export { ConfigType, Plugin, PluginSetup, PluginStart }; export { AppClient }; -export type { AppRequestContext } from './types'; +export type { SecuritySolutionApiRequestHandlerContext } from './types'; export { EndpointError } from './endpoint/errors'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/index.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/index.ts index 8417115fb1896..cf0ceaff4ec4c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/index.ts @@ -5,34 +5,11 @@ * 2.0. */ -import { DEFAULT_SIGNALS_INDEX, SIGNALS_INDEX_KEY } from '../../../../../common/constants'; -import { requestContextMock } from './request_context'; -import { serverMock } from './server'; -import { requestMock } from './request'; -import { responseMock } from './response_factory'; -import { ConfigType } from '../../../../config'; -import { UnderlyingLogClient } from '../../rule_execution_log/types'; - -export { requestMock, requestContextMock, responseMock, serverMock }; - -export const createMockConfig = (): ConfigType => ({ - [SIGNALS_INDEX_KEY]: DEFAULT_SIGNALS_INDEX, - maxRuleImportExportSize: 10000, - maxRuleImportPayloadBytes: 10485760, - maxTimelineImportExportSize: 10000, - maxTimelineImportPayloadBytes: 10485760, - enableExperimental: [], - endpointResultListDefaultFirstPageIndex: 0, - endpointResultListDefaultPageSize: 10, - packagerTaskInterval: '60s', - alertMergeStrategy: 'missingFields', - alertIgnoreFields: [], - prebuiltRulesFromFileSystem: true, - prebuiltRulesFromSavedObjects: false, - ruleExecutionLog: { - underlyingClient: UnderlyingLogClient.savedObjects, - }, -}); +export { requestContextMock } from './request_context'; +export { requestMock } from './request'; +export { responseMock } from './response_factory'; +export { serverMock } from './server'; +export { configMock, createMockConfig } from '../../../../config.mock'; export const mockGetCurrentUser = { user: { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts index 6039ad6ab6126..2f5f8ac846954 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts @@ -5,66 +5,103 @@ * 2.0. */ -import type { SecuritySolutionRequestHandlerContext } from '../../../../types'; -import { - coreMock, - elasticsearchServiceMock, - savedObjectsClientMock, -} from '../../../../../../../../src/core/server/mocks'; +import type { MockedKeys } from '@kbn/utility-types/jest'; +import { coreMock } from 'src/core/server/mocks'; + +import { ActionsApiRequestHandlerContext } from '../../../../../../actions/server'; +import { AlertingApiRequestHandlerContext } from '../../../../../../alerting/server'; import { rulesClientMock } from '../../../../../../alerting/server/mocks'; import { licensingMock } from '../../../../../../licensing/server/mocks'; +import { listMock } from '../../../../../../lists/server/mocks'; +import { ruleRegistryMocks } from '../../../../../../rule_registry/server/mocks'; + import { siemMock } from '../../../../mocks'; +import { createMockConfig } from '../../../../config.mock'; import { ruleExecutionLogClientMock } from '../../rule_execution_log/__mocks__/rule_execution_log_client'; +import { requestMock } from './request'; +import { internalFrameworkRequest } from '../../../framework'; -const createMockClients = () => ({ - rulesClient: rulesClientMock.create(), - licensing: { license: licensingMock.createLicenseMock() }, - clusterClient: elasticsearchServiceMock.createScopedClusterClient(), - savedObjectsClient: savedObjectsClientMock.create(), - ruleExecutionLogClient: ruleExecutionLogClientMock.create(), - appClient: siemMock.createClient(), -}); - -/** - * Adds mocking to the interface so we don't have to cast everywhere - */ -type SecuritySolutionRequestHandlerContextMock = SecuritySolutionRequestHandlerContext & { - core: { - elasticsearch: { - client: { - asCurrentUser: { - updateByQuery: jest.Mock; - search: jest.Mock; - security: { - hasPrivileges: jest.Mock; - }; - }; - }; - }; +import type { + SecuritySolutionApiRequestHandlerContext, + SecuritySolutionRequestHandlerContext, +} from '../../../../types'; + +const createMockClients = () => { + const core = coreMock.createRequestHandlerContext(); + const license = licensingMock.createLicenseMock(); + + return { + core, + clusterClient: core.elasticsearch.client, + savedObjectsClient: core.savedObjects.client, + + licensing: { + ...licensingMock.createRequestHandlerContext({ license }), + license, + }, + lists: { + listClient: listMock.getListClient(), + exceptionListClient: listMock.getExceptionListClient(core.savedObjects.client), + }, + rulesClient: rulesClientMock.create(), + ruleDataService: ruleRegistryMocks.createRuleDataService(), + + config: createMockConfig(), + appClient: siemMock.createClient(), + ruleExecutionLogClient: ruleExecutionLogClientMock.create(), }; }; +type MockClients = ReturnType; + +type SecuritySolutionRequestHandlerContextMock = + MockedKeys & { + core: MockClients['core']; + }; + const createRequestContextMock = ( - clients: ReturnType = createMockClients() + clients: MockClients = createMockClients() ): SecuritySolutionRequestHandlerContextMock => { - const coreContext = coreMock.createRequestHandlerContext(); return { - alerting: { getRulesClient: jest.fn(() => clients.rulesClient) }, - core: { - ...coreContext, - elasticsearch: { - ...coreContext.elasticsearch, - client: clients.clusterClient, - }, - savedObjects: { client: clients.savedObjectsClient }, - }, + core: clients.core, + securitySolution: createSecuritySolutionRequestContextMock(clients), + actions: {} as unknown as jest.Mocked, + alerting: { + getRulesClient: jest.fn(() => clients.rulesClient), + } as unknown as jest.Mocked, licensing: clients.licensing, - securitySolution: { - getAppClient: jest.fn(() => clients.appClient), - getExecutionLogClient: jest.fn(() => clients.ruleExecutionLogClient), - getSpaceId: jest.fn(() => 'default'), + lists: { + getListClient: jest.fn(() => clients.lists.listClient), + getExceptionListClient: jest.fn(() => clients.lists.exceptionListClient), }, - } as unknown as SecuritySolutionRequestHandlerContextMock; + }; +}; + +const createSecuritySolutionRequestContextMock = ( + clients: MockClients +): jest.Mocked => { + const core = clients.core; + const kibanaRequest = requestMock.create(); + + return { + core, + getConfig: jest.fn(() => clients.config), + getFrameworkRequest: jest.fn(() => { + return { + ...kibanaRequest.body, + [internalFrameworkRequest]: kibanaRequest, + context: { core }, + user: { + username: 'mockUser', + }, + }; + }), + getAppClient: jest.fn(() => clients.appClient), + getSpaceId: jest.fn(() => 'default'), + getRuleDataService: jest.fn(() => clients.ruleDataService), + getExecutionLogClient: jest.fn(() => clients.ruleExecutionLogClient), + getExceptionListClient: jest.fn(() => clients.lists.exceptionListClient), + }; }; const createTools = () => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_responses.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_responses.ts index 9d1cd3cbca3fb..1520b4da82d8d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_responses.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_responses.ts @@ -5,7 +5,9 @@ * 2.0. */ -import { SavedObjectsFindResponse, SavedObjectsFindResult } from 'kibana/server'; +import type { estypes } from '@elastic/elasticsearch'; +import { SavedObjectsFindResponse, SavedObjectsFindResult } from 'src/core/server'; + import { ActionResult } from '../../../../../../actions/server'; import { SignalSearchResponse } from '../../signals/types'; import { @@ -562,6 +564,28 @@ export const getFindBulkResultStatus = (): FindBulkExecutionLogResponse => ({ ], }); +export const getBasicEmptySearchResponse = (): estypes.SearchResponse => ({ + took: 1, + timed_out: false, + _shards: { total: 1, successful: 1, skipped: 0, failed: 0 }, + hits: { + hits: [], + total: { relation: 'eq', value: 0 }, + max_score: 0, + }, +}); + +export const getBasicNoShardsSearchResponse = (): estypes.SearchResponse => ({ + took: 1, + timed_out: false, + _shards: { total: 0, successful: 0, skipped: 0, failed: 0 }, + hits: { + hits: [], + total: { relation: 'eq', value: 0 }, + max_score: 0, + }, +}); + export const getEmptySignalsResponse = (): SignalSearchResponse => ({ took: 1, timed_out: false, @@ -588,7 +612,7 @@ export const getEmptyEqlSequencesResponse = (): EqlSearchResponse => ({ timed_out: false, }); -export const getSuccessfulSignalUpdateResponse = () => ({ +export const getSuccessfulSignalUpdateResponse = (): estypes.UpdateByQueryResponse => ({ took: 18, timed_out: false, total: 1, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/create_index_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/create_index_route.ts index 6ec23c32e4976..b011fd3fcd247 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/create_index_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/create_index_route.ts @@ -16,9 +16,8 @@ import { createBootstrapIndex, } from '@kbn/securitysolution-es-utils'; import type { - AppClient, + SecuritySolutionApiRequestHandlerContext, SecuritySolutionPluginRouter, - SecuritySolutionRequestHandlerContext, } from '../../../../types'; import { DETECTION_ENGINE_INDEX_URL } from '../../../../../common/constants'; import { buildSiemResponse } from '../utils'; @@ -32,15 +31,8 @@ import signalsPolicy from './signals_policy.json'; import { templateNeedsUpdate } from './check_template_version'; import { getIndexVersion } from './get_index_version'; import { isOutdated } from '../../migrations/helpers'; -import { RuleDataPluginService } from '../../../../../../rule_registry/server'; -import { ConfigType } from '../../../../config'; -import { parseExperimentalConfigValue } from '../../../../../common/experimental_features'; -export const createIndexRoute = ( - router: SecuritySolutionPluginRouter, - ruleDataService: RuleDataPluginService, - config: ConfigType -) => { +export const createIndexRoute = (router: SecuritySolutionPluginRouter) => { router.post( { path: DETECTION_ENGINE_INDEX_URL, @@ -51,14 +43,13 @@ export const createIndexRoute = ( }, async (context, request, response) => { const siemResponse = buildSiemResponse(response); - const { ruleRegistryEnabled } = parseExperimentalConfigValue(config.enableExperimental); try { const siemClient = context.securitySolution?.getAppClient(); if (!siemClient) { return siemResponse.error({ statusCode: 404 }); } - await createDetectionIndex(context, siemClient, ruleDataService, ruleRegistryEnabled); + await createDetectionIndex(context.securitySolution); return response.ok({ body: { acknowledged: true } }); } catch (err) { const error = transformError(err); @@ -71,30 +62,18 @@ export const createIndexRoute = ( ); }; -class CreateIndexError extends Error { - public readonly statusCode: number; - constructor(message: string, statusCode: number) { - super(message); - this.statusCode = statusCode; - } -} - export const createDetectionIndex = async ( - context: SecuritySolutionRequestHandlerContext, - siemClient: AppClient, - ruleDataService: RuleDataPluginService, - ruleRegistryEnabled: boolean + context: SecuritySolutionApiRequestHandlerContext ): Promise => { + const config = context.getConfig(); const esClient = context.core.elasticsearch.client.asCurrentUser; - const spaceId = siemClient.getSpaceId(); - - if (!siemClient) { - throw new CreateIndexError('', 404); - } - + const siemClient = context.getAppClient(); + const spaceId = context.getSpaceId(); const index = siemClient.getSignalsIndex(); const indexExists = await getIndexExists(esClient, index); + const { ruleRegistryEnabled } = config.experimentalFeatures; + // If using the rule registry implementation, we don't want to create new .siem-signals indices - // only create/update resources if there are existing indices if (ruleRegistryEnabled && !indexExists) { @@ -106,7 +85,10 @@ export const createDetectionIndex = async ( if (!policyExists) { await setPolicy(esClient, index, signalsPolicy); } + + const ruleDataService = context.getRuleDataService(); const aadIndexAliasName = ruleDataService.getResourceName(`security.alerts-${spaceId}`); + if (await templateNeedsUpdate({ alias: index, esClient })) { await esClient.indices.putIndexTemplate({ name: index, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/privileges/read_privileges_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/privileges/read_privileges_route.test.ts index 7ffa45e2bf7ee..2c2c65f5f78f7 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/privileges/read_privileges_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/privileges/read_privileges_route.test.ts @@ -19,9 +19,9 @@ describe('read_privileges route', () => { server = serverMock.create(); ({ context } = requestContextMock.createTools()); - context.core.elasticsearch.client.asCurrentUser.security.hasPrivileges.mockResolvedValue({ - body: getMockPrivilegesResult(), - }); + context.core.elasticsearch.client.asCurrentUser.security.hasPrivileges.mockResolvedValue( + elasticsearchClientMock.createSuccessTransportRequestPromise(getMockPrivilegesResult()) + ); readPrivilegesRoute(server.router, true); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.test.ts index 26e09d69d3a45..29ceb74e9ba0c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.test.ts @@ -10,14 +10,13 @@ import { addPrepackagedRulesRequest, getFindResultWithSingleHit, getAlertMock, + getBasicEmptySearchResponse, + getBasicNoShardsSearchResponse, } from '../__mocks__/request_responses'; -import { requestContextMock, serverMock, createMockConfig, mockGetCurrentUser } from '../__mocks__'; +import { configMock, requestContextMock, serverMock } from '../__mocks__'; import { AddPrepackagedRulesSchemaDecoded } from '../../../../../common/detection_engine/schemas/request/add_prepackaged_rules_schema'; -import { SecurityPluginSetup } from '../../../../../../security/server'; import { addPrepackedRulesRoute, createPrepackagedRules } from './add_prepackaged_rules_route'; import { listMock } from '../../../../../../lists/server/mocks'; -import { siemMock } from '../../../../mocks'; -import { FrameworkRequest } from '../../../framework'; import { ExceptionListClient } from '../../../../../../lists/server'; import { installPrepackagedTimelines } from '../../../timeline/routes/prepackaged_timelines/install_prepackaged_timelines'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths @@ -76,25 +75,21 @@ describe.each([ ['Legacy', false], ['RAC', true], ])('add_prepackaged_rules_route - %s', (_, isRuleRegistryEnabled) => { - const siemMockClient = siemMock.createClient(); let server: ReturnType; let { clients, context } = requestContextMock.createTools(); - let securitySetup: SecurityPluginSetup; let mockExceptionsClient: ExceptionListClient; const testif = isRuleRegistryEnabled ? test.skip : test; + const defaultConfig = context.securitySolution.getConfig(); beforeEach(() => { server = serverMock.create(); ({ clients, context } = requestContextMock.createTools()); - securitySetup = { - authc: { - getCurrentUser: jest.fn().mockReturnValue(mockGetCurrentUser), - }, - authz: {}, - } as unknown as SecurityPluginSetup; - mockExceptionsClient = listMock.getExceptionListClient(); + context.securitySolution.getConfig.mockImplementation(() => + configMock.withRuleRegistryEnabled(defaultConfig, isRuleRegistryEnabled) + ); + clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled)); clients.rulesClient.update.mockResolvedValue( getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()) @@ -110,9 +105,9 @@ describe.each([ }); context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValue( - elasticsearchClientMock.createSuccessTransportRequestPromise({ _shards: { total: 1 } }) + elasticsearchClientMock.createSuccessTransportRequestPromise(getBasicEmptySearchResponse()) ); - addPrepackedRulesRoute(server.router, createMockConfig(), securitySetup, isRuleRegistryEnabled); + addPrepackedRulesRoute(server.router); }); describe('status codes', () => { @@ -138,7 +133,9 @@ describe.each([ test('it returns a 400 if the index does not exist when rule registry not enabled', async () => { const request = addPrepackagedRulesRequest(); context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise({ _shards: { total: 0 } }) + elasticsearchClientMock.createSuccessTransportRequestPromise( + getBasicNoShardsSearchResponse() + ) ); const response = await server.inject(request, context); @@ -302,39 +299,22 @@ describe.each([ describe('createPrepackagedRules', () => { test('uses exception lists client from context when available', async () => { - context.lists = { - getExceptionListClient: jest.fn(), - getListClient: jest.fn(), - }; - const config = createMockConfig(); - await createPrepackagedRules( - context, - siemMockClient, + context.securitySolution, clients.rulesClient, - {} as FrameworkRequest, - 1200, - config.prebuiltRulesFromFileSystem, - config.prebuiltRulesFromSavedObjects, mockExceptionsClient ); expect(mockExceptionsClient.createEndpointList).not.toHaveBeenCalled(); - expect(context.lists?.getExceptionListClient).toHaveBeenCalled(); + expect(context.securitySolution.getExceptionListClient).toHaveBeenCalled(); }); test('uses passed in exceptions list client when lists client not available in context', async () => { - const { lists, ...myContext } = context; - const config = createMockConfig(); + context.securitySolution.getExceptionListClient.mockImplementation(() => null); await createPrepackagedRules( - myContext, - siemMockClient, + context.securitySolution, clients.rulesClient, - {} as FrameworkRequest, - 1200, - config.prebuiltRulesFromFileSystem, - config.prebuiltRulesFromSavedObjects, mockExceptionsClient ); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts index ddf4e956beac4..50766af669ce7 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts @@ -9,9 +9,8 @@ import moment from 'moment'; import { transformError, getIndexExists } from '@kbn/securitysolution-es-utils'; import { validate } from '@kbn/securitysolution-io-ts-utils'; import type { - AppClient, + SecuritySolutionApiRequestHandlerContext, SecuritySolutionPluginRouter, - SecuritySolutionRequestHandlerContext, } from '../../../../types'; import { @@ -21,10 +20,6 @@ import { import { importTimelineResultSchema } from '../../../../../common/types/timeline'; import { DETECTION_ENGINE_PREPACKAGED_URL } from '../../../../../common/constants'; -import { ConfigType } from '../../../../config'; -import { SetupPlugins } from '../../../../plugin'; -import { buildFrameworkRequest } from '../../../timeline/utils/common'; - import { getLatestPrepackagedRules } from '../../rules/get_prepackaged_rules'; import { installPrepackagedRules } from '../../rules/install_prepacked_rules'; import { updatePrepackagedRules } from '../../rules/update_prepacked_rules'; @@ -35,17 +30,11 @@ import { ruleAssetSavedObjectsClientFactory } from '../../rules/rule_asset/rule_ import { buildSiemResponse } from '../utils'; import { RulesClient } from '../../../../../../alerting/server'; -import { FrameworkRequest } from '../../../framework'; import { ExceptionListClient } from '../../../../../../lists/server'; import { installPrepackagedTimelines } from '../../../timeline/routes/prepackaged_timelines/install_prepackaged_timelines'; -export const addPrepackedRulesRoute = ( - router: SecuritySolutionPluginRouter, - config: ConfigType, - security: SetupPlugins['security'], - isRuleRegistryEnabled: boolean -) => { +export const addPrepackedRulesRoute = (router: SecuritySolutionPluginRouter) => { router.put( { path: DETECTION_ENGINE_PREPACKAGED_URL, @@ -63,7 +52,6 @@ export const addPrepackedRulesRoute = ( }, async (context, _, response) => { const siemResponse = buildSiemResponse(response); - const frameworkRequest = await buildFrameworkRequest(context, security, _); try { const rulesClient = context.alerting?.getRulesClient(); @@ -74,15 +62,9 @@ export const addPrepackedRulesRoute = ( } const validated = await createPrepackagedRules( - context, - siemClient, + context.securitySolution, rulesClient, - frameworkRequest, - config.maxTimelineImportExportSize, - config.prebuiltRulesFromFileSystem, - config.prebuiltRulesFromSavedObjects, - undefined, - isRuleRegistryEnabled + undefined ); return response.ok({ body: validated ?? {} }); } catch (err) { @@ -105,22 +87,26 @@ class PrepackagedRulesError extends Error { } export const createPrepackagedRules = async ( - context: SecuritySolutionRequestHandlerContext, - siemClient: AppClient, + context: SecuritySolutionApiRequestHandlerContext, rulesClient: RulesClient, - frameworkRequest: FrameworkRequest, - maxTimelineImportExportSize: ConfigType['maxTimelineImportExportSize'], - prebuiltRulesFromFileSystem: ConfigType['prebuiltRulesFromFileSystem'], - prebuiltRulesFromSavedObjects: ConfigType['prebuiltRulesFromSavedObjects'], - exceptionsClient?: ExceptionListClient, - isRuleRegistryEnabled?: boolean | undefined + exceptionsClient?: ExceptionListClient ): Promise => { + const config = context.getConfig(); + const frameworkRequest = context.getFrameworkRequest(); const esClient = context.core.elasticsearch.client; const savedObjectsClient = context.core.savedObjects.client; - const exceptionsListClient = - context.lists != null ? context.lists.getExceptionListClient() : exceptionsClient; + const siemClient = context.getAppClient(); + const exceptionsListClient = context.getExceptionListClient() ?? exceptionsClient; const ruleAssetsClient = ruleAssetSavedObjectsClientFactory(savedObjectsClient); - const ruleStatusClient = context.securitySolution.getExecutionLogClient(); + const ruleStatusClient = context.getExecutionLogClient(); + + const { + maxTimelineImportExportSize, + prebuiltRulesFromFileSystem, + prebuiltRulesFromSavedObjects, + experimentalFeatures: { ruleRegistryEnabled }, + } = config; + if (!siemClient || !rulesClient) { throw new PrepackagedRulesError('', 404); } @@ -137,12 +123,12 @@ export const createPrepackagedRules = async ( ); const prepackagedRules = await getExistingPrepackagedRules({ rulesClient, - isRuleRegistryEnabled: isRuleRegistryEnabled ?? false, + isRuleRegistryEnabled: ruleRegistryEnabled, }); const rulesToInstall = getRulesToInstall(latestPrepackagedRules, prepackagedRules); const rulesToUpdate = getRulesToUpdate(latestPrepackagedRules, prepackagedRules); const signalsIndex = siemClient.getSignalsIndex(); - if (!isRuleRegistryEnabled && (rulesToInstall.length !== 0 || rulesToUpdate.length !== 0)) { + if (!ruleRegistryEnabled && (rulesToInstall.length !== 0 || rulesToUpdate.length !== 0)) { const signalsIndexExists = await getIndexExists(esClient.asCurrentUser, signalsIndex); if (!signalsIndexExists) { throw new PrepackagedRulesError( @@ -153,12 +139,7 @@ export const createPrepackagedRules = async ( } await Promise.all( - installPrepackagedRules( - rulesClient, - rulesToInstall, - signalsIndex, - isRuleRegistryEnabled ?? false - ) + installPrepackagedRules(rulesClient, rulesToInstall, signalsIndex, ruleRegistryEnabled) ); const timeline = await installPrepackagedTimelines( maxTimelineImportExportSize, @@ -172,11 +153,11 @@ export const createPrepackagedRules = async ( await updatePrepackagedRules( rulesClient, savedObjectsClient, - context.securitySolution.getSpaceId(), + context.getSpaceId(), ruleStatusClient, rulesToUpdate, signalsIndex, - isRuleRegistryEnabled ?? false + ruleRegistryEnabled ); const prepackagedRulesOutput: PrePackagedRulesAndTimelinesSchema = { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.test.ts index 6f721bb2bb9c5..6dc303d5a266b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.test.ts @@ -14,6 +14,8 @@ import { getEmptyFindResult, getAlertMock, createBulkMlRuleRequest, + getBasicEmptySearchResponse, + getBasicNoShardsSearchResponse, } from '../__mocks__/request_responses'; import { requestContextMock, serverMock, requestMock } from '../__mocks__'; import { createRulesBulkRoute } from './create_rules_bulk_route'; @@ -43,7 +45,7 @@ describe.each([ ); // successful creation context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValue( - elasticsearchClientMock.createSuccessTransportRequestPromise({ _shards: { total: 1 } }) + elasticsearchClientMock.createSuccessTransportRequestPromise(getBasicEmptySearchResponse()) ); createRulesBulkRoute(server.router, ml, isRuleRegistryEnabled); }); @@ -93,7 +95,9 @@ describe.each([ test('returns an error object if the index does not exist when rule registry not enabled', async () => { context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise({ _shards: { total: 0 } }) + elasticsearchClientMock.createSuccessTransportRequestPromise( + getBasicNoShardsSearchResponse() + ) ); const response = await server.inject(getReadBulkRequest(), context); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.test.ts index 59fe5c0ff68a1..010c4b27507bb 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.test.ts @@ -13,6 +13,8 @@ import { getRuleExecutionStatuses, getFindResultWithSingleHit, createMlRuleRequest, + getBasicEmptySearchResponse, + getBasicNoShardsSearchResponse, } from '../__mocks__/request_responses'; import { mlServicesMock, mlAuthzMock as mockMlAuthzFactory } from '../../../machine_learning/mocks'; import { buildMlAuthz } from '../../../machine_learning/authz'; @@ -44,7 +46,7 @@ describe.each([ clients.ruleExecutionLogClient.find.mockResolvedValue(getRuleExecutionStatuses()); // needed to transform: ; context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValue( - elasticsearchClientMock.createSuccessTransportRequestPromise({ _shards: { total: 1 } }) + elasticsearchClientMock.createSuccessTransportRequestPromise(getBasicEmptySearchResponse()) ); createRulesRoute(server.router, ml, isRuleRegistryEnabled); }); @@ -103,7 +105,9 @@ describe.each([ describe('unhappy paths', () => { test('it returns a 400 if the index does not exist when rule registry not enabled', async () => { context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise({ _shards: { total: 0 } }) + elasticsearchClientMock.createSuccessTransportRequestPromise( + getBasicNoShardsSearchResponse() + ) ); const response = await server.inject(getCreateRequest(), context); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.test.ts index aa301bcc0335e..23779afdc5410 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.test.ts @@ -12,6 +12,8 @@ import { getEmptyFindResult, getAlertMock, getFindResultWithSingleHit, + getBasicEmptySearchResponse, + getBasicNoShardsSearchResponse, } from '../__mocks__/request_responses'; import { createMockConfig, requestContextMock, serverMock, requestMock } from '../__mocks__'; import { mlServicesMock, mlAuthzMock as mockMlAuthzFactory } from '../../../machine_learning/mocks'; @@ -52,7 +54,7 @@ describe.each([ getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()) ); context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValue( - elasticsearchClientMock.createSuccessTransportRequestPromise({ _shards: { total: 1 } }) + elasticsearchClientMock.createSuccessTransportRequestPromise(getBasicEmptySearchResponse()) ); importRulesRoute(server.router, config, ml, isRuleRegistryEnabled); }); @@ -133,7 +135,9 @@ describe.each([ test('returns an error if the index does not exist when rule registry not enabled', async () => { clients.appClient.getSignalsIndex.mockReturnValue('mockSignalsIndex'); context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise({ _shards: { total: 0 } }) + elasticsearchClientMock.createSuccessTransportRequestPromise( + getBasicNoShardsSearchResponse() + ) ); const response = await server.inject(request, context); expect(response.status).toEqual(isRuleRegistryEnabled ? 200 : 400); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals.test.ts index f6abfc9ebe3d1..07c3bc37e7d72 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals.test.ts @@ -26,11 +26,13 @@ describe('set signal status', () => { beforeEach(() => { server = serverMock.create(); ({ context } = requestContextMock.createTools()); - context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValue( + + context.core.elasticsearch.client.asCurrentUser.updateByQuery.mockResolvedValue( elasticsearchClientMock.createSuccessTransportRequestPromise( getSuccessfulSignalUpdateResponse() ) ); + setSignalsStatusRoute(server.router); }); diff --git a/x-pack/plugins/security_solution/server/lib/framework/types.ts b/x-pack/plugins/security_solution/server/lib/framework/types.ts index eceff4b35f74f..3ecd2adf00242 100644 --- a/x-pack/plugins/security_solution/server/lib/framework/types.ts +++ b/x-pack/plugins/security_solution/server/lib/framework/types.ts @@ -5,14 +5,13 @@ * 2.0. */ -import { KibanaRequest } from '../../../../../../src/core/server'; +import { KibanaRequest, RequestHandlerContext } from '../../../../../../src/core/server'; import { AuthenticatedUser } from '../../../../security/common/model'; -import type { SecuritySolutionRequestHandlerContext } from '../../types'; export const internalFrameworkRequest = Symbol('internalFrameworkRequest'); export interface FrameworkRequest extends Pick { [internalFrameworkRequest]: KibanaRequest; - context: SecuritySolutionRequestHandlerContext; + context: RequestHandlerContext; user: AuthenticatedUser | null; } diff --git a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/create_timelines/helpers.test.ts b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/create_timelines/helpers.test.ts index 05f3b5373a8de..8bf5213d6a47f 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/create_timelines/helpers.test.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/routes/timelines/create_timelines/helpers.test.ts @@ -32,17 +32,12 @@ const notes = [ const existingNoteIds = undefined; const isImmutable = true; -jest.mock('moment', () => { - const mockMoment = { - toISOString: jest - .fn() - .mockReturnValueOnce('2020-11-03T11:37:31.655Z') - .mockReturnValue('2020-11-04T11:37:31.655Z'), - subtract: jest.fn(), - }; - mockMoment.subtract.mockReturnValue(mockMoment); - return jest.fn().mockReturnValue(mockMoment); -}); +// System under test uses moment.js under the hood, so we need to mock time. +// Mocking moment via jest.mock('moment') breaks imports of moment in other files. +// Instead, we simply mock Date.now() via jest API and moment starts using it. +// This affects all the tests in this file and doesn't affect tests in other files. +// https://jestjs.io/docs/timer-mocks +jest.useFakeTimers('modern').setSystemTime(new Date('2020-11-04T11:37:31.655Z')); jest.mock('../../../saved_object/timelines', () => ({ persistTimeline: jest.fn().mockResolvedValue({ diff --git a/x-pack/plugins/security_solution/server/lib/timeline/utils/common.ts b/x-pack/plugins/security_solution/server/lib/timeline/utils/common.ts index be086732ddcd0..91f8e4153a63b 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/utils/common.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/utils/common.ts @@ -12,15 +12,14 @@ import { Readable } from 'stream'; import { createListStream } from '@kbn/utils'; import { schema } from '@kbn/config-schema'; -import { KibanaRequest } from 'src/core/server'; +import { KibanaRequest, RequestHandlerContext } from 'src/core/server'; import { formatErrors } from '@kbn/securitysolution-io-ts-utils'; import { SetupPlugins, StartPlugins } from '../../../plugin'; -import type { SecuritySolutionRequestHandlerContext } from '../../../types'; import { FrameworkRequest } from '../../framework'; export const buildFrameworkRequest = async ( - context: SecuritySolutionRequestHandlerContext, + context: RequestHandlerContext, security: StartPlugins['security'] | SetupPlugins['security'] | undefined, request: KibanaRequest ): Promise => { @@ -30,7 +29,7 @@ export const buildFrameworkRequest = async ( return set( 'user', user, - set( + set( 'context.core.savedObjects.client', savedObjectsClient, request diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index 0f908d7db8e05..f2aa4927a7688 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -9,45 +9,15 @@ import { Observable } from 'rxjs'; import LRU from 'lru-cache'; import { estypes } from '@elastic/elasticsearch'; -import { - CoreSetup, - CoreStart, - Logger, - Plugin as IPlugin, - PluginInitializerContext, - SavedObjectsClient, -} from '../../../../src/core/server'; -import { - PluginSetup as DataPluginSetup, - PluginStart as DataPluginStart, -} from '../../../../src/plugins/data/server'; -import { - UsageCollectionSetup, - UsageCounter, -} from '../../../../src/plugins/usage_collection/server'; -import { - PluginSetupContract as AlertingSetup, - PluginStartContract as AlertPluginStartContract, -} from '../../alerting/server'; -import { mappingFromFieldMap } from '../../rule_registry/common/mapping_from_field_map'; +import { Logger, SavedObjectsClient } from '../../../../src/core/server'; +import { UsageCounter } from '../../../../src/plugins/usage_collection/server'; -import { PluginStartContract as CasesPluginStartContract } from '../../cases/server'; import { ECS_COMPONENT_TEMPLATE_NAME } from '../../rule_registry/common/assets'; -import { SecurityPluginSetup as SecuritySetup, SecurityPluginStart } from '../../security/server'; -import { - IRuleDataClient, - RuleRegistryPluginSetupContract, - RuleRegistryPluginStartContract, - Dataset, -} from '../../rule_registry/server'; -import { PluginSetupContract as FeaturesSetup } from '../../features/server'; -import { MlPluginSetup as MlSetup } from '../../ml/server'; +import { mappingFromFieldMap } from '../../rule_registry/common/mapping_from_field_map'; +import { IRuleDataClient, Dataset } from '../../rule_registry/server'; import { ListPluginSetup } from '../../lists/server'; -import { EncryptedSavedObjectsPluginSetup as EncryptedSavedObjectsSetup } from '../../encrypted_saved_objects/server'; -import { SpacesPluginSetup as SpacesSetup } from '../../spaces/server'; -import { ILicense, LicensingPluginStart } from '../../licensing/server'; -import { FleetStartContract } from '../../fleet/server'; -import { TaskManagerSetupContract, TaskManagerStartContract } from '../../task_manager/server'; +import { ILicense } from '../../licensing/server'; + import { createEqlAlertType, createIndicatorMatchAlertType, @@ -70,7 +40,6 @@ import { SIGNALS_ID, LEGACY_NOTIFICATIONS_ID, QUERY_RULE_TYPE_ID, - DEFAULT_SPACE_ID, INDICATOR_RULE_TYPE_ID, ML_RULE_TYPE_ID, EQL_RULE_TYPE_ID, @@ -89,18 +58,13 @@ import { registerTrustedAppsRoutes } from './endpoint/routes/trusted_apps'; import { securitySolutionSearchStrategyProvider } from './search_strategy/security_solution'; import { TelemetryEventsSender } from './lib/telemetry/sender'; import { TelemetryReceiver } from './lib/telemetry/receiver'; -import { - TelemetryPluginStart, - TelemetryPluginSetup, -} from '../../../../src/plugins/telemetry/server'; import { licenseService } from './lib/license'; import { PolicyWatcher } from './endpoint/lib/policy/license_watch'; -import { parseExperimentalConfigValue } from '../common/experimental_features'; import { migrateArtifactsToFleet } from './endpoint/lib/artifacts/migrate_artifacts_to_fleet'; import aadFieldConversion from './lib/detection_engine/routes/index/signal_aad_mapping.json'; import { alertsFieldMap } from './lib/detection_engine/rule_types/field_maps/alerts'; import { rulesFieldMap } from './lib/detection_engine/rule_types/field_maps/rules'; -import { RuleExecutionLogClient } from './lib/detection_engine/rule_execution_log/rule_execution_log_client'; +import { registerEventLogProvider } from './lib/detection_engine/rule_execution_log/event_log_adapter/register_event_log_provider'; import { getKibanaPrivilegesFeaturePrivileges, getCasesKibanaFeature } from './features'; import { EndpointMetadataService } from './endpoint/services/metadata'; import { CreateRuleOptions } from './lib/detection_engine/rule_types/types'; @@ -109,50 +73,28 @@ import { legacyRulesNotificationAlertType } from './lib/detection_engine/notific // eslint-disable-next-line no-restricted-imports import { legacyIsNotificationAlertExecutor } from './lib/detection_engine/notifications/legacy_types'; import { createSecurityRuleTypeWrapper } from './lib/detection_engine/rule_types/create_security_rule_type_wrapper'; -import { IEventLogClientService, IEventLogService } from '../../event_log/server'; -import { registerEventLogProvider } from './lib/detection_engine/rule_execution_log/event_log_adapter/register_event_log_provider'; -export interface SetupPlugins { - alerting: AlertingSetup; - data: DataPluginSetup; - encryptedSavedObjects?: EncryptedSavedObjectsSetup; - eventLog: IEventLogService; - features: FeaturesSetup; - lists?: ListPluginSetup; - ml?: MlSetup; - ruleRegistry: RuleRegistryPluginSetupContract; - security?: SecuritySetup; - spaces?: SpacesSetup; - taskManager?: TaskManagerSetupContract; - telemetry?: TelemetryPluginSetup; - usageCollection?: UsageCollectionSetup; -} - -export interface StartPlugins { - alerting: AlertPluginStartContract; - cases?: CasesPluginStartContract; - data: DataPluginStart; - eventLog: IEventLogClientService; - fleet?: FleetStartContract; - licensing: LicensingPluginStart; - ruleRegistry: RuleRegistryPluginStartContract; - security: SecurityPluginStart; - taskManager?: TaskManagerStartContract; - telemetry?: TelemetryPluginStart; -} +import { RequestContextFactory } from './request_context_factory'; -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface PluginSetup {} +import type { + ISecuritySolutionPlugin, + SecuritySolutionPluginSetupDependencies, + SecuritySolutionPluginStartDependencies, + SecuritySolutionPluginCoreSetupDependencies, + SecuritySolutionPluginCoreStartDependencies, + SecuritySolutionPluginSetup, + SecuritySolutionPluginStart, + PluginInitializerContext, +} from './plugin_contract'; -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface PluginStart {} +export { SetupPlugins, StartPlugins, PluginSetup, PluginStart } from './plugin_contract'; -export class Plugin implements IPlugin { - private readonly logger: Logger; +export class Plugin implements ISecuritySolutionPlugin { + private readonly pluginContext: PluginInitializerContext; private readonly config: ConfigType; - private context: PluginInitializerContext; - private appClientFactory: AppClientFactory; - private setupPlugins?: SetupPlugins; + private readonly logger: Logger; + private readonly appClientFactory: AppClientFactory; + private readonly endpointAppContextService = new EndpointAppContextService(); private readonly telemetryReceiver: TelemetryReceiver; private readonly telemetryEventsSender: TelemetryEventsSender; @@ -167,10 +109,11 @@ export class Plugin implements IPlugin({ max: 3, maxAge: 1000 * 60 * 5 }); this.telemetryEventsSender = new TelemetryEventsSender(this.logger); @@ -179,26 +122,47 @@ export class Plugin implements IPlugin, plugins: SetupPlugins) { + public setup( + core: SecuritySolutionPluginCoreSetupDependencies, + plugins: SecuritySolutionPluginSetupDependencies + ): SecuritySolutionPluginSetup { this.logger.debug('plugin setup'); - this.setupPlugins = plugins; - const config = this.config; - const globalConfig = this.context.config.legacy.get(); + const { pluginContext, config, logger, appClientFactory } = this; + const experimentalFeatures = config.experimentalFeatures; + + appClientFactory.setup({ + getSpaceId: plugins.spaces?.spacesService?.getSpaceId, + config, + }); - const experimentalFeatures = parseExperimentalConfigValue(config.enableExperimental); initSavedObjects(core.savedObjects); initUiSettings(core.uiSettings, experimentalFeatures); + + const eventLogService = plugins.eventLog; + registerEventLogProvider(eventLogService); + + const requestContextFactory = new RequestContextFactory({ config, core, plugins }); + const router = core.http.createRouter(); + core.http.registerRouteHandlerContext( + APP_ID, + (context, request) => requestContextFactory.create(context, request) + ); + const endpointContext: EndpointAppContext = { - logFactory: this.context.logger, + logFactory: pluginContext.logger, service: this.endpointAppContextService, config: (): Promise => Promise.resolve(config), experimentalFeatures, }; + this.endpointAppContextService.setup({ + securitySolutionRequestContextFactory: requestContextFactory, + }); + initUsageCollectors({ core, - kibanaIndex: globalConfig.kibana.index, + kibanaIndex: config.kibanaIndex, signalsIndex: config.signalsIndex, ml: plugins.ml, usageCollection: plugins.usageCollection, @@ -206,29 +170,6 @@ export class Plugin implements IPlugin(); - core.http.registerRouteHandlerContext( - APP_ID, - (context, request, response) => ({ - getAppClient: () => this.appClientFactory.create(request), - getSpaceId: () => plugins.spaces?.spacesService?.getSpaceId(request) || DEFAULT_SPACE_ID, - getExecutionLogClient: () => - new RuleExecutionLogClient({ - savedObjectsClient: context.core.savedObjects.client, - eventLogService, - underlyingClient: config.ruleExecutionLog.underlyingClient, - }), - }) - ); - - this.appClientFactory.setup({ - getSpaceId: plugins.spaces?.spacesService?.getSpaceId, - config, - }); - // TODO: Once we are past experimental phase this check can be removed along with legacy registration of rules const isRuleRegistryEnabled = experimentalFeatures.ruleRegistryEnabled; @@ -265,9 +206,9 @@ export class Plugin implements IPlugin; + +export type SecuritySolutionPluginCoreStartDependencies = CoreStart; + +export type ISecuritySolutionPlugin = Plugin< + SecuritySolutionPluginSetup, + SecuritySolutionPluginStart, + SecuritySolutionPluginSetupDependencies, + SecuritySolutionPluginStartDependencies +>; + +export type { + PluginInitializerContext, + // Legacy type identifiers left for compatibility with the rest of the code: + SecuritySolutionPluginSetupDependencies as SetupPlugins, + SecuritySolutionPluginStartDependencies as StartPlugins, + SecuritySolutionPluginSetup as PluginSetup, + SecuritySolutionPluginStart as PluginStart, +}; diff --git a/x-pack/plugins/security_solution/server/request_context_factory.mock.ts b/x-pack/plugins/security_solution/server/request_context_factory.mock.ts new file mode 100644 index 0000000000000..5621ac8fb26ab --- /dev/null +++ b/x-pack/plugins/security_solution/server/request_context_factory.mock.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { requestContextMock } from './lib/detection_engine/routes/__mocks__'; +import { IRequestContextFactory } from './request_context_factory'; + +export const requestContextFactoryMock = { + create: (): jest.Mocked => ({ + create: jest.fn((context, request) => { + const fullContext = requestContextMock.create(); + const securitySolutionContext = fullContext.securitySolution; + return Promise.resolve(securitySolutionContext); + }), + }), +}; + +export const RequestContextFactoryMock = jest + .fn, []>() + .mockImplementation(requestContextFactoryMock.create); diff --git a/x-pack/plugins/security_solution/server/request_context_factory.ts b/x-pack/plugins/security_solution/server/request_context_factory.ts new file mode 100644 index 0000000000000..c2e622bc495c9 --- /dev/null +++ b/x-pack/plugins/security_solution/server/request_context_factory.ts @@ -0,0 +1,90 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { KibanaRequest, RequestHandlerContext } from 'kibana/server'; +import { ExceptionListClient } from '../../lists/server'; + +import { DEFAULT_SPACE_ID } from '../common/constants'; +import { AppClientFactory } from './client'; +import { ConfigType } from './config'; +import { RuleExecutionLogClient } from './lib/detection_engine/rule_execution_log/rule_execution_log_client'; +import { buildFrameworkRequest } from './lib/timeline/utils/common'; +import { + SecuritySolutionPluginCoreSetupDependencies, + SecuritySolutionPluginSetupDependencies, +} from './plugin_contract'; +import { SecuritySolutionApiRequestHandlerContext } from './types'; + +export interface IRequestContextFactory { + create( + context: RequestHandlerContext, + request: KibanaRequest + ): Promise; +} + +interface ConstructorOptions { + config: ConfigType; + core: SecuritySolutionPluginCoreSetupDependencies; + plugins: SecuritySolutionPluginSetupDependencies; +} + +export class RequestContextFactory implements IRequestContextFactory { + private readonly appClientFactory: AppClientFactory; + + constructor(private readonly options: ConstructorOptions) { + this.appClientFactory = new AppClientFactory(); + } + + public async create( + context: RequestHandlerContext, + request: KibanaRequest + ): Promise { + const { options, appClientFactory } = this; + const { config, plugins } = options; + const { lists, ruleRegistry, security, spaces } = plugins; + + appClientFactory.setup({ + getSpaceId: plugins.spaces?.spacesService?.getSpaceId, + config, + }); + + const frameworkRequest = await buildFrameworkRequest(context, security, request); + + return { + core: context.core, + + getConfig: () => config, + + getFrameworkRequest: () => frameworkRequest, + + getAppClient: () => appClientFactory.create(request), + + getSpaceId: () => spaces?.spacesService?.getSpaceId(request) || DEFAULT_SPACE_ID, + + getRuleDataService: () => ruleRegistry.ruleDataService, + + getExecutionLogClient: () => + new RuleExecutionLogClient({ + savedObjectsClient: context.core.savedObjects.client, + eventLogService: plugins.eventLog, + underlyingClient: config.ruleExecutionLog.underlyingClient, + }), + + getExceptionListClient: () => { + if (!lists) { + return null; + } + + const username = security?.authc.getCurrentUser(request)?.username || 'elastic'; + return new ExceptionListClient({ + savedObjectsClient: context.core.savedObjects.client, + user: username, + }); + }, + }; + } +} diff --git a/x-pack/plugins/security_solution/server/routes/index.ts b/x-pack/plugins/security_solution/server/routes/index.ts index 148580d5c4477..9d31684907f86 100644 --- a/x-pack/plugins/security_solution/server/routes/index.ts +++ b/x-pack/plugins/security_solution/server/routes/index.ts @@ -6,7 +6,6 @@ */ import { Logger } from 'src/core/server'; -import { RuleDataPluginService } from '../../../rule_registry/server'; import { SecuritySolutionPluginRouter } from '../types'; @@ -67,7 +66,6 @@ export const initRoutes = ( hasEncryptionKey: boolean, security: SetupPlugins['security'], ml: SetupPlugins['ml'], - ruleDataService: RuleDataPluginService, logger: Logger, isRuleRegistryEnabled: boolean ) => { @@ -85,7 +83,7 @@ export const initRoutes = ( // TODO: pass isRuleRegistryEnabled to all relevant routes - addPrepackedRulesRoute(router, config, security, isRuleRegistryEnabled); + addPrepackedRulesRoute(router); getPrepackagedRulesStatusRoute(router, config, security, isRuleRegistryEnabled); createRulesBulkRoute(router, ml, isRuleRegistryEnabled); updateRulesBulkRoute(router, ml, isRuleRegistryEnabled); @@ -127,7 +125,7 @@ export const initRoutes = ( // Detection Engine index routes that have the REST endpoints of /api/detection_engine/index // All REST index creation, policy management for spaces - createIndexRoute(router, ruleDataService, config); + createIndexRoute(router); readIndexRoute(router, config); deleteIndexRoute(router); diff --git a/x-pack/plugins/security_solution/server/types.ts b/x-pack/plugins/security_solution/server/types.ts index 7822a5b8ba3c5..84643a329573b 100644 --- a/x-pack/plugins/security_solution/server/types.ts +++ b/x-pack/plugins/security_solution/server/types.ts @@ -6,28 +6,35 @@ */ import type { IRouter, RequestHandlerContext } from 'src/core/server'; -import type { ListsApiRequestHandlerContext } from '../../lists/server'; -import type { LicensingApiRequestHandlerContext } from '../../licensing/server'; +import type { ActionsApiRequestHandlerContext } from '../../actions/server'; import type { AlertingApiRequestHandlerContext } from '../../alerting/server'; +import type { LicensingApiRequestHandlerContext } from '../../licensing/server'; +import type { ListsApiRequestHandlerContext, ExceptionListClient } from '../../lists/server'; +import type { IRuleDataService } from '../../rule_registry/server'; import { AppClient } from './client'; -import { RuleExecutionLogClient } from './lib/detection_engine/rule_execution_log/rule_execution_log_client'; -import type { ActionsApiRequestHandlerContext } from '../../actions/server'; +import { ConfigType } from './config'; +import { IRuleExecutionLogClient } from './lib/detection_engine/rule_execution_log/types'; +import { FrameworkRequest } from './lib/framework'; export { AppClient }; -export interface AppRequestContext { +export interface SecuritySolutionApiRequestHandlerContext extends RequestHandlerContext { + getConfig: () => ConfigType; + getFrameworkRequest: () => FrameworkRequest; getAppClient: () => AppClient; getSpaceId: () => string; - getExecutionLogClient: () => RuleExecutionLogClient; + getRuleDataService: () => IRuleDataService; + getExecutionLogClient: () => IRuleExecutionLogClient; + getExceptionListClient: () => ExceptionListClient | null; } -export type SecuritySolutionRequestHandlerContext = RequestHandlerContext & { - securitySolution: AppRequestContext; - licensing: LicensingApiRequestHandlerContext; - alerting: AlertingApiRequestHandlerContext; +export interface SecuritySolutionRequestHandlerContext extends RequestHandlerContext { + securitySolution: SecuritySolutionApiRequestHandlerContext; actions: ActionsApiRequestHandlerContext; + alerting: AlertingApiRequestHandlerContext; + licensing: LicensingApiRequestHandlerContext; lists?: ListsApiRequestHandlerContext; -}; +} export type SecuritySolutionPluginRouter = IRouter; From ba20ea16309b4277479dbe253bc548cefab0a63c Mon Sep 17 00:00:00 2001 From: Marshall Main <55718608+marshallmain@users.noreply.github.com> Date: Tue, 19 Oct 2021 22:40:26 -0400 Subject: [PATCH 103/204] [Security Solution] Improve rule statuses if user has no permissions to source index (#115114) * Prevent error in field_caps from silencing privilege errors * Fix threshold bug and fix privileges in new executor * Fix unit tests Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../create_security_rule_type_wrapper.ts | 93 +++++++-------- .../signals/signal_rule_alert_type.test.ts | 5 +- .../signals/signal_rule_alert_type.ts | 74 +++++------- .../bulk_create_threshold_signals.ts | 8 +- .../detection_engine/signals/utils.test.ts | 4 - .../lib/detection_engine/signals/utils.ts | 39 ++----- .../tests/check_privileges.ts | 107 ++++++++++++++++++ .../security_and_spaces/tests/index.ts | 1 + .../detection_engine_api_integration/utils.ts | 19 ++++ 9 files changed, 213 insertions(+), 137 deletions(-) create mode 100644 x-pack/test/detection_engine_api_integration/security_and_spaces/tests/check_privileges.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts index 0fe7cbdc9bd9f..c2e4b926d6375 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts @@ -6,13 +6,8 @@ */ import { isEmpty } from 'lodash'; -import { flow } from 'fp-ts/lib/function'; -import { Either, chain, fold, tryCatch } from 'fp-ts/lib/Either'; - -import { TIMESTAMP } from '@kbn/rule-data-utils'; import { parseScheduleDates } from '@kbn/securitysolution-io-ts-utils'; import { ListArray } from '@kbn/securitysolution-io-ts-list-types'; -import { toError } from '@kbn/securitysolution-list-api'; import { createPersistenceRuleTypeWrapper } from '../../../../../rule_registry/server'; import { buildRuleMessageFactory } from './factories/build_rule_message_factory'; @@ -136,58 +131,46 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = const inputIndices = params.index ?? []; - const [privileges, timestampFieldCaps] = await Promise.all([ - checkPrivilegesFromEsClient(esClient, inputIndices), - esClient.fieldCaps({ - index: index ?? ['*'], - fields: hasTimestampOverride - ? [TIMESTAMP, timestampOverride as string] - : [TIMESTAMP], - include_unmapped: true, - }), - ]); - - fold, void>( - async (error: Error) => logger.error(buildRuleMessage(error.message)), - async (status: Promise) => (wroteWarningStatus = await status) - )( - flow( - () => - tryCatch( - () => - hasReadIndexPrivileges({ - ...basicLogArguments, - privileges, - logger, - buildRuleMessage, - ruleStatusClient, - }), - toError - ), - chain((wroteStatus: unknown) => - tryCatch( - () => - hasTimestampFields({ - ...basicLogArguments, - wroteStatus: wroteStatus as boolean, - timestampField: hasTimestampOverride - ? (timestampOverride as string) - : '@timestamp', - ruleName: name, - timestampFieldCapsResponse: timestampFieldCaps, - inputIndices, - ruleStatusClient, - logger, - buildRuleMessage, - }), - toError - ) - ) - )() as Either> - ); + const privileges = await checkPrivilegesFromEsClient(esClient, inputIndices); + + wroteWarningStatus = await hasReadIndexPrivileges({ + ...basicLogArguments, + privileges, + logger, + buildRuleMessage, + ruleStatusClient, + }); + + if (!wroteWarningStatus) { + const timestampFieldCaps = await services.scopedClusterClient.asCurrentUser.fieldCaps( + { + index, + fields: hasTimestampOverride + ? ['@timestamp', timestampOverride as string] + : ['@timestamp'], + include_unmapped: true, + } + ); + wroteWarningStatus = await hasTimestampFields({ + ...basicLogArguments, + timestampField: hasTimestampOverride ? (timestampOverride as string) : '@timestamp', + timestampFieldCapsResponse: timestampFieldCaps, + inputIndices, + ruleStatusClient, + logger, + buildRuleMessage, + }); + } } } catch (exc) { - logger.error(buildRuleMessage(`Check privileges failed to execute ${exc}`)); + const errorMessage = buildRuleMessage(`Check privileges failed to execute ${exc}`); + logger.error(errorMessage); + await ruleStatusClient.logStatusChange({ + ...basicLogArguments, + message: errorMessage, + newStatus: RuleExecutionStatus['partial failure'], + }); + wroteWarningStatus = true; } let hasError = false; const { tuples, remainingGap } = getRuleRangeTuples({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts index 6a84776ccee5d..f9a2f5cfc0bfe 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts @@ -263,7 +263,8 @@ describe('signal_rule_alert_type', () => { 2, expect.objectContaining({ newStatus: RuleExecutionStatus['partial failure'], - message: 'Missing required read privileges on the following indices: ["some*"]', + message: + 'This rule may not have the required read privileges to the following indices/index patterns: ["some*"]', }) ); }); @@ -293,7 +294,7 @@ describe('signal_rule_alert_type', () => { expect.objectContaining({ newStatus: RuleExecutionStatus['partial failure'], message: - 'This rule may not have the required read privileges to the following indices: ["myfa*","some*"]', + 'This rule may not have the required read privileges to the following indices/index patterns: ["myfa*","some*"]', }) ); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts index 90220814fb928..0b1524a5682ab 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts @@ -8,12 +8,9 @@ import { Logger, SavedObject } from 'src/core/server'; import isEmpty from 'lodash/isEmpty'; -import { chain, tryCatch } from 'fp-ts/lib/TaskEither'; -import { flow } from 'fp-ts/lib/function'; import * as t from 'io-ts'; import { validateNonExact, parseScheduleDates } from '@kbn/securitysolution-io-ts-utils'; -import { toError, toPromise } from '@kbn/securitysolution-list-api'; import { SIGNALS_ID, @@ -191,53 +188,44 @@ export const signalRulesAlertType = ({ index, experimentalFeatures, }); - const [privileges, timestampFieldCaps] = await Promise.all([ - checkPrivileges(services, inputIndices), - services.scopedClusterClient.asCurrentUser.fieldCaps({ + const privileges = await checkPrivileges(services, inputIndices); + + wroteWarningStatus = await hasReadIndexPrivileges({ + ...basicLogArguments, + privileges, + logger, + buildRuleMessage, + ruleStatusClient, + }); + + if (!wroteWarningStatus) { + const timestampFieldCaps = await services.scopedClusterClient.asCurrentUser.fieldCaps({ index, fields: hasTimestampOverride ? ['@timestamp', timestampOverride as string] : ['@timestamp'], include_unmapped: true, - }), - ]); - - wroteWarningStatus = await flow( - () => - tryCatch( - () => - hasReadIndexPrivileges({ - ...basicLogArguments, - privileges, - logger, - buildRuleMessage, - ruleStatusClient, - }), - toError - ), - chain((wroteStatus) => - tryCatch( - () => - hasTimestampFields({ - ...basicLogArguments, - wroteStatus: wroteStatus as boolean, - timestampField: hasTimestampOverride - ? (timestampOverride as string) - : '@timestamp', - timestampFieldCapsResponse: timestampFieldCaps, - inputIndices, - ruleStatusClient, - logger, - buildRuleMessage, - }), - toError - ) - ), - toPromise - )(); + }); + wroteWarningStatus = await hasTimestampFields({ + ...basicLogArguments, + timestampField: hasTimestampOverride ? (timestampOverride as string) : '@timestamp', + timestampFieldCapsResponse: timestampFieldCaps, + inputIndices, + ruleStatusClient, + logger, + buildRuleMessage, + }); + } } } catch (exc) { - logger.error(buildRuleMessage(`Check privileges failed to execute ${exc}`)); + const errorMessage = buildRuleMessage(`Check privileges failed to execute ${exc}`); + logger.error(errorMessage); + await ruleStatusClient.logStatusChange({ + ...basicLogArguments, + message: errorMessage, + newStatus: RuleExecutionStatus['partial failure'], + }); + wroteWarningStatus = true; } const { tuples, remainingGap } = getRuleRangeTuples({ logger, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.ts index c202065176ff1..e2ef2cb6c841d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.ts @@ -66,8 +66,11 @@ const getTransformedHits = ( timestampOverride: TimestampOverrideOrUndefined, signalHistory: ThresholdSignalHistory ) => { + if (results.aggregations == null) { + return []; + } const aggParts = threshold.field.length - ? results.aggregations && getThresholdAggregationParts(results.aggregations) + ? getThresholdAggregationParts(results.aggregations) : { field: null, index: 0, @@ -132,8 +135,7 @@ const getTransformedHits = ( }; return getCombinations( - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - (results.aggregations![aggParts.name] as { buckets: TermAggregationBucket[] }).buckets, + (results.aggregations[aggParts.name] as { buckets: TermAggregationBucket[] }).buckets, 0, aggParts.field ).reduce((acc: Array>, bucket) => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts index 0e50db97d1256..ce2b15a46ef6f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts @@ -781,7 +781,6 @@ describe('utils', () => { }; mockLogger.error.mockClear(); const res = await hasTimestampFields({ - wroteStatus: false, timestampField, ruleName: 'myfakerulename', // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -825,7 +824,6 @@ describe('utils', () => { }; mockLogger.error.mockClear(); const res = await hasTimestampFields({ - wroteStatus: false, timestampField, ruleName: 'myfakerulename', // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -855,7 +853,6 @@ describe('utils', () => { }; mockLogger.error.mockClear(); const res = await hasTimestampFields({ - wroteStatus: false, timestampField, ruleName: 'Endpoint Security', // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -885,7 +882,6 @@ describe('utils', () => { }; mockLogger.error.mockClear(); const res = await hasTimestampFields({ - wroteStatus: false, timestampField, ruleName: 'NOT Endpoint Security', // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts index 79b36cf62573a..0a3eda70bbd87 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts @@ -115,34 +115,15 @@ export const hasReadIndexPrivileges = async (args: { } = args; const indexNames = Object.keys(privileges.index); - const [indexesWithReadPrivileges, indexesWithNoReadPrivileges] = partition( + const [, indexesWithNoReadPrivileges] = partition( indexNames, (indexName) => privileges.index[indexName].read ); - if (indexesWithReadPrivileges.length > 0 && indexesWithNoReadPrivileges.length > 0) { + if (indexesWithNoReadPrivileges.length > 0) { // some indices have read privileges others do not. // set a warning status - const errorString = `Missing required read privileges on the following indices: ${JSON.stringify( - indexesWithNoReadPrivileges - )}`; - logger.error(buildRuleMessage(errorString)); - await ruleStatusClient.logStatusChange({ - message: errorString, - ruleId, - ruleName, - ruleType, - spaceId, - newStatus: RuleExecutionStatus['partial failure'], - }); - return true; - } else if ( - indexesWithReadPrivileges.length === 0 && - indexesWithNoReadPrivileges.length === indexNames.length - ) { - // none of the indices had read privileges so set the status to failed - // since we can't search on any indices we do not have read privileges on - const errorString = `This rule may not have the required read privileges to the following indices: ${JSON.stringify( + const errorString = `This rule may not have the required read privileges to the following indices/index patterns: ${JSON.stringify( indexesWithNoReadPrivileges )}`; logger.error(buildRuleMessage(errorString)); @@ -160,7 +141,6 @@ export const hasReadIndexPrivileges = async (args: { }; export const hasTimestampFields = async (args: { - wroteStatus: boolean; timestampField: string; ruleName: string; // any is derived from here @@ -176,7 +156,6 @@ export const hasTimestampFields = async (args: { buildRuleMessage: BuildRuleMessage; }): Promise => { const { - wroteStatus, timestampField, ruleName, timestampFieldCapsResponse, @@ -189,7 +168,7 @@ export const hasTimestampFields = async (args: { buildRuleMessage, } = args; - if (!wroteStatus && isEmpty(timestampFieldCapsResponse.body.indices)) { + if (isEmpty(timestampFieldCapsResponse.body.indices)) { const errorString = `This rule is attempting to query data from Elasticsearch indices listed in the "Index pattern" section of the rule definition, however no index matching: ${JSON.stringify( inputIndices )} was found. This warning will continue to appear until a matching index is created or this rule is de-activated. ${ @@ -208,10 +187,9 @@ export const hasTimestampFields = async (args: { }); return true; } else if ( - !wroteStatus && - (isEmpty(timestampFieldCapsResponse.body.fields) || - timestampFieldCapsResponse.body.fields[timestampField] == null || - timestampFieldCapsResponse.body.fields[timestampField]?.unmapped?.indices != null) + isEmpty(timestampFieldCapsResponse.body.fields) || + timestampFieldCapsResponse.body.fields[timestampField] == null || + timestampFieldCapsResponse.body.fields[timestampField]?.unmapped?.indices != null ) { // if there is a timestamp override and the unmapped array for the timestamp override key is not empty, // warning @@ -236,7 +214,7 @@ export const hasTimestampFields = async (args: { }); return true; } - return wroteStatus; + return false; }; export const checkPrivileges = async ( @@ -257,6 +235,7 @@ export const checkPrivilegesFromEsClient = async ( index: [ { names: indices ?? [], + allow_restricted_indices: true, privileges: ['read'], }, ], diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/check_privileges.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/check_privileges.ts new file mode 100644 index 0000000000000..58b5f98ff0c0d --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/check_privileges.ts @@ -0,0 +1,107 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { DETECTION_ENGINE_RULES_URL } from '../../../../plugins/security_solution/common/constants'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { + createSignalsIndex, + deleteSignalsIndex, + deleteAllAlerts, + waitForRuleSuccessOrStatus, + getRuleForSignalTesting, + createRuleWithAuth, + getThresholdRuleForSignalTesting, +} from '../../utils'; +import { createUserAndRole, deleteUserAndRole } from '../../../common/services/security_solution'; +import { ROLES } from '../../../../plugins/security_solution/common/test'; +import { ThresholdCreateSchema } from '../../../../plugins/security_solution/common/detection_engine/schemas/request'; + +// eslint-disable-next-line import/no-default-export +export default ({ getService }: FtrProviderContext) => { + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + + describe('check_privileges', () => { + before(async () => { + await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts'); + await esArchiver.load('x-pack/test/functional/es_archives/security_solution/alias'); + await createSignalsIndex(supertest); + }); + + after(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/hosts'); + await esArchiver.unload('x-pack/test/functional/es_archives/security_solution/alias'); + await deleteSignalsIndex(supertest); + }); + + beforeEach(async () => { + await deleteAllAlerts(supertest); + }); + + afterEach(async () => { + await deleteAllAlerts(supertest); + }); + + describe('should set status to partial failure when user has no access', () => { + const indexTestCases = [ + ['host_alias'], + ['host_alias', 'auditbeat-8.0.0'], + ['host_alias*'], + ['host_alias*', 'auditbeat-*'], + ]; + indexTestCases.forEach((index) => { + it(`for KQL rule with index param: ${index}`, async () => { + const rule = getRuleForSignalTesting(index); + await createUserAndRole(getService, ROLES.detections_admin); + const { id } = await createRuleWithAuth(supertestWithoutAuth, rule, { + user: ROLES.detections_admin, + pass: 'changeme', + }); + await waitForRuleSuccessOrStatus(supertest, id, 'partial failure'); + const { body } = await supertest + .post(`${DETECTION_ENGINE_RULES_URL}/_find_statuses`) + .set('kbn-xsrf', 'true') + .send({ ids: [id] }) + .expect(200); + expect(body[id]?.current_status?.last_success_message).to.eql( + `This rule may not have the required read privileges to the following indices/index patterns: ["${index[0]}"]` + ); + + await deleteUserAndRole(getService, ROLES.detections_admin); + }); + + it(`for threshold rule with index param: ${index}`, async () => { + const rule: ThresholdCreateSchema = { + ...getThresholdRuleForSignalTesting(index), + threshold: { + field: [], + value: 700, + }, + }; + await createUserAndRole(getService, ROLES.detections_admin); + const { id } = await createRuleWithAuth(supertestWithoutAuth, rule, { + user: ROLES.detections_admin, + pass: 'changeme', + }); + await waitForRuleSuccessOrStatus(supertest, id, 'partial failure'); + const { body } = await supertest + .post(`${DETECTION_ENGINE_RULES_URL}/_find_statuses`) + .set('kbn-xsrf', 'true') + .send({ ids: [id] }) + .expect(200); + expect(body[id]?.current_status?.last_success_message).to.eql( + `This rule may not have the required read privileges to the following indices/index patterns: ["${index[0]}"]` + ); + + await deleteUserAndRole(getService, ROLES.detections_admin); + }); + }); + }); + }); +}; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/index.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/index.ts index 1b88c4fe21b49..00147a2ec2ef7 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/index.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/index.ts @@ -18,6 +18,7 @@ export default ({ loadTestFile }: FtrProviderContext): void => { loadTestFile(require.resolve('./add_actions')); loadTestFile(require.resolve('./update_actions')); loadTestFile(require.resolve('./add_prepackaged_rules')); + loadTestFile(require.resolve('./check_privileges')); loadTestFile(require.resolve('./create_rules')); loadTestFile(require.resolve('./create_rules_bulk')); loadTestFile(require.resolve('./create_index')); diff --git a/x-pack/test/detection_engine_api_integration/utils.ts b/x-pack/test/detection_engine_api_integration/utils.ts index eeae21c3b7bad..dd1b1ba966175 100644 --- a/x-pack/test/detection_engine_api_integration/utils.ts +++ b/x-pack/test/detection_engine_api_integration/utils.ts @@ -916,6 +916,25 @@ export const createRule = async ( return body; }; +/** + * Helper to cut down on the noise in some of the tests. This checks for + * an expected 200 still and does not try to any retries. + * @param supertest The supertest deps + * @param rule The rule to create + */ +export const createRuleWithAuth = async ( + supertest: SuperTest.SuperTest, + rule: CreateRulesSchema, + auth: { user: string; pass: string } +): Promise => { + const { body } = await supertest + .post(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .auth(auth.user, auth.pass) + .send(rule); + return body; +}; + /** * Helper to cut down on the noise in some of the tests. This checks for * an expected 200 still and does not do any retries. From 78003671fc18a8c3ee5c1a0b904b82b9de3f9e16 Mon Sep 17 00:00:00 2001 From: Dominique Clarke Date: Wed, 20 Oct 2021 00:13:02 -0400 Subject: [PATCH 104/204] [Uptime] [Synthetics Integration] browser - add script recorder option (#115184) * Add script recorder tab for browser based monitors * implement policy edit flow for script recorder * add tests and translations * adjust types * update metadata key and add test * merge master * adjust usePolicy and source_field * revert merge master * adjust types * use reusable hook * update zip url tls default fields * rename content constant * adjust error handling and remove ts-ignore * adjust error default value * remove unnecessary fragment * adjust types * update tech preview content Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../fleet_package/browser/formatters.ts | 2 +- .../fleet_package/browser/normalizers.ts | 44 +++--- .../browser/script_recorder_fields.test.tsx | 125 ++++++++++++++++ .../browser/script_recorder_fields.tsx | 134 ++++++++++++++++++ .../fleet_package/browser/simple_fields.tsx | 22 ++- .../fleet_package/browser/source_field.tsx | 79 ++++++++++- .../fleet_package/browser/uploader.tsx | 93 ++++++++++++ .../browser/zip_url_tls_fields.tsx | 29 +++- .../contexts/browser_context.tsx | 18 +-- .../fleet_package/contexts/index.ts | 2 +- .../contexts/policy_config_context.tsx | 6 + .../fleet_package/custom_fields.test.tsx | 30 ++-- .../fleet_package/custom_fields.tsx | 8 +- .../fleet_package/hooks/use_policy.ts | 131 +++++++++++++++++ .../{ => hooks}/use_update_policy.test.tsx | 11 +- .../{ => hooks}/use_update_policy.ts | 6 +- .../synthetics_policy_create_extension.tsx | 76 ++-------- .../synthetics_policy_edit_extension.tsx | 73 +--------- ...ics_policy_edit_extension_wrapper.test.tsx | 53 +++++++ ...nthetics_policy_edit_extension_wrapper.tsx | 1 + .../public/components/fleet_package/types.tsx | 4 + 21 files changed, 735 insertions(+), 212 deletions(-) create mode 100644 x-pack/plugins/uptime/public/components/fleet_package/browser/script_recorder_fields.test.tsx create mode 100644 x-pack/plugins/uptime/public/components/fleet_package/browser/script_recorder_fields.tsx create mode 100644 x-pack/plugins/uptime/public/components/fleet_package/browser/uploader.tsx create mode 100644 x-pack/plugins/uptime/public/components/fleet_package/hooks/use_policy.ts rename x-pack/plugins/uptime/public/components/fleet_package/{ => hooks}/use_update_policy.test.tsx (99%) rename x-pack/plugins/uptime/public/components/fleet_package/{ => hooks}/use_update_policy.ts (95%) diff --git a/x-pack/plugins/uptime/public/components/fleet_package/browser/formatters.ts b/x-pack/plugins/uptime/public/components/fleet_package/browser/formatters.ts index 640db94028bc6..e457453a38f19 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/browser/formatters.ts +++ b/x-pack/plugins/uptime/public/components/fleet_package/browser/formatters.ts @@ -9,9 +9,9 @@ import { BrowserFields, ConfigKeys } from '../types'; import { Formatter, commonFormatters, + objectToJsonFormatter, arrayToJsonFormatter, stringToJsonFormatter, - objectToJsonFormatter, } from '../common/formatters'; import { tlsValueToYamlFormatter, diff --git a/x-pack/plugins/uptime/public/components/fleet_package/browser/normalizers.ts b/x-pack/plugins/uptime/public/components/fleet_package/browser/normalizers.ts index 34b937b80dad0..2c675b9f28804 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/browser/normalizers.ts +++ b/x-pack/plugins/uptime/public/components/fleet_package/browser/normalizers.ts @@ -14,7 +14,6 @@ import { } from '../common/normalizers'; import { defaultBrowserSimpleFields, defaultBrowserAdvancedFields } from '../contexts'; -import { tlsJsonToObjectNormalizer, tlsStringToObjectNormalizer } from '../tls/normalizers'; export type BrowserNormalizerMap = Record; @@ -42,33 +41,22 @@ export const browserNormalizers: BrowserNormalizerMap = { [ConfigKeys.PARAMS]: getBrowserNormalizer(ConfigKeys.PARAMS), [ConfigKeys.SCREENSHOTS]: getBrowserNormalizer(ConfigKeys.SCREENSHOTS), [ConfigKeys.SYNTHETICS_ARGS]: getBrowserJsonToJavascriptNormalizer(ConfigKeys.SYNTHETICS_ARGS), - [ConfigKeys.ZIP_URL_TLS_CERTIFICATE_AUTHORITIES]: (fields) => - tlsJsonToObjectNormalizer( - fields?.[ConfigKeys.ZIP_URL_TLS_CERTIFICATE_AUTHORITIES]?.value, - ConfigKeys.TLS_CERTIFICATE_AUTHORITIES - ), - [ConfigKeys.ZIP_URL_TLS_CERTIFICATE]: (fields) => - tlsJsonToObjectNormalizer( - fields?.[ConfigKeys.ZIP_URL_TLS_CERTIFICATE]?.value, - ConfigKeys.TLS_CERTIFICATE - ), - [ConfigKeys.ZIP_URL_TLS_KEY]: (fields) => - tlsJsonToObjectNormalizer(fields?.[ConfigKeys.ZIP_URL_TLS_KEY]?.value, ConfigKeys.TLS_KEY), - [ConfigKeys.ZIP_URL_TLS_KEY_PASSPHRASE]: (fields) => - tlsStringToObjectNormalizer( - fields?.[ConfigKeys.ZIP_URL_TLS_KEY_PASSPHRASE]?.value, - ConfigKeys.TLS_KEY_PASSPHRASE - ), - [ConfigKeys.ZIP_URL_TLS_VERIFICATION_MODE]: (fields) => - tlsStringToObjectNormalizer( - fields?.[ConfigKeys.ZIP_URL_TLS_VERIFICATION_MODE]?.value, - ConfigKeys.TLS_VERIFICATION_MODE - ), - [ConfigKeys.ZIP_URL_TLS_VERSION]: (fields) => - tlsJsonToObjectNormalizer( - fields?.[ConfigKeys.ZIP_URL_TLS_VERSION]?.value, - ConfigKeys.TLS_VERSION - ), + [ConfigKeys.ZIP_URL_TLS_CERTIFICATE_AUTHORITIES]: getBrowserJsonToJavascriptNormalizer( + ConfigKeys.ZIP_URL_TLS_CERTIFICATE_AUTHORITIES + ), + [ConfigKeys.ZIP_URL_TLS_CERTIFICATE]: getBrowserJsonToJavascriptNormalizer( + ConfigKeys.ZIP_URL_TLS_CERTIFICATE + ), + [ConfigKeys.ZIP_URL_TLS_KEY]: getBrowserJsonToJavascriptNormalizer(ConfigKeys.ZIP_URL_TLS_KEY), + [ConfigKeys.ZIP_URL_TLS_KEY_PASSPHRASE]: getBrowserNormalizer( + ConfigKeys.ZIP_URL_TLS_KEY_PASSPHRASE + ), + [ConfigKeys.ZIP_URL_TLS_VERIFICATION_MODE]: getBrowserNormalizer( + ConfigKeys.ZIP_URL_TLS_VERIFICATION_MODE + ), + [ConfigKeys.ZIP_URL_TLS_VERSION]: getBrowserJsonToJavascriptNormalizer( + ConfigKeys.ZIP_URL_TLS_VERSION + ), [ConfigKeys.JOURNEY_FILTERS_MATCH]: getBrowserJsonToJavascriptNormalizer( ConfigKeys.JOURNEY_FILTERS_MATCH ), diff --git a/x-pack/plugins/uptime/public/components/fleet_package/browser/script_recorder_fields.test.tsx b/x-pack/plugins/uptime/public/components/fleet_package/browser/script_recorder_fields.test.tsx new file mode 100644 index 0000000000000..b19ca47ab76a5 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/fleet_package/browser/script_recorder_fields.test.tsx @@ -0,0 +1,125 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { fireEvent, waitFor } from '@testing-library/react'; +import { render } from '../../../lib/helper/rtl_helpers'; +import { ScriptRecorderFields } from './script_recorder_fields'; +import { PolicyConfigContextProvider } from '../contexts'; + +jest.mock('@elastic/eui/lib/services/accessibility/html_id_generator', () => ({ + ...jest.requireActual('@elastic/eui/lib/services/accessibility/html_id_generator'), + htmlIdGenerator: () => () => `id-${Math.random()}`, +})); + +const onChange = jest.fn(); + +describe('', () => { + let file: File; + const testScript = 'step(() => {})'; + const WrappedComponent = ({ + isEditable = true, + script = '', + fileName = '', + }: { + isEditable?: boolean; + script?: string; + fileName?: string; + }) => { + return ( + + + + ); + }; + + beforeEach(() => { + jest.clearAllMocks(); + file = new File([testScript], 'samplescript.js', { type: 'text/javascript' }); + }); + + it('renders ScriptRecorderFields', () => { + const { getByText, queryByText } = render(); + + const downloadLink = getByText('Download the Elastic Synthetics Recorder'); + + expect(downloadLink).toBeInTheDocument(); + expect(downloadLink).toHaveAttribute('target', '_blank'); + + expect(queryByText('Show script')).not.toBeInTheDocument(); + expect(queryByText('Remove script')).not.toBeInTheDocument(); + }); + + it('handles uploading files', async () => { + const { getByTestId } = render(); + + const uploader = getByTestId('syntheticsFleetScriptRecorderUploader'); + + fireEvent.change(uploader, { + target: { files: [file] }, + }); + + await waitFor(() => { + expect(onChange).toBeCalledWith({ scriptText: testScript, fileName: 'samplescript.js' }); + }); + }); + + it('shows user errors for invalid file types', async () => { + const { getByTestId, getByText } = render(); + file = new File(['journey(() => {})'], 'samplescript.js', { type: 'text/javascript' }); + + let uploader = getByTestId('syntheticsFleetScriptRecorderUploader') as HTMLInputElement; + + fireEvent.change(uploader, { + target: { files: [file] }, + }); + + uploader = getByTestId('syntheticsFleetScriptRecorderUploader') as HTMLInputElement; + + await waitFor(() => { + expect(onChange).not.toBeCalled(); + expect( + getByText( + 'Error uploading file. Please upload a .js file generated by the Elastic Synthetics Recorder in inline script format.' + ) + ).toBeInTheDocument(); + }); + }); + + it('shows show script button when script is available', () => { + const { getByText, queryByText } = render(); + + const showScriptBtn = getByText('Show script'); + + expect(queryByText(testScript)).not.toBeInTheDocument(); + + fireEvent.click(showScriptBtn); + + expect(getByText(testScript)).toBeInTheDocument(); + }); + + it('shows show remove script button when script is available and isEditable is true', async () => { + const { getByText, getByTestId } = render( + + ); + + const showScriptBtn = getByText('Show script'); + fireEvent.click(showScriptBtn); + + expect(getByText(testScript)).toBeInTheDocument(); + + fireEvent.click(getByTestId('euiFlyoutCloseButton')); + + const removeScriptBtn = getByText('Remove script'); + + fireEvent.click(removeScriptBtn); + + await waitFor(() => { + expect(onChange).toBeCalledWith({ scriptText: '', fileName: '' }); + }); + }); +}); diff --git a/x-pack/plugins/uptime/public/components/fleet_package/browser/script_recorder_fields.tsx b/x-pack/plugins/uptime/public/components/fleet_package/browser/script_recorder_fields.tsx new file mode 100644 index 0000000000000..9b99b4094e63b --- /dev/null +++ b/x-pack/plugins/uptime/public/components/fleet_package/browser/script_recorder_fields.tsx @@ -0,0 +1,134 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useState, useCallback } from 'react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { + EuiLink, + EuiFlexGroup, + EuiFlexItem, + EuiFlyout, + EuiFlyoutHeader, + EuiFormRow, + EuiCodeBlock, + EuiTitle, + EuiButton, + EuiSpacer, + EuiText, +} from '@elastic/eui'; +import { usePolicyConfigContext } from '../contexts/policy_config_context'; +import { Uploader } from './uploader'; + +interface Props { + onChange: ({ scriptText, fileName }: { scriptText: string; fileName: string }) => void; + script: string; + fileName?: string; +} + +export function ScriptRecorderFields({ onChange, script, fileName }: Props) { + const [showScript, setShowScript] = useState(false); + const { isEditable } = usePolicyConfigContext(); + + const handleUpload = useCallback( + ({ scriptText, fileName: fileNameT }: { scriptText: string; fileName: string }) => { + onChange({ scriptText, fileName: fileNameT }); + }, + [onChange] + ); + + return ( + <> + + + + + + {isEditable && script ? ( + + + {fileName} + + + ) : ( + + )} + {script && ( + <> + + + + setShowScript(true)} + iconType="editorCodeBlock" + iconSide="right" + > + + + + + {isEditable && ( + onChange({ scriptText: '', fileName: '' })} + iconType="trash" + iconSide="right" + color="danger" + > + + + )} + + + + )} + {showScript && ( + setShowScript(false)} + aria-labelledby="syntheticsBrowserScriptBlockHeader" + closeButtonAriaLabel={CLOSE_BUTTON_LABEL} + > + + + + {fileName || PLACEHOLDER_FILE_NAME} + + + +
+ + {script} + +
+
+ )} + + ); +} + +const PLACEHOLDER_FILE_NAME = i18n.translate( + 'xpack.uptime.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.scriptRecorder.mockFileName', + { + defaultMessage: 'test_script.js', + } +); + +const CLOSE_BUTTON_LABEL = i18n.translate( + 'xpack.uptime.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.scriptRecorder.closeButtonLabel', + { + defaultMessage: 'Close script flyout', + } +); diff --git a/x-pack/plugins/uptime/public/components/fleet_package/browser/simple_fields.tsx b/x-pack/plugins/uptime/public/components/fleet_package/browser/simple_fields.tsx index 775778296fba8..50ad14aa98287 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/browser/simple_fields.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/browser/simple_fields.tsx @@ -24,7 +24,17 @@ export const BrowserSimpleFields = memo(({ validate }) => { setFields((prevFields) => ({ ...prevFields, [configKey]: value })); }; const onChangeSourceField = useCallback( - ({ zipUrl, folder, username, password, inlineScript, params, proxyUrl }) => { + ({ + zipUrl, + folder, + username, + password, + inlineScript, + params, + proxyUrl, + isGeneratedScript, + fileName, + }) => { setFields((prevFields) => ({ ...prevFields, [ConfigKeys.SOURCE_ZIP_URL]: zipUrl, @@ -34,6 +44,13 @@ export const BrowserSimpleFields = memo(({ validate }) => { [ConfigKeys.SOURCE_ZIP_PASSWORD]: password, [ConfigKeys.SOURCE_INLINE]: inlineScript, [ConfigKeys.PARAMS]: params, + [ConfigKeys.METADATA]: { + ...prevFields[ConfigKeys.METADATA], + script_source: { + is_generated_script: isGeneratedScript, + file_name: fileName, + }, + }, })); }, [setFields] @@ -87,6 +104,9 @@ export const BrowserSimpleFields = memo(({ validate }) => { password: defaultValues[ConfigKeys.SOURCE_ZIP_PASSWORD], inlineScript: defaultValues[ConfigKeys.SOURCE_INLINE], params: defaultValues[ConfigKeys.PARAMS], + isGeneratedScript: + defaultValues[ConfigKeys.METADATA].script_source?.is_generated_script, + fileName: defaultValues[ConfigKeys.METADATA].script_source?.file_name, }), [defaultValues] )} diff --git a/x-pack/plugins/uptime/public/components/fleet_package/browser/source_field.tsx b/x-pack/plugins/uptime/public/components/fleet_package/browser/source_field.tsx index 6f2a7c99ad0d5..e4b03e53432dd 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/browser/source_field.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/browser/source_field.tsx @@ -5,23 +5,28 @@ * 2.0. */ import React, { useEffect, useState } from 'react'; +import styled from 'styled-components'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; - import { EuiTabbedContent, EuiFormRow, EuiFieldText, EuiFieldPassword, EuiSpacer, + EuiBetaBadge, + EuiFlexGroup, + EuiFlexItem, } from '@elastic/eui'; import { OptionalLabel } from '../optional_label'; import { CodeEditor } from '../code_editor'; +import { ScriptRecorderFields } from './script_recorder_fields'; import { ZipUrlTLSFields } from './zip_url_tls_fields'; import { MonacoEditorLangId } from '../types'; enum SourceType { INLINE = 'syntheticsBrowserInlineConfig', + SCRIPT_RECORDER = 'syntheticsBrowserScriptRecorderConfig', ZIP = 'syntheticsBrowserZipURLConfig', } @@ -33,6 +38,8 @@ interface SourceConfig { password: string; inlineScript: string; params: string; + isGeneratedScript?: boolean; + fileName?: string; } interface Props { @@ -48,12 +55,22 @@ export const defaultValues = { password: '', inlineScript: '', params: '', + isGeneratedScript: false, + fileName: '', +}; + +const getDefaultTab = (defaultConfig: SourceConfig) => { + if (defaultConfig.inlineScript && defaultConfig.isGeneratedScript) { + return SourceType.SCRIPT_RECORDER; + } else if (defaultConfig.inlineScript) { + return SourceType.INLINE; + } + + return SourceType.ZIP; }; export const SourceField = ({ onChange, defaultConfig = defaultValues }: Props) => { - const [sourceType, setSourceType] = useState( - defaultConfig.inlineScript ? SourceType.INLINE : SourceType.ZIP - ); + const [sourceType, setSourceType] = useState(getDefaultTab(defaultConfig)); const [config, setConfig] = useState(defaultConfig); useEffect(() => { @@ -264,6 +281,52 @@ export const SourceField = ({ onChange, defaultConfig = defaultValues }: Props) ), }, + { + id: 'syntheticsBrowserScriptRecorderConfig', + name: ( + + + + + + + + + ), + 'data-test-subj': 'syntheticsSourceTab__scriptRecorder', + content: ( + + setConfig((prevConfig) => ({ + ...prevConfig, + inlineScript: scriptText, + isGeneratedScript: true, + fileName, + })) + } + script={config.inlineScript} + fileName={config.fileName} + /> + ), + }, ]; return ( @@ -272,11 +335,17 @@ export const SourceField = ({ onChange, defaultConfig = defaultValues }: Props) initialSelectedTab={tabs.find((tab) => tab.id === sourceType)} autoFocus="selected" onTabClick={(tab) => { - setSourceType(tab.id as SourceType); if (tab.id !== sourceType) { setConfig(defaultValues); } + setSourceType(tab.id as SourceType); }} /> ); }; + +const StyledBetaBadgeWrapper = styled(EuiFlexItem)` + .euiToolTipAnchor { + display: flex; + } +`; diff --git a/x-pack/plugins/uptime/public/components/fleet_package/browser/uploader.tsx b/x-pack/plugins/uptime/public/components/fleet_package/browser/uploader.tsx new file mode 100644 index 0000000000000..f17f11ebdccb6 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/fleet_package/browser/uploader.tsx @@ -0,0 +1,93 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useState, useRef } from 'react'; + +import { i18n } from '@kbn/i18n'; + +import { EuiFormRow, EuiFilePicker } from '@elastic/eui'; + +interface Props { + onUpload: ({ scriptText, fileName }: { scriptText: string; fileName: string }) => void; +} + +export function Uploader({ onUpload }: Props) { + const fileReader = useRef(null); + const [error, setError] = useState(null); + const filePickerRef = useRef(null); + + const handleFileRead = (fileName: string) => { + const content = fileReader?.current?.result as string; + + if (content?.trim().slice(0, 4) !== 'step') { + setError(PARSING_ERROR); + filePickerRef.current?.removeFiles(); + return; + } + + onUpload({ scriptText: content, fileName }); + setError(null); + }; + + const handleFileChosen = (files: FileList | null) => { + if (!files || !files.length) { + onUpload({ scriptText: '', fileName: '' }); + return; + } + if (files.length && files[0].type !== 'text/javascript') { + setError(INVALID_FILE_ERROR); + filePickerRef.current?.removeFiles(); + return; + } + fileReader.current = new FileReader(); + fileReader.current.onloadend = () => handleFileRead(files[0].name); + fileReader.current.readAsText(files[0]); + }; + + return ( + + + + ); +} + +const TESTING_SCRIPT_LABEL = i18n.translate( + 'xpack.uptime.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.uploader.fieldLabel', + { + defaultMessage: 'Testing script', + } +); + +const PROMPT_TEXT = i18n.translate( + 'xpack.uptime.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.uploader.label', + { + defaultMessage: 'Select recorder-generated .js file', + } +); + +const INVALID_FILE_ERROR = i18n.translate( + 'xpack.uptime.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.uploader.invalidFileError', + { + defaultMessage: + 'Invalid file type. Please upload a .js file generated by the Elastic Synthetics Recorder.', + } +); + +const PARSING_ERROR = i18n.translate( + 'xpack.uptime.createPackagePolicy.stepConfigure.monitorIntegrationSettingsSection.browser.uploader.parsingError', + { + defaultMessage: + 'Error uploading file. Please upload a .js file generated by the Elastic Synthetics Recorder in inline script format.', + } +); diff --git a/x-pack/plugins/uptime/public/components/fleet_package/browser/zip_url_tls_fields.tsx b/x-pack/plugins/uptime/public/components/fleet_package/browser/zip_url_tls_fields.tsx index bd5d2f3e5d4a2..ed1ad9a8ce65c 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/browser/zip_url_tls_fields.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/browser/zip_url_tls_fields.tsx @@ -12,7 +12,11 @@ import { EuiSwitch, EuiFormRow } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { TLSOptions, TLSConfig } from '../common/tls_options'; -import { useBrowserSimpleFieldsContext, usePolicyConfigContext } from '../contexts'; +import { + useBrowserSimpleFieldsContext, + usePolicyConfigContext, + defaultTLSFields, +} from '../contexts'; import { ConfigKeys } from '../types'; @@ -67,12 +71,23 @@ export const ZipUrlTLSFields = () => { {isZipUrlTLSEnabled ? ( >; @@ -24,6 +23,10 @@ interface IBrowserSimpleFieldsContextProvider { export const initialValues: IBrowserSimpleFields = { ...commonDefaultValues, [ConfigKeys.METADATA]: { + script_source: { + is_generated_script: false, + file_name: '', + }, is_zip_url_tls_enabled: false, }, [ConfigKeys.MONITOR_TYPE]: DataStream.BROWSER, @@ -34,13 +37,12 @@ export const initialValues: IBrowserSimpleFields = { [ConfigKeys.SOURCE_ZIP_PROXY_URL]: '', [ConfigKeys.SOURCE_INLINE]: '', [ConfigKeys.PARAMS]: '', - [ConfigKeys.ZIP_URL_TLS_CERTIFICATE_AUTHORITIES]: - tlsDefaultValues[ConfigKeys.TLS_CERTIFICATE_AUTHORITIES], - [ConfigKeys.ZIP_URL_TLS_CERTIFICATE]: tlsDefaultValues[ConfigKeys.TLS_CERTIFICATE], - [ConfigKeys.ZIP_URL_TLS_KEY]: tlsDefaultValues[ConfigKeys.TLS_KEY], - [ConfigKeys.ZIP_URL_TLS_KEY_PASSPHRASE]: tlsDefaultValues[ConfigKeys.TLS_KEY_PASSPHRASE], - [ConfigKeys.ZIP_URL_TLS_VERIFICATION_MODE]: tlsDefaultValues[ConfigKeys.TLS_VERIFICATION_MODE], - [ConfigKeys.ZIP_URL_TLS_VERSION]: tlsDefaultValues[ConfigKeys.TLS_VERSION], + [ConfigKeys.ZIP_URL_TLS_CERTIFICATE_AUTHORITIES]: undefined, + [ConfigKeys.ZIP_URL_TLS_CERTIFICATE]: undefined, + [ConfigKeys.ZIP_URL_TLS_KEY]: undefined, + [ConfigKeys.ZIP_URL_TLS_KEY_PASSPHRASE]: undefined, + [ConfigKeys.ZIP_URL_TLS_VERIFICATION_MODE]: undefined, + [ConfigKeys.ZIP_URL_TLS_VERSION]: undefined, }; const defaultContext: IBrowserSimpleFieldsContext = { diff --git a/x-pack/plugins/uptime/public/components/fleet_package/contexts/index.ts b/x-pack/plugins/uptime/public/components/fleet_package/contexts/index.ts index 3d08dec46a4be..df2e9cfa6d4ea 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/contexts/index.ts +++ b/x-pack/plugins/uptime/public/components/fleet_package/contexts/index.ts @@ -7,7 +7,7 @@ export { PolicyConfigContext, PolicyConfigContextProvider, - initialValue as defaultMonitorType, + initialValue as defaultPolicyConfig, usePolicyConfigContext, } from './policy_config_context'; export { diff --git a/x-pack/plugins/uptime/public/components/fleet_package/contexts/policy_config_context.tsx b/x-pack/plugins/uptime/public/components/fleet_package/contexts/policy_config_context.tsx index 535a415c9a43d..69c0e1d7ba4fe 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/contexts/policy_config_context.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/contexts/policy_config_context.tsx @@ -18,6 +18,7 @@ interface IPolicyConfigContext { isZipUrlTLSEnabled?: boolean; defaultIsTLSEnabled?: boolean; defaultIsZipUrlTLSEnabled?: boolean; + isEditable?: boolean; } interface IPolicyConfigContextProvider { @@ -25,6 +26,7 @@ interface IPolicyConfigContextProvider { defaultMonitorType?: DataStream; defaultIsTLSEnabled?: boolean; defaultIsZipUrlTLSEnabled?: boolean; + isEditable?: boolean; } export const initialValue = DataStream.HTTP; @@ -45,6 +47,7 @@ const defaultContext: IPolicyConfigContext = { defaultMonitorType: initialValue, // immutable, defaultIsTLSEnabled: false, defaultIsZipUrlTLSEnabled: false, + isEditable: false, }; export const PolicyConfigContext = createContext(defaultContext); @@ -54,6 +57,7 @@ export const PolicyConfigContextProvider = ({ defaultMonitorType = initialValue, defaultIsTLSEnabled = false, defaultIsZipUrlTLSEnabled = false, + isEditable = false, }: IPolicyConfigContextProvider) => { const [monitorType, setMonitorType] = useState(defaultMonitorType); const [isTLSEnabled, setIsTLSEnabled] = useState(defaultIsTLSEnabled); @@ -70,6 +74,7 @@ export const PolicyConfigContextProvider = ({ setIsZipUrlTLSEnabled, defaultIsTLSEnabled, defaultIsZipUrlTLSEnabled, + isEditable, }; }, [ monitorType, @@ -78,6 +83,7 @@ export const PolicyConfigContextProvider = ({ isZipUrlTLSEnabled, defaultIsTLSEnabled, defaultIsZipUrlTLSEnabled, + isEditable, ]); return ; diff --git a/x-pack/plugins/uptime/public/components/fleet_package/custom_fields.test.tsx b/x-pack/plugins/uptime/public/components/fleet_package/custom_fields.test.tsx index 62c6f5598adb4..03bba0f8d2e54 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/custom_fields.test.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/custom_fields.test.tsx @@ -54,21 +54,17 @@ const defaultTCPConfig = defaultConfig[DataStream.TCP]; describe.skip('', () => { const WrappedComponent = ({ validate = defaultValidation, - typeEditable = false, + isEditable = false, dataStreams = [DataStream.HTTP, DataStream.TCP, DataStream.ICMP, DataStream.BROWSER], }) => { return ( - + - + @@ -80,7 +76,7 @@ describe.skip('', () => { it('renders CustomFields', async () => { const { getByText, getByLabelText, queryByLabelText } = render(); - const monitorType = queryByLabelText('Monitor Type') as HTMLInputElement; + const monitorType = getByLabelText('Monitor Type') as HTMLInputElement; const url = getByLabelText('URL') as HTMLInputElement; const proxyUrl = getByLabelText('Proxy URL') as HTMLInputElement; const monitorIntervalNumber = getByLabelText('Number') as HTMLInputElement; @@ -88,7 +84,7 @@ describe.skip('', () => { const apmServiceName = getByLabelText('APM service name') as HTMLInputElement; const maxRedirects = getByLabelText('Max redirects') as HTMLInputElement; const timeout = getByLabelText('Timeout in seconds') as HTMLInputElement; - expect(monitorType).not.toBeInTheDocument(); + expect(monitorType).toBeInTheDocument(); expect(url).toBeInTheDocument(); expect(url.value).toEqual(defaultHTTPConfig[ConfigKeys.URLS]); expect(proxyUrl).toBeInTheDocument(); @@ -117,6 +113,13 @@ describe.skip('', () => { }); }); + it('does not show monitor type dropdown when isEditable is true', async () => { + const { queryByLabelText } = render(); + const monitorType = queryByLabelText('Monitor Type') as HTMLInputElement; + + expect(monitorType).not.toBeInTheDocument(); + }); + it('shows SSL fields when Enable SSL Fields is checked', async () => { const { findByLabelText, queryByLabelText } = render(); const enableSSL = queryByLabelText('Enable TLS configuration') as HTMLInputElement; @@ -180,7 +183,7 @@ describe.skip('', () => { it('handles switching monitor type', () => { const { getByText, getByLabelText, queryByLabelText, getAllByLabelText } = render( - + ); const monitorType = getByLabelText('Monitor Type') as HTMLInputElement; expect(monitorType).toBeInTheDocument(); @@ -244,7 +247,7 @@ describe.skip('', () => { }); it('shows resolve hostnames locally field when proxy url is filled for tcp monitors', () => { - const { getByLabelText, queryByLabelText } = render(); + const { getByLabelText, queryByLabelText } = render(); const monitorType = getByLabelText('Monitor Type') as HTMLInputElement; fireEvent.change(monitorType, { target: { value: DataStream.TCP } }); @@ -302,10 +305,7 @@ describe.skip('', () => { it('does not show monitor options that are not contained in datastreams', async () => { const { getByText, queryByText, queryByLabelText } = render( - + ); const monitorType = queryByLabelText('Monitor Type') as HTMLInputElement; diff --git a/x-pack/plugins/uptime/public/components/fleet_package/custom_fields.tsx b/x-pack/plugins/uptime/public/components/fleet_package/custom_fields.tsx index 7323464f3e9dd..98eac21a42076 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/custom_fields.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/custom_fields.tsx @@ -30,13 +30,13 @@ import { BrowserSimpleFields } from './browser/simple_fields'; import { BrowserAdvancedFields } from './browser/advanced_fields'; interface Props { - typeEditable?: boolean; validate: Validation; dataStreams?: DataStream[]; } -export const CustomFields = memo(({ typeEditable = false, validate, dataStreams = [] }) => { - const { monitorType, setMonitorType, isTLSEnabled, setIsTLSEnabled } = usePolicyConfigContext(); +export const CustomFields = memo(({ validate, dataStreams = [] }) => { + const { monitorType, setMonitorType, isTLSEnabled, setIsTLSEnabled, isEditable } = + usePolicyConfigContext(); const isHTTP = monitorType === DataStream.HTTP; const isTCP = monitorType === DataStream.TCP; @@ -88,7 +88,7 @@ export const CustomFields = memo(({ typeEditable = false, validate, dataS > - {typeEditable && ( + {!isEditable && ( { + const { isTLSEnabled, isZipUrlTLSEnabled } = usePolicyConfigContext(); + const { fields: httpSimpleFields } = useHTTPSimpleFieldsContext(); + const { fields: tcpSimpleFields } = useTCPSimpleFieldsContext(); + const { fields: icmpSimpleFields } = useICMPSimpleFieldsContext(); + const { fields: browserSimpleFields } = useBrowserSimpleFieldsContext(); + const { fields: httpAdvancedFields } = useHTTPAdvancedFieldsContext(); + const { fields: tcpAdvancedFields } = useTCPAdvancedFieldsContext(); + const { fields: browserAdvancedFields } = useBrowserAdvancedFieldsContext(); + const { fields: tlsFields } = useTLSFieldsContext(); + + const metadata = useMemo( + () => ({ + is_tls_enabled: isTLSEnabled, + is_zip_url_tls_enabled: isZipUrlTLSEnabled, + }), + [isTLSEnabled, isZipUrlTLSEnabled] + ); + + const policyConfig: PolicyConfig = useMemo( + () => ({ + [DataStream.HTTP]: { + ...httpSimpleFields, + ...httpAdvancedFields, + ...tlsFields, + [ConfigKeys.METADATA]: { + ...httpSimpleFields[ConfigKeys.METADATA], + ...metadata, + }, + [ConfigKeys.NAME]: name, + } as HTTPFields, + [DataStream.TCP]: { + ...tcpSimpleFields, + ...tcpAdvancedFields, + ...tlsFields, + [ConfigKeys.METADATA]: { + ...tcpSimpleFields[ConfigKeys.METADATA], + ...metadata, + }, + [ConfigKeys.NAME]: name, + } as TCPFields, + [DataStream.ICMP]: { + ...icmpSimpleFields, + [ConfigKeys.NAME]: name, + } as ICMPFields, + [DataStream.BROWSER]: { + ...browserSimpleFields, + ...browserAdvancedFields, + [ConfigKeys.METADATA]: { + ...browserSimpleFields[ConfigKeys.METADATA], + ...metadata, + }, + [ConfigKeys.NAME]: name, + } as BrowserFields, + }), + [ + metadata, + httpSimpleFields, + httpAdvancedFields, + tcpSimpleFields, + tcpAdvancedFields, + icmpSimpleFields, + browserSimpleFields, + browserAdvancedFields, + tlsFields, + name, + ] + ); + + return policyConfig; +}; diff --git a/x-pack/plugins/uptime/public/components/fleet_package/use_update_policy.test.tsx b/x-pack/plugins/uptime/public/components/fleet_package/hooks/use_update_policy.test.tsx similarity index 99% rename from x-pack/plugins/uptime/public/components/fleet_package/use_update_policy.test.tsx rename to x-pack/plugins/uptime/public/components/fleet_package/hooks/use_update_policy.test.tsx index 3bffab33adb1e..dc46a4b57bcd8 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/use_update_policy.test.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/hooks/use_update_policy.test.tsx @@ -4,11 +4,10 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - -import { useUpdatePolicy } from './use_update_policy'; import { renderHook } from '@testing-library/react-hooks'; -import { NewPackagePolicy } from '../../../../fleet/public'; -import { validate } from './validation'; +import { useUpdatePolicy } from './use_update_policy'; +import { NewPackagePolicy } from '../../../../../fleet/public'; +import { validate } from '../validation'; import { ConfigKeys, DataStream, @@ -20,8 +19,8 @@ import { ITLSFields, HTTPFields, BrowserFields, -} from './types'; -import { defaultConfig } from './synthetics_policy_create_extension'; +} from '../types'; +import { defaultConfig } from '../synthetics_policy_create_extension'; describe('useBarChartsHooks', () => { const newPolicy: NewPackagePolicy = { diff --git a/x-pack/plugins/uptime/public/components/fleet_package/use_update_policy.ts b/x-pack/plugins/uptime/public/components/fleet_package/hooks/use_update_policy.ts similarity index 95% rename from x-pack/plugins/uptime/public/components/fleet_package/use_update_policy.ts rename to x-pack/plugins/uptime/public/components/fleet_package/hooks/use_update_policy.ts index 145a86c6bd50d..17ded6385da4f 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/use_update_policy.ts +++ b/x-pack/plugins/uptime/public/components/fleet_package/hooks/use_update_policy.ts @@ -5,9 +5,9 @@ * 2.0. */ import { useEffect, useRef, useState } from 'react'; -import { NewPackagePolicy } from '../../../../fleet/public'; -import { ConfigKeys, DataStream, Validation, ICustomFields } from './types'; -import { formatters } from './helpers/formatters'; +import { NewPackagePolicy } from '../../../../../fleet/public'; +import { ConfigKeys, DataStream, Validation, ICustomFields } from '../types'; +import { formatters } from '../helpers/formatters'; interface Props { monitorType: DataStream; diff --git a/x-pack/plugins/uptime/public/components/fleet_package/synthetics_policy_create_extension.tsx b/x-pack/plugins/uptime/public/components/fleet_package/synthetics_policy_create_extension.tsx index 3db7ac424e651..4fa101a329cd0 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/synthetics_policy_create_extension.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/synthetics_policy_create_extension.tsx @@ -8,36 +8,21 @@ import React, { memo, useEffect, useMemo } from 'react'; import { PackagePolicyCreateExtensionComponentProps } from '../../../../fleet/public'; import { useTrackPageview } from '../../../../observability/public'; -import { - PolicyConfig, - DataStream, - ConfigKeys, - HTTPFields, - TCPFields, - ICMPFields, - BrowserFields, -} from './types'; +import { PolicyConfig, DataStream } from './types'; import { usePolicyConfigContext, - useTCPSimpleFieldsContext, - useTCPAdvancedFieldsContext, - useICMPSimpleFieldsContext, - useHTTPSimpleFieldsContext, - useHTTPAdvancedFieldsContext, - useTLSFieldsContext, - useBrowserSimpleFieldsContext, - useBrowserAdvancedFieldsContext, - defaultHTTPAdvancedFields, defaultHTTPSimpleFields, - defaultICMPSimpleFields, + defaultHTTPAdvancedFields, defaultTCPSimpleFields, defaultTCPAdvancedFields, + defaultICMPSimpleFields, defaultBrowserSimpleFields, defaultBrowserAdvancedFields, defaultTLSFields, } from './contexts'; import { CustomFields } from './custom_fields'; -import { useUpdatePolicy } from './use_update_policy'; +import { useUpdatePolicy } from './hooks/use_update_policy'; +import { usePolicy } from './hooks/use_policy'; import { validate } from './validation'; export const defaultConfig: PolicyConfig = { @@ -65,55 +50,12 @@ export const defaultConfig: PolicyConfig = { */ export const SyntheticsPolicyCreateExtension = memo( ({ newPolicy, onChange }) => { - const { monitorType, isTLSEnabled, isZipUrlTLSEnabled } = usePolicyConfigContext(); - const { fields: httpSimpleFields } = useHTTPSimpleFieldsContext(); - const { fields: tcpSimpleFields } = useTCPSimpleFieldsContext(); - const { fields: icmpSimpleFields } = useICMPSimpleFieldsContext(); - const { fields: browserSimpleFields } = useBrowserSimpleFieldsContext(); - const { fields: httpAdvancedFields } = useHTTPAdvancedFieldsContext(); - const { fields: tcpAdvancedFields } = useTCPAdvancedFieldsContext(); - const { fields: browserAdvancedFields } = useBrowserAdvancedFieldsContext(); - const { fields: tlsFields } = useTLSFieldsContext(); - - const metaData = useMemo( - () => ({ - is_tls_enabled: isTLSEnabled, - is_zip_url_tls_enabled: isZipUrlTLSEnabled, - }), - [isTLSEnabled, isZipUrlTLSEnabled] - ); - - const policyConfig: PolicyConfig = { - [DataStream.HTTP]: { - ...httpSimpleFields, - ...httpAdvancedFields, - ...tlsFields, - [ConfigKeys.METADATA]: metaData, - [ConfigKeys.NAME]: newPolicy.name, - } as HTTPFields, - [DataStream.TCP]: { - ...tcpSimpleFields, - ...tcpAdvancedFields, - ...tlsFields, - [ConfigKeys.METADATA]: metaData, - [ConfigKeys.NAME]: newPolicy.name, - } as TCPFields, - [DataStream.ICMP]: { - ...icmpSimpleFields, - [ConfigKeys.NAME]: newPolicy.name, - } as ICMPFields, - [DataStream.BROWSER]: { - ...browserSimpleFields, - ...browserAdvancedFields, - ...tlsFields, - [ConfigKeys.METADATA]: metaData, - [ConfigKeys.NAME]: newPolicy.name, - } as BrowserFields, - }; - useTrackPageview({ app: 'fleet', path: 'syntheticsCreate' }); useTrackPageview({ app: 'fleet', path: 'syntheticsCreate', delay: 15000 }); + const { monitorType } = usePolicyConfigContext(); + const policyConfig: PolicyConfig = usePolicy(newPolicy.name); + const dataStreams: DataStream[] = useMemo(() => { return newPolicy.inputs.map((input) => { return input.type.replace(/synthetics\//g, '') as DataStream; @@ -143,7 +85,7 @@ export const SyntheticsPolicyCreateExtension = memo; + return ; } ); SyntheticsPolicyCreateExtension.displayName = 'SyntheticsPolicyCreateExtension'; diff --git a/x-pack/plugins/uptime/public/components/fleet_package/synthetics_policy_edit_extension.tsx b/x-pack/plugins/uptime/public/components/fleet_package/synthetics_policy_edit_extension.tsx index 8e441d4eed6e3..1e01f43439a31 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/synthetics_policy_edit_extension.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/synthetics_policy_edit_extension.tsx @@ -5,32 +5,14 @@ * 2.0. */ -import React, { memo, useMemo } from 'react'; +import React, { memo } from 'react'; import { PackagePolicyEditExtensionComponentProps } from '../../../../fleet/public'; import { useTrackPageview } from '../../../../observability/public'; -import { - usePolicyConfigContext, - useTCPSimpleFieldsContext, - useTCPAdvancedFieldsContext, - useICMPSimpleFieldsContext, - useHTTPSimpleFieldsContext, - useHTTPAdvancedFieldsContext, - useTLSFieldsContext, - useBrowserSimpleFieldsContext, - useBrowserAdvancedFieldsContext, -} from './contexts'; -import { - ICustomFields, - DataStream, - HTTPFields, - TCPFields, - ICMPFields, - BrowserFields, - ConfigKeys, - PolicyConfig, -} from './types'; +import { usePolicyConfigContext } from './contexts'; +import { ICustomFields, PolicyConfig } from './types'; import { CustomFields } from './custom_fields'; -import { useUpdatePolicy } from './use_update_policy'; +import { useUpdatePolicy } from './hooks/use_update_policy'; +import { usePolicy } from './hooks/use_policy'; import { validate } from './validation'; interface SyntheticsPolicyEditExtensionProps { @@ -47,50 +29,9 @@ export const SyntheticsPolicyEditExtension = memo { useTrackPageview({ app: 'fleet', path: 'syntheticsEdit' }); useTrackPageview({ app: 'fleet', path: 'syntheticsEdit', delay: 15000 }); - const { monitorType, isTLSEnabled, isZipUrlTLSEnabled } = usePolicyConfigContext(); - const { fields: httpSimpleFields } = useHTTPSimpleFieldsContext(); - const { fields: tcpSimpleFields } = useTCPSimpleFieldsContext(); - const { fields: icmpSimpleFields } = useICMPSimpleFieldsContext(); - const { fields: httpAdvancedFields } = useHTTPAdvancedFieldsContext(); - const { fields: tcpAdvancedFields } = useTCPAdvancedFieldsContext(); - const { fields: tlsFields } = useTLSFieldsContext(); - const { fields: browserSimpleFields } = useBrowserSimpleFieldsContext(); - const { fields: browserAdvancedFields } = useBrowserAdvancedFieldsContext(); - const metadata = useMemo( - () => ({ - is_tls_enabled: isTLSEnabled, - is_zip_url_tls_enabled: isZipUrlTLSEnabled, - }), - [isTLSEnabled, isZipUrlTLSEnabled] - ); - - const policyConfig: PolicyConfig = { - [DataStream.HTTP]: { - ...httpSimpleFields, - ...httpAdvancedFields, - ...tlsFields, - [ConfigKeys.METADATA]: metadata, - [ConfigKeys.NAME]: newPolicy.name, - } as HTTPFields, - [DataStream.TCP]: { - ...tcpSimpleFields, - ...tcpAdvancedFields, - ...tlsFields, - [ConfigKeys.METADATA]: metadata, - [ConfigKeys.NAME]: newPolicy.name, - } as TCPFields, - [DataStream.ICMP]: { - ...icmpSimpleFields, - [ConfigKeys.NAME]: newPolicy.name, - } as ICMPFields, - [DataStream.BROWSER]: { - ...browserSimpleFields, - ...browserAdvancedFields, - [ConfigKeys.METADATA]: metadata, - [ConfigKeys.NAME]: newPolicy.name, - } as BrowserFields, - }; + const { monitorType } = usePolicyConfigContext(); + const policyConfig: PolicyConfig = usePolicy(newPolicy.name); useUpdatePolicy({ defaultConfig, diff --git a/x-pack/plugins/uptime/public/components/fleet_package/synthetics_policy_edit_extension_wrapper.test.tsx b/x-pack/plugins/uptime/public/components/fleet_package/synthetics_policy_edit_extension_wrapper.test.tsx index e874ca73d951b..c141e33ea1595 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/synthetics_policy_edit_extension_wrapper.test.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/synthetics_policy_edit_extension_wrapper.test.tsx @@ -1075,6 +1075,59 @@ describe('', () => { expect(queryByLabelText('Host')).not.toBeInTheDocument(); }); + it.each([ + [true, 'Testing script'], + [false, 'Inline script'], + ])( + 'browser monitors - auto selects the right tab depending on source metadata', + async (isGeneratedScript, text) => { + const currentPolicy = { + ...defaultCurrentPolicy, + inputs: [ + { + ...defaultNewPolicy.inputs[0], + enabled: false, + }, + { + ...defaultNewPolicy.inputs[1], + enabled: false, + }, + { + ...defaultNewPolicy.inputs[2], + enabled: false, + }, + { + ...defaultNewPolicy.inputs[3], + enabled: true, + streams: [ + { + ...defaultNewPolicy.inputs[3].streams[0], + vars: { + ...defaultNewPolicy.inputs[3].streams[0].vars, + 'source.inline.script': { + type: 'yaml', + value: JSON.stringify('step(() => {})'), + }, + __ui: { + type: 'yaml', + value: JSON.stringify({ + script_source: { + is_generated_script: isGeneratedScript, + }, + }), + }, + }, + }, + ], + }, + ], + }; + + const { getByText } = render(); + + expect(getByText(text)).toBeInTheDocument(); + } + ); it('hides tls fields when metadata.is_tls_enabled is false', async () => { const { getByLabelText, queryByLabelText } = render( diff --git a/x-pack/plugins/uptime/public/components/fleet_package/types.tsx b/x-pack/plugins/uptime/public/components/fleet_package/types.tsx index f391c6c271f69..13296ee4521cd 100644 --- a/x-pack/plugins/uptime/public/components/fleet_package/types.tsx +++ b/x-pack/plugins/uptime/public/components/fleet_package/types.tsx @@ -129,6 +129,10 @@ export enum ConfigKeys { export interface Metadata { is_tls_enabled?: boolean; is_zip_url_tls_enabled?: boolean; + script_source?: { + is_generated_script: boolean; + file_name: string; + }; } export interface ICommonFields { From 473cabcef53dc7b3d8b9f5c8302c300ab8504ea5 Mon Sep 17 00:00:00 2001 From: Tyler Smalley Date: Tue, 19 Oct 2021 21:37:50 -0700 Subject: [PATCH 105/204] skip flaky suite (#115666) --- x-pack/test/accessibility/apps/ml.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/accessibility/apps/ml.ts b/x-pack/test/accessibility/apps/ml.ts index 4babe0bd6ff88..e06661b000203 100644 --- a/x-pack/test/accessibility/apps/ml.ts +++ b/x-pack/test/accessibility/apps/ml.ts @@ -13,7 +13,8 @@ export default function ({ getService }: FtrProviderContext) { const a11y = getService('a11y'); const ml = getService('ml'); - describe('ml', () => { + // Failing: See https://github.com/elastic/kibana/issues/115666 + describe.skip('ml', () => { const esArchiver = getService('esArchiver'); before(async () => { From a01165ab30ed00ab9d6de8097a1429fab30fcec2 Mon Sep 17 00:00:00 2001 From: Frank Hassanabad Date: Tue, 19 Oct 2021 22:42:35 -0600 Subject: [PATCH 106/204] [Security Solutions] Fixes 11 different flakey FTR/e2e tests and scenarios (#115688) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Fixes flakes across tests that have either been skipped or have been a source of flake in the categories of: * Sorting fixes because Elasticsearch can return hits/arrays back in different orders * Flat array fixes because Elasticsearch can sometimes return `[]` or `[[]]` in-deterministically in some cases 🤷 , so we just flatten the array out completely and test for `[]` within those tests. * `waitForSignalsToBePresent` was missing in a test and sometimes we would get an empty array response which would fail CI. Also I audited other tests for `[[]]` and `waitForSignalsToBePresent` and fixed them where they were present or if the `waitForSignalsToBePresent` count was incorrect. This should give us more stability when the CI is under pressure. Sorting fixes: https://github.com/elastic/kibana/issues/115554 https://github.com/elastic/kibana/issues/115321 https://github.com/elastic/kibana/issues/115319 https://github.com/elastic/kibana/issues/114581 Flat array fixes: https://github.com/elastic/kibana/issues/89052 https://github.com/elastic/kibana/issues/115315 https://github.com/elastic/kibana/issues/115308 https://github.com/elastic/kibana/issues/115304 https://github.com/elastic/kibana/issues/115313 https://github.com/elastic/kibana/issues/113418 Missing additional check for "waitForSignalsToBePresent" or incorrect number of signals to wait for fixes: https://github.com/elastic/kibana/issues/115310 ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --- .../security_and_spaces/tests/aliases.ts | 10 +- .../tests/create_endpoint_exceptions.ts | 188 ++++++++++-------- .../exception_operators_data_types/float.ts | 2 +- .../exception_operators_data_types/integer.ts | 2 +- .../ip_array.ts | 22 +- .../keyword_array.ts | 22 +- .../exception_operators_data_types/long.ts | 2 +- .../exception_operators_data_types/text.ts | 16 +- .../text_array.ts | 20 +- 9 files changed, 150 insertions(+), 134 deletions(-) diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/aliases.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/aliases.ts index 9b7c75bab3100..3c033d2077c54 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/aliases.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/aliases.ts @@ -51,9 +51,9 @@ export default ({ getService }: FtrProviderContext) => { await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); - const hits = signalsOpen.hits.hits.map( - (signal) => (signal._source?.host_alias as HostAlias).name - ); + const hits = signalsOpen.hits.hits + .map((signal) => (signal._source?.host_alias as HostAlias).name) + .sort(); expect(hits).to.eql(['host name 1', 'host name 2', 'host name 3', 'host name 4']); }); @@ -63,7 +63,9 @@ export default ({ getService }: FtrProviderContext) => { await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); - const hits = signalsOpen.hits.hits.map((signal) => (signal._source?.host as HostAlias).name); + const hits = signalsOpen.hits.hits + .map((signal) => (signal._source?.host as HostAlias).name) + .sort(); expect(hits).to.eql(['host name 1', 'host name 2', 'host name 3', 'host name 4']); }); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_endpoint_exceptions.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_endpoint_exceptions.ts index b0f208aadaf1b..6d04ffc67c573 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_endpoint_exceptions.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_endpoint_exceptions.ts @@ -7,6 +7,7 @@ import expect from '@kbn/expect'; +import type SuperTest from 'supertest'; import { createListsIndex, deleteAllExceptions, @@ -25,6 +26,45 @@ import { waitForSignalsToBePresent, } from '../../utils'; +interface Host { + os: { + type?: string; + name?: string; + }; +} + +/** + * Convenience method to get signals by host and sort them for better deterministic testing + * since Elastic can return the hits back in any order we want to sort them on return for testing. + * @param supertest Super test for testing. + * @param id The signals id + * @returns The array of hosts sorted + */ +export const getHostHits = async ( + supertest: SuperTest.SuperTest, + id: string +): Promise => { + const signalsOpen = await getSignalsById(supertest, id); + return signalsOpen.hits.hits + .map((hit) => hit._source?.host as Host) + .sort((a, b) => { + let sortOrder = 0; + if (a.os.name != null && b.os.name != null) { + sortOrder += a.os.name.localeCompare(b.os.name); + } + if (a.os.type != null && b.os.type != null) { + sortOrder += a.os.type.localeCompare(b.os.type); + } + if (a.os.type != null && b.os.name != null) { + sortOrder += a.os.type.localeCompare(b.os.name); + } + if (a.os.name != null && b.os.type != null) { + sortOrder += a.os.name.localeCompare(b.os.type); + } + return sortOrder; + }); +}; + // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); @@ -64,20 +104,19 @@ export default ({ getService }: FtrProviderContext) => { const { id } = await createRule(supertest, rule); await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); - const signalsOpen = await getSignalsById(supertest, id); - const hits = signalsOpen.hits.hits.map((hit) => hit._source?.host).sort(); + const hits = await getHostHits(supertest, id); expect(hits).to.eql([ { os: { type: 'linux' }, }, { - os: { type: 'windows' }, + os: { type: 'linux' }, }, { os: { type: 'macos' }, }, { - os: { type: 'linux' }, + os: { type: 'windows' }, }, ]); }); @@ -87,20 +126,19 @@ export default ({ getService }: FtrProviderContext) => { const { id } = await createRule(supertest, rule); await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); - const signalsOpen = await getSignalsById(supertest, id); - const hits = signalsOpen.hits.hits.map((hit) => hit._source?.host).sort(); + const hits = await getHostHits(supertest, id); expect(hits).to.eql([ { os: { name: 'Linux' }, }, { - os: { name: 'Windows' }, + os: { name: 'Linux' }, }, { os: { name: 'Macos' }, }, { - os: { name: 'Linux' }, + os: { name: 'Windows' }, }, ]); }); @@ -130,17 +168,16 @@ export default ({ getService }: FtrProviderContext) => { ); await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); - const signalsOpen = await getSignalsById(supertest, id); - const hits = signalsOpen.hits.hits.map((hit) => hit._source?.host); + const hits = await getHostHits(supertest, id); expect(hits).to.eql([ { - os: { name: 'Windows' }, + os: { name: 'Linux' }, }, { os: { name: 'Macos' }, }, { - os: { name: 'Linux' }, + os: { name: 'Windows' }, }, ]); }); @@ -167,17 +204,16 @@ export default ({ getService }: FtrProviderContext) => { ); await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); - const signalsOpen = await getSignalsById(supertest, id); - const hits = signalsOpen.hits.hits.map((hit) => hit._source?.host); + const hits = await getHostHits(supertest, id); expect(hits).to.eql([ { - os: { name: 'Windows' }, + os: { name: 'Linux' }, }, { os: { name: 'Macos' }, }, { - os: { name: 'Linux' }, + os: { name: 'Windows' }, }, ]); }); @@ -215,14 +251,13 @@ export default ({ getService }: FtrProviderContext) => { ); await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); - const signalsOpen = await getSignalsById(supertest, id); - const hits = signalsOpen.hits.hits.map((hit) => hit._source?.host); + const hits = await getHostHits(supertest, id); expect(hits).to.eql([ { - os: { name: 'Macos' }, + os: { name: 'Linux' }, }, { - os: { name: 'Linux' }, + os: { name: 'Macos' }, }, ]); }); @@ -260,14 +295,13 @@ export default ({ getService }: FtrProviderContext) => { ); await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); - const signalsOpen = await getSignalsById(supertest, id); - const hits = signalsOpen.hits.hits.map((hit) => hit._source?.host); + const hits = await getHostHits(supertest, id); expect(hits).to.eql([ { - os: { name: 'Macos' }, + os: { name: 'Linux' }, }, { - os: { name: 'Linux' }, + os: { name: 'Macos' }, }, ]); }); @@ -296,17 +330,16 @@ export default ({ getService }: FtrProviderContext) => { ); await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); - const signalsOpen = await getSignalsById(supertest, id); - const hits = signalsOpen.hits.hits.map((hit) => hit._source?.host); + const hits = await getHostHits(supertest, id); expect(hits).to.eql([ { - os: { type: 'windows' }, + os: { type: 'linux' }, }, { os: { type: 'macos' }, }, { - os: { type: 'linux' }, + os: { type: 'windows' }, }, ]); }); @@ -333,17 +366,16 @@ export default ({ getService }: FtrProviderContext) => { ); await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); - const signalsOpen = await getSignalsById(supertest, id); - const hits = signalsOpen.hits.hits.map((hit) => hit._source?.host); + const hits = await getHostHits(supertest, id); expect(hits).to.eql([ { - os: { type: 'windows' }, + os: { type: 'linux' }, }, { os: { type: 'macos' }, }, { - os: { type: 'linux' }, + os: { type: 'windows' }, }, ]); }); @@ -381,14 +413,13 @@ export default ({ getService }: FtrProviderContext) => { ); await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); - const signalsOpen = await getSignalsById(supertest, id); - const hits = signalsOpen.hits.hits.map((hit) => hit._source?.host); + const hits = await getHostHits(supertest, id); expect(hits).to.eql([ { - os: { type: 'macos' }, + os: { type: 'linux' }, }, { - os: { type: 'linux' }, + os: { type: 'macos' }, }, ]); }); @@ -426,14 +457,13 @@ export default ({ getService }: FtrProviderContext) => { ); await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); - const signalsOpen = await getSignalsById(supertest, id); - const hits = signalsOpen.hits.hits.map((hit) => hit._source?.host); + const hits = await getHostHits(supertest, id); expect(hits).to.eql([ { - os: { type: 'macos' }, + os: { type: 'linux' }, }, { - os: { type: 'linux' }, + os: { type: 'macos' }, }, ]); }); @@ -462,14 +492,13 @@ export default ({ getService }: FtrProviderContext) => { ); await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 6, [id]); - const signalsOpen = await getSignalsById(supertest, id); - const hits = signalsOpen.hits.hits.map((hit) => hit._source?.host); + const hits = await getHostHits(supertest, id); expect(hits).to.eql([ { - os: { type: 'windows' }, + os: { type: 'linux' }, }, { - os: { name: 'Windows' }, + os: { name: 'Linux' }, }, { os: { type: 'macos' }, @@ -478,10 +507,10 @@ export default ({ getService }: FtrProviderContext) => { os: { name: 'Macos' }, }, { - os: { type: 'linux' }, + os: { type: 'windows' }, }, { - os: { name: 'Linux' }, + os: { name: 'Windows' }, }, ]); }); @@ -508,14 +537,13 @@ export default ({ getService }: FtrProviderContext) => { ); await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 6, [id]); - const signalsOpen = await getSignalsById(supertest, id); - const hits = signalsOpen.hits.hits.map((hit) => hit._source?.host); + const hits = await getHostHits(supertest, id); expect(hits).to.eql([ { - os: { type: 'windows' }, + os: { type: 'linux' }, }, { - os: { name: 'Windows' }, + os: { name: 'Linux' }, }, { os: { type: 'macos' }, @@ -524,10 +552,10 @@ export default ({ getService }: FtrProviderContext) => { os: { name: 'Macos' }, }, { - os: { type: 'linux' }, + os: { type: 'windows' }, }, { - os: { name: 'Linux' }, + os: { name: 'Windows' }, }, ]); }); @@ -565,20 +593,19 @@ export default ({ getService }: FtrProviderContext) => { ); await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); - const signalsOpen = await getSignalsById(supertest, id); - const hits = signalsOpen.hits.hits.map((hit) => hit._source?.host); + const hits = await getHostHits(supertest, id); expect(hits).to.eql([ { - os: { type: 'macos' }, + os: { type: 'linux' }, }, { - os: { name: 'Macos' }, + os: { name: 'Linux' }, }, { - os: { type: 'linux' }, + os: { type: 'macos' }, }, { - os: { name: 'Linux' }, + os: { name: 'Macos' }, }, ]); }); @@ -616,20 +643,19 @@ export default ({ getService }: FtrProviderContext) => { ); await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); - const signalsOpen = await getSignalsById(supertest, id); - const hits = signalsOpen.hits.hits.map((hit) => hit._source?.host); + const hits = await getHostHits(supertest, id); expect(hits).to.eql([ { - os: { type: 'macos' }, + os: { type: 'linux' }, }, { - os: { name: 'Macos' }, + os: { name: 'Linux' }, }, { - os: { type: 'linux' }, + os: { type: 'macos' }, }, { - os: { name: 'Linux' }, + os: { name: 'Macos' }, }, ]); }); @@ -668,8 +694,7 @@ export default ({ getService }: FtrProviderContext) => { ); await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); - const signalsOpen = await getSignalsById(supertest, id); - const hits = signalsOpen.hits.hits.map((hit) => hit._source?.host); + const hits = await getHostHits(supertest, id); expect(hits).to.eql([ { os: { type: 'macos' }, @@ -708,8 +733,7 @@ export default ({ getService }: FtrProviderContext) => { ); await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 1, [id]); - const signalsOpen = await getSignalsById(supertest, id); - const hits = signalsOpen.hits.hits.map((hit) => hit._source?.host); + const hits = await getHostHits(supertest, id); expect(hits).to.eql([ { os: { type: 'macos' }, @@ -741,17 +765,16 @@ export default ({ getService }: FtrProviderContext) => { ); await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 3, [id]); - const signalsOpen = await getSignalsById(supertest, id); - const hits = signalsOpen.hits.hits.map((hit) => hit._source?.host); + const hits = await getHostHits(supertest, id); expect(hits).to.eql([ { os: { type: 'linux' }, }, { - os: { type: 'macos' }, + os: { type: 'linux' }, }, { - os: { type: 'linux' }, + os: { type: 'macos' }, }, ]); }); @@ -778,14 +801,13 @@ export default ({ getService }: FtrProviderContext) => { ); await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); - const signalsOpen = await getSignalsById(supertest, id); - const hits = signalsOpen.hits.hits.map((hit) => hit._source?.host); + const hits = await getHostHits(supertest, id); expect(hits).to.eql([ { - os: { type: 'macos' }, + os: { type: 'linux' }, }, { - os: { type: 'linux' }, + os: { type: 'macos' }, }, ]); }); @@ -812,14 +834,13 @@ export default ({ getService }: FtrProviderContext) => { ); await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 2, [id]); - const signalsOpen = await getSignalsById(supertest, id); - const hits = signalsOpen.hits.hits.map((hit) => hit._source?.host); + const hits = await getHostHits(supertest, id); expect(hits).to.eql([ { - os: { type: 'macos' }, + os: { type: 'linux' }, }, { - os: { type: 'linux' }, + os: { type: 'macos' }, }, ]); }); @@ -846,20 +867,19 @@ export default ({ getService }: FtrProviderContext) => { ); await waitForRuleSuccessOrStatus(supertest, id); await waitForSignalsToBePresent(supertest, 4, [id]); - const signalsOpen = await getSignalsById(supertest, id); - const hits = signalsOpen.hits.hits.map((hit) => hit._source?.host); + const hits = await getHostHits(supertest, id); expect(hits).to.eql([ { os: { type: 'linux' }, }, { - os: { type: 'windows' }, + os: { type: 'linux' }, }, { os: { type: 'macos' }, }, { - os: { type: 'linux' }, + os: { type: 'windows' }, }, ]); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/float.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/float.ts index f7208a8832c4d..912596ed7ca00 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/float.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/float.ts @@ -499,7 +499,7 @@ export default ({ getService }: FtrProviderContext) => { ], ]); await waitForRuleSuccessOrStatus(supertest, id); - await waitForSignalsToBePresent(supertest, 1, [id]); + await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source?.float).sort(); expect(hits).to.eql(['1.1', '1.2', '1.3']); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/integer.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/integer.ts index 42152fd18473a..da9219e4b52f6 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/integer.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/integer.ts @@ -501,7 +501,7 @@ export default ({ getService }: FtrProviderContext) => { ], ]); await waitForRuleSuccessOrStatus(supertest, id); - await waitForSignalsToBePresent(supertest, 1, [id]); + await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source?.integer).sort(); expect(hits).to.eql(['2', '3', '4']); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip_array.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip_array.ts index 147e6058dffa8..526c6d1c988ce 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip_array.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip_array.ts @@ -151,7 +151,7 @@ export default ({ getService }: FtrProviderContext) => { await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source?.ip).sort(); - expect(ips).to.eql([[]]); + expect(ips.flat(Number.MAX_SAFE_INTEGER)).to.eql([]); }); it('should filter a CIDR range of "127.0.0.1/30"', async () => { @@ -167,7 +167,7 @@ export default ({ getService }: FtrProviderContext) => { ], ]); await waitForRuleSuccessOrStatus(supertest, id); - await waitForSignalsToBePresent(supertest, 1, [id]); + await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source?.ip).sort(); expect(ips).to.eql([ @@ -190,7 +190,7 @@ export default ({ getService }: FtrProviderContext) => { ], ]); await waitForRuleSuccessOrStatus(supertest, id); - await waitForSignalsToBePresent(supertest, 1, [id]); + await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source?.ip).sort(); expect(ips).to.eql([[], ['127.0.0.8', '127.0.0.9', '127.0.0.10']]); @@ -346,7 +346,7 @@ export default ({ getService }: FtrProviderContext) => { await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source?.ip).sort(); - expect(ips).to.eql([[]]); + expect(ips.flat(Number.MAX_SAFE_INTEGER)).to.eql([]); }); }); @@ -392,8 +392,7 @@ export default ({ getService }: FtrProviderContext) => { }); }); - // FLAKY: https://github.com/elastic/kibana/issues/115315 - describe.skip('"exists" operator', () => { + describe('"exists" operator', () => { it('will return 1 empty result if matching against ip', async () => { const rule = getRuleForSignalTesting(['ip_as_array']); const { id } = await createRuleWithExceptionEntries(supertest, rule, [ @@ -408,7 +407,7 @@ export default ({ getService }: FtrProviderContext) => { await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source?.ip).sort(); - expect(ips).to.eql([[]]); + expect(ips.flat(Number.MAX_SAFE_INTEGER)).to.eql([]); }); }); @@ -487,8 +486,7 @@ export default ({ getService }: FtrProviderContext) => { expect(ips).to.eql([[], ['127.0.0.8', '127.0.0.9', '127.0.0.10']]); }); - // FLAKY https://github.com/elastic/kibana/issues/89052 - it.skip('will return 1 result if we have a list that includes all ips', async () => { + it('will return 1 result if we have a list that includes all ips', async () => { await importFile( supertest, 'ip', @@ -512,7 +510,7 @@ export default ({ getService }: FtrProviderContext) => { await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source?.ip).sort(); - expect(ips).to.eql([[]]); + expect(ips.flat(Number.MAX_SAFE_INTEGER)).to.eql([]); }); it('will return 2 results if we have a list which contains the CIDR ranges of "127.0.0.1/32, 127.0.0.2/31, 127.0.0.4/30"', async () => { @@ -546,7 +544,7 @@ export default ({ getService }: FtrProviderContext) => { ], ]); await waitForRuleSuccessOrStatus(supertest, id); - await waitForSignalsToBePresent(supertest, 1, [id]); + await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source?.ip).sort(); expect(ips).to.eql([[], ['127.0.0.8', '127.0.0.9', '127.0.0.10']]); @@ -577,7 +575,7 @@ export default ({ getService }: FtrProviderContext) => { ], ]); await waitForRuleSuccessOrStatus(supertest, id); - await waitForSignalsToBePresent(supertest, 1, [id]); + await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source?.ip).sort(); expect(ips).to.eql([[], ['127.0.0.8', '127.0.0.9', '127.0.0.10']]); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/keyword_array.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/keyword_array.ts index e852558aaa6a8..8571aa8eeaa60 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/keyword_array.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/keyword_array.ts @@ -60,7 +60,7 @@ export default ({ getService }: FtrProviderContext) => { const rule = getRuleForSignalTesting(['keyword_as_array']); const { id } = await createRule(supertest, rule); await waitForRuleSuccessOrStatus(supertest, id); - await waitForSignalsToBePresent(supertest, 3, [id]); + await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source?.keyword).sort(); expect(hits).to.eql([ @@ -84,7 +84,7 @@ export default ({ getService }: FtrProviderContext) => { ], ]); await waitForRuleSuccessOrStatus(supertest, id); - await waitForSignalsToBePresent(supertest, 2, [id]); + await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source?.keyword).sort(); expect(hits).to.eql([ @@ -153,7 +153,7 @@ export default ({ getService }: FtrProviderContext) => { await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source?.keyword).sort(); - expect(hits).to.eql([[]]); + expect(hits.flat(Number.MAX_SAFE_INTEGER)).to.eql([]); }); }); @@ -281,7 +281,7 @@ export default ({ getService }: FtrProviderContext) => { await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source?.keyword).sort(); - expect(hits).to.eql([[]]); + expect(hits.flat(Number.MAX_SAFE_INTEGER)).to.eql([]); }); }); @@ -328,8 +328,7 @@ export default ({ getService }: FtrProviderContext) => { }); describe('"exists" operator', () => { - // FLAKY https://github.com/elastic/kibana/issues/115308 - it.skip('will return 1 results if matching against keyword for the empty array', async () => { + it('will return 1 results if matching against keyword for the empty array', async () => { const rule = getRuleForSignalTesting(['keyword_as_array']); const { id } = await createRuleWithExceptionEntries(supertest, rule, [ [ @@ -343,7 +342,7 @@ export default ({ getService }: FtrProviderContext) => { await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source?.keyword).sort(); - expect(hits).to.eql([[]]); + expect(hits.flat(Number.MAX_SAFE_INTEGER)).to.eql([]); }); }); @@ -399,7 +398,7 @@ export default ({ getService }: FtrProviderContext) => { ], ]); await waitForRuleSuccessOrStatus(supertest, id); - await waitForSignalsToBePresent(supertest, 3, [id]); + await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source?.keyword).sort(); expect(hits).to.eql([ @@ -437,7 +436,7 @@ export default ({ getService }: FtrProviderContext) => { ], ]); await waitForRuleSuccessOrStatus(supertest, id); - await waitForSignalsToBePresent(supertest, 2, [id]); + await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source?.keyword).sort(); expect(hits).to.eql([ @@ -497,8 +496,7 @@ export default ({ getService }: FtrProviderContext) => { expect(hits).to.eql([[], ['word eight', 'word nine', 'word ten']]); }); - // FLAKY https://github.com/elastic/kibana/issues/115304 - it.skip('will return only the empty array for results if we have a list that includes all keyword', async () => { + it('will return only the empty array for results if we have a list that includes all keyword', async () => { await importFile( supertest, 'keyword', @@ -522,7 +520,7 @@ export default ({ getService }: FtrProviderContext) => { await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source?.keyword).sort(); - expect(hits).to.eql([[]]); + expect(hits.flat(Number.MAX_SAFE_INTEGER)).to.eql([]); }); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/long.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/long.ts index 35573edea3c39..8d5f1515e4ab6 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/long.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/long.ts @@ -499,7 +499,7 @@ export default ({ getService }: FtrProviderContext) => { ], ]); await waitForRuleSuccessOrStatus(supertest, id); - await waitForSignalsToBePresent(supertest, 1, [id]); + await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source?.long).sort(); expect(hits).to.eql(['2', '3', '4']); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text.ts index 4e4823fcf747f..367e68f7f9ed1 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text.ts @@ -56,8 +56,7 @@ export default ({ getService }: FtrProviderContext) => { await deleteListsIndex(supertest); }); - // FLAKY: https://github.com/elastic/kibana/issues/115310 - describe.skip('"is" operator', () => { + describe('"is" operator', () => { it('should find all the text from the data set when no exceptions are set on the rule', async () => { const rule = getRuleForSignalTesting(['text']); const { id } = await createRule(supertest, rule); @@ -241,7 +240,7 @@ export default ({ getService }: FtrProviderContext) => { ], ]); await waitForRuleSuccessOrStatus(supertest, id); - await waitForSignalsToBePresent(supertest, 1, [id]); + await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source?.text).sort(); expect(hits).to.eql(['word four', 'word three', 'word two']); @@ -344,6 +343,7 @@ export default ({ getService }: FtrProviderContext) => { ], ]); await waitForRuleSuccessOrStatus(supertest, id); + await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source?.text).sort(); expect(hits).to.eql(['word four', 'word one', 'word three', 'word two']); @@ -618,7 +618,7 @@ export default ({ getService }: FtrProviderContext) => { ], ]); await waitForRuleSuccessOrStatus(supertest, id); - await waitForSignalsToBePresent(supertest, 1, [id]); + await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source?.text).sort(); expect(hits).to.eql(['word four', 'word three', 'word two']); @@ -646,7 +646,7 @@ export default ({ getService }: FtrProviderContext) => { ], ]); await waitForRuleSuccessOrStatus(supertest, id); - await waitForSignalsToBePresent(supertest, 1, [id]); + await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source?.text).sort(); expect(hits).to.eql(['word four', 'word three', 'word two']); @@ -669,7 +669,7 @@ export default ({ getService }: FtrProviderContext) => { ], ]); await waitForRuleSuccessOrStatus(supertest, id); - await waitForSignalsToBePresent(supertest, 1, [id]); + await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source?.text).sort(); expect(hits).to.eql(['word four', 'word two']); @@ -850,7 +850,7 @@ export default ({ getService }: FtrProviderContext) => { ], ]); await waitForRuleSuccessOrStatus(supertest, id); - await waitForSignalsToBePresent(supertest, 1, [id]); + await waitForSignalsToBePresent(supertest, 2, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source?.text).sort(); expect(hits).to.eql(['word one', 'word three']); @@ -878,7 +878,7 @@ export default ({ getService }: FtrProviderContext) => { ], ]); await waitForRuleSuccessOrStatus(supertest, id); - await waitForSignalsToBePresent(supertest, 1, [id]); + await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source?.text).sort(); expect(hits).to.eql(['word four', 'word one', 'word three', 'word two']); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text_array.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text_array.ts index f0a5fe7c1ffb1..3eedabd41d663 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text_array.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text_array.ts @@ -58,7 +58,7 @@ export default ({ getService }: FtrProviderContext) => { const rule = getRuleForSignalTesting(['text_as_array']); const { id } = await createRule(supertest, rule); await waitForRuleSuccessOrStatus(supertest, id); - await waitForSignalsToBePresent(supertest, 3, [id]); + await waitForSignalsToBePresent(supertest, 4, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source?.text).sort(); expect(hits).to.eql([ @@ -82,7 +82,7 @@ export default ({ getService }: FtrProviderContext) => { ], ]); await waitForRuleSuccessOrStatus(supertest, id); - await waitForSignalsToBePresent(supertest, 2, [id]); + await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source?.text).sort(); expect(hits).to.eql([ @@ -151,7 +151,7 @@ export default ({ getService }: FtrProviderContext) => { await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source?.text).sort(); - expect(hits).to.eql([[]]); + expect(hits.flat(Number.MAX_SAFE_INTEGER)).to.eql([]); }); }); @@ -279,7 +279,7 @@ export default ({ getService }: FtrProviderContext) => { await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source?.text).sort(); - expect(hits).to.eql([[]]); + expect(hits.flat(Number.MAX_SAFE_INTEGER)).to.eql([]); }); }); @@ -326,8 +326,7 @@ export default ({ getService }: FtrProviderContext) => { }); describe('"exists" operator', () => { - // FLAKY https://github.com/elastic/kibana/issues/115313 - it.skip('will return 1 results if matching against text for the empty array', async () => { + it('will return 1 results if matching against text for the empty array', async () => { const rule = getRuleForSignalTesting(['text_as_array']); const { id } = await createRuleWithExceptionEntries(supertest, rule, [ [ @@ -341,7 +340,7 @@ export default ({ getService }: FtrProviderContext) => { await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source?.text).sort(); - expect(hits).to.eql([[]]); + expect(hits.flat(Number.MAX_SAFE_INTEGER)).to.eql([]); }); }); @@ -435,7 +434,7 @@ export default ({ getService }: FtrProviderContext) => { ], ]); await waitForRuleSuccessOrStatus(supertest, id); - await waitForSignalsToBePresent(supertest, 2, [id]); + await waitForSignalsToBePresent(supertest, 3, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source?.text).sort(); expect(hits).to.eql([ @@ -495,8 +494,7 @@ export default ({ getService }: FtrProviderContext) => { expect(hits).to.eql([[], ['word eight', 'word nine', 'word ten']]); }); - // FLAKY https://github.com/elastic/kibana/issues/113418 - it.skip('will return only the empty array for results if we have a list that includes all text', async () => { + it('will return only the empty array for results if we have a list that includes all text', async () => { await importFile( supertest, 'text', @@ -520,7 +518,7 @@ export default ({ getService }: FtrProviderContext) => { await waitForRuleSuccessOrStatus(supertest, id); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source?.text).sort(); - expect(hits).to.eql([[]]); + expect(hits.flat(Number.MAX_SAFE_INTEGER)).to.eql([]); }); }); From 6a2b7fe3d3bc41e30de977f946fd009a8fa1f0d2 Mon Sep 17 00:00:00 2001 From: Yara Tercero Date: Tue, 19 Oct 2021 22:17:08 -0700 Subject: [PATCH 107/204] [Security Solution][Platform] - Export exceptions with rule (#115144) ### Summary Introduces exports of exception lists with rules. Import of exception lists not yet supported. --- x-pack/plugins/lists/server/index.ts | 1 + .../routes/export_exception_list_route.ts | 63 +++-------- .../exception_list_client.mock.ts | 11 ++ .../exception_lists/exception_list_client.ts | 20 ++++ .../exception_list_client_types.ts | 18 ++++ .../export_exception_list_and_items.test.ts | 62 +++++++++++ .../export_exception_list_and_items.ts | 93 ++++++++++++++++ .../server/services/exception_lists/index.ts | 1 + .../downloads/test_exception_list.ndjson | 2 - .../cypress/objects/exception.ts | 2 +- .../security_solution/cypress/objects/rule.ts | 2 +- .../routes/rules/export_rules_route.ts | 16 ++- .../routes/rules/perform_bulk_action_route.ts | 4 +- .../create_rules_stream_from_ndjson.test.ts | 2 +- .../rules/create_rules_stream_from_ndjson.ts | 6 +- .../rules/get_export_all.test.ts | 17 ++- .../detection_engine/rules/get_export_all.ts | 21 ++-- .../rules/get_export_by_object_ids.test.ts | 16 ++- .../rules/get_export_by_object_ids.ts | 18 +++- .../rules/get_export_details_ndjson.test.ts | 6 +- .../rules/get_export_details_ndjson.ts | 6 +- .../rules/get_export_rule_exceptions.test.ts | 79 ++++++++++++++ .../rules/get_export_rule_exceptions.ts | 102 ++++++++++++++++++ .../read_stream/create_stream_from_ndjson.ts | 12 +++ .../basic/tests/export_rules.ts | 8 +- .../security_and_spaces/tests/export_rules.ts | 8 +- .../tests/perform_bulk_action.ts | 8 +- .../tests/export_exception_list.ts | 2 +- 28 files changed, 523 insertions(+), 83 deletions(-) create mode 100644 x-pack/plugins/lists/server/services/exception_lists/export_exception_list_and_items.test.ts create mode 100644 x-pack/plugins/lists/server/services/exception_lists/export_exception_list_and_items.ts delete mode 100644 x-pack/plugins/security_solution/cypress/downloads/test_exception_list.ndjson create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_rule_exceptions.test.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_rule_exceptions.ts diff --git a/x-pack/plugins/lists/server/index.ts b/x-pack/plugins/lists/server/index.ts index 772a8cbe7ec35..9f395cb0d94bc 100644 --- a/x-pack/plugins/lists/server/index.ts +++ b/x-pack/plugins/lists/server/index.ts @@ -18,6 +18,7 @@ export { } from './services/exception_lists/exception_list_client_types'; export { ExceptionListClient } from './services/exception_lists/exception_list_client'; export type { ListPluginSetup, ListsApiRequestHandlerContext } from './types'; +export type { ExportExceptionListAndItemsReturn } from './services/exception_lists/export_exception_list_and_items'; export const config: PluginConfigDescriptor = { schema: ConfigSchema, diff --git a/x-pack/plugins/lists/server/routes/export_exception_list_route.ts b/x-pack/plugins/lists/server/routes/export_exception_list_route.ts index aa30c8a7d435d..b91537b6cb3b1 100644 --- a/x-pack/plugins/lists/server/routes/export_exception_list_route.ts +++ b/x-pack/plugins/lists/server/routes/export_exception_list_route.ts @@ -6,7 +6,6 @@ */ import { transformError } from '@kbn/securitysolution-es-utils'; -import { transformDataToNdjson } from '@kbn/securitysolution-utils'; import { exportExceptionListQuerySchema } from '@kbn/securitysolution-io-ts-list-types'; import { EXCEPTION_LIST_URL } from '@kbn/securitysolution-list-constants'; @@ -30,43 +29,28 @@ export const exportExceptionListRoute = (router: ListsPluginRouter): void => { try { const { id, list_id: listId, namespace_type: namespaceType } = request.query; - const exceptionLists = getExceptionListClient(context); - const exceptionList = await exceptionLists.getExceptionList({ + const exceptionListsClient = getExceptionListClient(context); + + const exportContent = await exceptionListsClient.exportExceptionListAndItems({ id, listId, namespaceType, }); - if (exceptionList == null) { + if (exportContent == null) { return siemResponse.error({ - body: `exception list with list_id: ${listId} does not exist`, + body: `exception list with list_id: ${listId} or id: ${id} does not exist`, statusCode: 400, }); - } else { - const listItems = await exceptionLists.findExceptionListItem({ - filter: undefined, - listId, - namespaceType, - page: 1, - perPage: 10000, - sortField: 'exception-list.created_at', - sortOrder: 'desc', - }); - const exceptionItems = listItems?.data ?? []; - - const { exportData } = getExport([exceptionList, ...exceptionItems]); - const { exportDetails } = getExportDetails(exceptionItems); - - // TODO: Allow the API to override the name of the file to export - const fileName = exceptionList.list_id; - return response.ok({ - body: `${exportData}${exportDetails}`, - headers: { - 'Content-Disposition': `attachment; filename="${fileName}"`, - 'Content-Type': 'application/ndjson', - }, - }); } + + return response.ok({ + body: `${exportContent.exportData}${JSON.stringify(exportContent.exportDetails)}\n`, + headers: { + 'Content-Disposition': `attachment; filename="${listId}"`, + 'Content-Type': 'application/ndjson', + }, + }); } catch (err) { const error = transformError(err); return siemResponse.error({ @@ -77,24 +61,3 @@ export const exportExceptionListRoute = (router: ListsPluginRouter): void => { } ); }; - -export const getExport = ( - data: unknown[] -): { - exportData: string; -} => { - const ndjson = transformDataToNdjson(data); - - return { exportData: ndjson }; -}; - -export const getExportDetails = ( - items: unknown[] -): { - exportDetails: string; -} => { - const exportDetails = JSON.stringify({ - exported_list_items_count: items.length, - }); - return { exportDetails: `${exportDetails}\n` }; -}; diff --git a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.mock.ts b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.mock.ts index 1566241e7351e..f5f6a4f1f2d5a 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.mock.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.mock.ts @@ -30,6 +30,17 @@ export class ExceptionListClientMock extends ExceptionListClient { public findExceptionList = jest.fn().mockResolvedValue(getFoundExceptionListSchemaMock()); public createTrustedAppsList = jest.fn().mockResolvedValue(getTrustedAppsListSchemaMock()); public createEndpointList = jest.fn().mockResolvedValue(getExceptionListSchemaMock()); + public exportExceptionListAndItems = jest.fn().mockResolvedValue({ + exportData: 'exportString', + exportDetails: { + exported_exception_list_count: 0, + exported_exception_list_item_count: 0, + missing_exception_list_item_count: 0, + missing_exception_list_items: [], + missing_exception_lists: [], + missing_exception_lists_count: 0, + }, + }); } export const getExceptionListClientMock = ( diff --git a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts index 77e82bf0f7578..542598fc82c90 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client.ts @@ -24,6 +24,7 @@ import { DeleteExceptionListItemByIdOptions, DeleteExceptionListItemOptions, DeleteExceptionListOptions, + ExportExceptionListAndItemsOptions, FindEndpointListItemOptions, FindExceptionListItemOptions, FindExceptionListOptions, @@ -38,6 +39,10 @@ import { UpdateExceptionListOptions, } from './exception_list_client_types'; import { getExceptionList } from './get_exception_list'; +import { + ExportExceptionListAndItemsReturn, + exportExceptionListAndItems, +} from './export_exception_list_and_items'; import { getExceptionListSummary } from './get_exception_list_summary'; import { createExceptionList } from './create_exception_list'; import { getExceptionListItem } from './get_exception_list_item'; @@ -492,4 +497,19 @@ export class ExceptionListClient { sortOrder, }); }; + + public exportExceptionListAndItems = async ({ + listId, + id, + namespaceType, + }: ExportExceptionListAndItemsOptions): Promise => { + const { savedObjectsClient } = this; + + return exportExceptionListAndItems({ + id, + listId, + namespaceType, + savedObjectsClient, + }); + }; } diff --git a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts index b734d3a7b1a3b..14de474974c11 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/exception_list_client_types.ts @@ -220,3 +220,21 @@ export interface FindExceptionListOptions { sortField: SortFieldOrUndefined; sortOrder: SortOrderOrUndefined; } + +export interface ExportExceptionListAndItemsOptions { + listId: ListIdOrUndefined; + id: IdOrUndefined; + namespaceType: NamespaceType; +} + +export interface ExportExceptionListAndItemsReturn { + exportData: string; + exportDetails: { + exported_exception_list_count: number; + exported_exception_list_item_count: number; + missing_exception_list_item_count: number; + missing_exception_list_items: string[]; + missing_exception_lists: string[]; + missing_exception_lists_count: number; + }; +} diff --git a/x-pack/plugins/lists/server/services/exception_lists/export_exception_list_and_items.test.ts b/x-pack/plugins/lists/server/services/exception_lists/export_exception_list_and_items.test.ts new file mode 100644 index 0000000000000..9f3c02fecca20 --- /dev/null +++ b/x-pack/plugins/lists/server/services/exception_lists/export_exception_list_and_items.test.ts @@ -0,0 +1,62 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { SavedObjectsClientContract } from 'kibana/server'; + +import { getExceptionListItemSchemaMock } from '../../../common/schemas/response/exception_list_item_schema.mock'; +import { getExceptionListSchemaMock } from '../../../common/schemas/response/exception_list_schema.mock'; + +import { exportExceptionListAndItems } from './export_exception_list_and_items'; +import { findExceptionListItem } from './find_exception_list_item'; +import { getExceptionList } from './get_exception_list'; + +jest.mock('./get_exception_list'); +jest.mock('./find_exception_list_item'); + +describe('export_exception_list_and_items', () => { + describe('exportExceptionListAndItems', () => { + test('it should return null if no matching exception list found', async () => { + (getExceptionList as jest.Mock).mockResolvedValue(null); + (findExceptionListItem as jest.Mock).mockResolvedValue({ data: [] }); + + const result = await exportExceptionListAndItems({ + id: '123', + listId: 'non-existent', + namespaceType: 'single', + savedObjectsClient: {} as SavedObjectsClientContract, + }); + expect(result).toBeNull(); + }); + + test('it should return stringified list and items', async () => { + (getExceptionList as jest.Mock).mockResolvedValue(getExceptionListSchemaMock()); + (findExceptionListItem as jest.Mock).mockResolvedValue({ + data: [getExceptionListItemSchemaMock()], + }); + + const result = await exportExceptionListAndItems({ + id: '123', + listId: 'non-existent', + namespaceType: 'single', + savedObjectsClient: {} as SavedObjectsClientContract, + }); + expect(result?.exportData).toEqual( + `${JSON.stringify(getExceptionListSchemaMock())}\n${JSON.stringify( + getExceptionListItemSchemaMock() + )}\n` + ); + expect(result?.exportDetails).toEqual({ + exported_exception_list_count: 1, + exported_exception_list_item_count: 1, + missing_exception_list_item_count: 0, + missing_exception_list_items: [], + missing_exception_lists: [], + missing_exception_lists_count: 0, + }); + }); + }); +}); diff --git a/x-pack/plugins/lists/server/services/exception_lists/export_exception_list_and_items.ts b/x-pack/plugins/lists/server/services/exception_lists/export_exception_list_and_items.ts new file mode 100644 index 0000000000000..46b3df4e5ac44 --- /dev/null +++ b/x-pack/plugins/lists/server/services/exception_lists/export_exception_list_and_items.ts @@ -0,0 +1,93 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { + IdOrUndefined, + ListIdOrUndefined, + NamespaceType, +} from '@kbn/securitysolution-io-ts-list-types'; +import { transformDataToNdjson } from '@kbn/securitysolution-utils'; +import { SavedObjectsClientContract } from 'kibana/server'; + +import { findExceptionListItem } from './find_exception_list_item'; +import { getExceptionList } from './get_exception_list'; + +interface ExportExceptionListAndItemsOptions { + id: IdOrUndefined; + listId: ListIdOrUndefined; + savedObjectsClient: SavedObjectsClientContract; + namespaceType: NamespaceType; +} + +export interface ExportExceptionListAndItemsReturn { + exportData: string; + exportDetails: { + exported_exception_list_count: number; + exported_exception_list_item_count: number; + missing_exception_list_item_count: number; + missing_exception_list_items: string[]; + missing_exception_lists: string[]; + missing_exception_lists_count: number; + }; +} + +export const exportExceptionListAndItems = async ({ + id, + listId, + namespaceType, + savedObjectsClient, +}: ExportExceptionListAndItemsOptions): Promise => { + const exceptionList = await getExceptionList({ + id, + listId, + namespaceType, + savedObjectsClient, + }); + + if (exceptionList == null) { + return null; + } else { + // TODO: Will need to address this when we switch over to + // using PIT, don't want it to get lost + // https://github.com/elastic/kibana/issues/103944 + const listItems = await findExceptionListItem({ + filter: undefined, + listId: exceptionList.list_id, + namespaceType: exceptionList.namespace_type, + page: 1, + perPage: 10000, + savedObjectsClient, + sortField: 'exception-list.created_at', + sortOrder: 'desc', + }); + const exceptionItems = listItems?.data ?? []; + const { exportData } = getExport([exceptionList, ...exceptionItems]); + + // TODO: Add logic for missing lists and items on errors + return { + exportData: `${exportData}`, + exportDetails: { + exported_exception_list_count: 1, + exported_exception_list_item_count: exceptionItems.length, + missing_exception_list_item_count: 0, + missing_exception_list_items: [], + missing_exception_lists: [], + missing_exception_lists_count: 0, + }, + }; + } +}; + +export const getExport = ( + data: unknown[] +): { + exportData: string; +} => { + const ndjson = transformDataToNdjson(data); + + return { exportData: ndjson }; +}; diff --git a/x-pack/plugins/lists/server/services/exception_lists/index.ts b/x-pack/plugins/lists/server/services/exception_lists/index.ts index e6a6dd7ef8c3c..fbc052936931a 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/index.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/index.ts @@ -10,6 +10,7 @@ export * from './create_exception_list_item'; export * from './delete_exception_list'; export * from './delete_exception_list_item'; export * from './delete_exception_list_items_by_list'; +export * from './export_exception_list_and_items'; export * from './find_exception_list'; export * from './find_exception_list_item'; export * from './find_exception_list_items'; diff --git a/x-pack/plugins/security_solution/cypress/downloads/test_exception_list.ndjson b/x-pack/plugins/security_solution/cypress/downloads/test_exception_list.ndjson deleted file mode 100644 index 54420eff29e0d..0000000000000 --- a/x-pack/plugins/security_solution/cypress/downloads/test_exception_list.ndjson +++ /dev/null @@ -1,2 +0,0 @@ -{"_version":"WzQyNjA0LDFd","created_at":"2021-10-14T01:30:22.034Z","created_by":"elastic","description":"Test exception list description","id":"4c65a230-2c8e-11ec-be1c-2bbdec602f88","immutable":false,"list_id":"test_exception_list","name":"Test exception list","namespace_type":"single","os_types":[],"tags":[],"tie_breaker_id":"b04983b4-1617-441c-bb6c-c729281fa2e9","type":"detection","updated_at":"2021-10-14T01:30:22.036Z","updated_by":"elastic","version":1} -{"exported_list_items_count":0} diff --git a/x-pack/plugins/security_solution/cypress/objects/exception.ts b/x-pack/plugins/security_solution/cypress/objects/exception.ts index b772924697148..1a70bb1038320 100644 --- a/x-pack/plugins/security_solution/cypress/objects/exception.ts +++ b/x-pack/plugins/security_solution/cypress/objects/exception.ts @@ -41,5 +41,5 @@ export const expectedExportedExceptionList = ( exceptionListResponse: Cypress.Response ): string => { const jsonrule = exceptionListResponse.body; - return `{"_version":"${jsonrule._version}","created_at":"${jsonrule.created_at}","created_by":"elastic","description":"${jsonrule.description}","id":"${jsonrule.id}","immutable":false,"list_id":"test_exception_list","name":"Test exception list","namespace_type":"single","os_types":[],"tags":[],"tie_breaker_id":"${jsonrule.tie_breaker_id}","type":"detection","updated_at":"${jsonrule.updated_at}","updated_by":"elastic","version":1}\n{"exported_list_items_count":0}\n`; + return `{"_version":"${jsonrule._version}","created_at":"${jsonrule.created_at}","created_by":"elastic","description":"${jsonrule.description}","id":"${jsonrule.id}","immutable":false,"list_id":"test_exception_list","name":"Test exception list","namespace_type":"single","os_types":[],"tags":[],"tie_breaker_id":"${jsonrule.tie_breaker_id}","type":"detection","updated_at":"${jsonrule.updated_at}","updated_by":"elastic","version":1}\n{"exported_exception_list_count":1,"exported_exception_list_item_count":0,"missing_exception_list_item_count":0,"missing_exception_list_items":[],"missing_exception_lists":[],"missing_exception_lists_count":0}\n`; }; diff --git a/x-pack/plugins/security_solution/cypress/objects/rule.ts b/x-pack/plugins/security_solution/cypress/objects/rule.ts index 27973854097db..ae04e20dfe86e 100644 --- a/x-pack/plugins/security_solution/cypress/objects/rule.ts +++ b/x-pack/plugins/security_solution/cypress/objects/rule.ts @@ -421,5 +421,5 @@ export const getEditedRule = (): CustomRule => ({ export const expectedExportedRule = (ruleResponse: Cypress.Response): string => { const jsonrule = ruleResponse.body; - return `{"id":"${jsonrule.id}","updated_at":"${jsonrule.updated_at}","updated_by":"elastic","created_at":"${jsonrule.created_at}","created_by":"elastic","name":"${jsonrule.name}","tags":[],"interval":"100m","enabled":false,"description":"${jsonrule.description}","risk_score":${jsonrule.risk_score},"severity":"${jsonrule.severity}","output_index":".siem-signals-default","author":[],"false_positives":[],"from":"now-50000h","rule_id":"rule_testing","max_signals":100,"risk_score_mapping":[],"severity_mapping":[],"threat":[],"to":"now","references":[],"version":1,"exceptions_list":[],"immutable":false,"type":"query","language":"kuery","index":["exceptions-*"],"query":"${jsonrule.query}","throttle":"no_actions","actions":[]}\n{"exported_count":1,"missing_rules":[],"missing_rules_count":0}\n`; + return `{"id":"${jsonrule.id}","updated_at":"${jsonrule.updated_at}","updated_by":"elastic","created_at":"${jsonrule.created_at}","created_by":"elastic","name":"${jsonrule.name}","tags":[],"interval":"100m","enabled":false,"description":"${jsonrule.description}","risk_score":${jsonrule.risk_score},"severity":"${jsonrule.severity}","output_index":".siem-signals-default","author":[],"false_positives":[],"from":"now-50000h","rule_id":"rule_testing","max_signals":100,"risk_score_mapping":[],"severity_mapping":[],"threat":[],"to":"now","references":[],"version":1,"exceptions_list":[],"immutable":false,"type":"query","language":"kuery","index":["exceptions-*"],"query":"${jsonrule.query}","throttle":"no_actions","actions":[]}\n{"exported_rules_count":1,"missing_rules":[],"missing_rules_count":0,"exported_exception_list_count":0,"exported_exception_list_item_count":0,"missing_exception_list_item_count":0,"missing_exception_list_items":[],"missing_exception_lists":[],"missing_exception_lists_count":0}\n`; }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/export_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/export_rules_route.ts index c84dd8147ebcc..277590820850b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/export_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/export_rules_route.ts @@ -46,6 +46,7 @@ export const exportRulesRoute = ( async (context, request, response) => { const siemResponse = buildSiemResponse(response); const rulesClient = context.alerting?.getRulesClient(); + const exceptionsClient = context.lists?.getExceptionListClient(); const savedObjectsClient = context.core.savedObjects.client; if (!rulesClient) { @@ -72,20 +73,27 @@ export const exportRulesRoute = ( } } - const exported = + const exportedRulesAndExceptions = request.body?.objects != null ? await getExportByObjectIds( rulesClient, + exceptionsClient, savedObjectsClient, request.body.objects, logger, isRuleRegistryEnabled ) - : await getExportAll(rulesClient, savedObjectsClient, logger, isRuleRegistryEnabled); + : await getExportAll( + rulesClient, + exceptionsClient, + savedObjectsClient, + logger, + isRuleRegistryEnabled + ); const responseBody = request.query.exclude_export_details - ? exported.rulesNdjson - : `${exported.rulesNdjson}${exported.exportDetails}`; + ? exportedRulesAndExceptions.rulesNdjson + : `${exportedRulesAndExceptions.rulesNdjson}${exportedRulesAndExceptions.exceptionLists}${exportedRulesAndExceptions.exportDetails}`; return response.ok({ headers: { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.ts index fb5a2315479da..d043149f8474e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/perform_bulk_action_route.ts @@ -47,6 +47,7 @@ export const performBulkActionRoute = ( try { const rulesClient = context.alerting?.getRulesClient(); + const exceptionsClient = context.lists?.getExceptionListClient(); const savedObjectsClient = context.core.savedObjects.client; const ruleStatusClient = context.securitySolution.getExecutionLogClient(); @@ -136,13 +137,14 @@ export const performBulkActionRoute = ( case BulkAction.export: const exported = await getExportByObjectIds( rulesClient, + exceptionsClient, savedObjectsClient, rules.data.map(({ params }) => ({ rule_id: params.ruleId })), logger, isRuleRegistryEnabled ); - const responseBody = `${exported.rulesNdjson}${exported.exportDetails}`; + const responseBody = `${exported.rulesNdjson}${exported.exceptionLists}${exported.exportDetails}`; return response.ok({ headers: { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules_stream_from_ndjson.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules_stream_from_ndjson.test.ts index c5b3e98c4c44e..f56d1d83eb873 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules_stream_from_ndjson.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules_stream_from_ndjson.test.ts @@ -207,7 +207,7 @@ describe('create_rules_stream_from_ndjson', () => { read() { this.push(getSampleAsNdjson(sample1)); this.push(getSampleAsNdjson(sample2)); - this.push('{"exported_count":1,"missing_rules":[],"missing_rules_count":0}\n'); + this.push('{"exported_rules_count":1,"missing_rules":[],"missing_rules_count":0}\n'); this.push(null); }, }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules_stream_from_ndjson.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules_stream_from_ndjson.ts index 0c2d81c18646b..d4357c45fd373 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules_stream_from_ndjson.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules_stream_from_ndjson.ts @@ -21,7 +21,8 @@ import { } from '../../../../common/detection_engine/schemas/request/import_rules_schema'; import { parseNdjsonStrings, - filterExportedCounts, + filterExportedRulesCounts, + filterExceptions, createLimitStream, } from '../../../utils/read_stream/create_stream_from_ndjson'; @@ -59,7 +60,8 @@ export const createRulesStreamFromNdJson = (ruleLimit: number) => { return [ createSplitStream('\n'), parseNdjsonStrings(), - filterExportedCounts(), + filterExportedRulesCounts(), + filterExceptions(), validateRules(), createLimitStream(ruleLimit), createConcatStream([]), diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_all.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_all.test.ts index 92e4f0bbb4a5e..80df4c94971cc 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_all.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_all.test.ts @@ -17,9 +17,12 @@ import { getListArrayMock } from '../../../../common/detection_engine/schemas/ty import { getThreatMock } from '../../../../common/detection_engine/schemas/types/threat.mock'; import { getQueryRuleParams } from '../schemas/rule_schemas.mock'; +import { getExceptionListClientMock } from '../../../../../lists/server/services/exception_lists/exception_list_client.mock'; import { loggingSystemMock } from 'src/core/server/mocks'; import { requestContextMock } from '../routes/__mocks__/request_context'; +const exceptionsClient = getExceptionListClientMock(); + describe.each([ ['Legacy', false], ['RAC', true], @@ -49,6 +52,7 @@ describe.each([ const exports = await getExportAll( rulesClient, + exceptionsClient, clients.savedObjectsClient, logger, isRuleRegistryEnabled @@ -97,7 +101,13 @@ describe.each([ exceptions_list: getListArrayMock(), }); expect(detailsJson).toEqual({ - exported_count: 1, + exported_exception_list_count: 0, + exported_exception_list_item_count: 0, + exported_rules_count: 1, + missing_exception_list_item_count: 0, + missing_exception_list_items: [], + missing_exception_lists: [], + missing_exception_lists_count: 0, missing_rules: [], missing_rules_count: 0, }); @@ -116,13 +126,16 @@ describe.each([ const exports = await getExportAll( rulesClient, + exceptionsClient, clients.savedObjectsClient, logger, isRuleRegistryEnabled ); expect(exports).toEqual({ rulesNdjson: '', - exportDetails: '{"exported_count":0,"missing_rules":[],"missing_rules_count":0}\n', + exportDetails: + '{"exported_rules_count":0,"missing_rules":[],"missing_rules_count":0,"exported_exception_list_count":0,"exported_exception_list_item_count":0,"missing_exception_list_item_count":0,"missing_exception_list_items":[],"missing_exception_lists":[],"missing_exception_lists_count":0}\n', + exceptionLists: '', }); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_all.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_all.ts index cbbda5df7e2bf..c0389de766ea5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_all.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_all.ts @@ -8,35 +8,44 @@ import { transformDataToNdjson } from '@kbn/securitysolution-utils'; import { Logger } from 'src/core/server'; +import { ExceptionListClient } from '../../../../../lists/server'; import { RulesClient, AlertServices } from '../../../../../alerting/server'; import { getNonPackagedRules } from './get_existing_prepackaged_rules'; import { getExportDetailsNdjson } from './get_export_details_ndjson'; import { transformAlertsToRules } from '../routes/rules/utils'; +import { getRuleExceptionsForExport } from './get_export_rule_exceptions'; // eslint-disable-next-line no-restricted-imports import { legacyGetBulkRuleActionsSavedObject } from '../rule_actions/legacy_get_bulk_rule_actions_saved_object'; export const getExportAll = async ( rulesClient: RulesClient, + exceptionsClient: ExceptionListClient | undefined, savedObjectsClient: AlertServices['savedObjectsClient'], logger: Logger, isRuleRegistryEnabled: boolean ): Promise<{ rulesNdjson: string; exportDetails: string; + exceptionLists: string | null; }> => { const ruleAlertTypes = await getNonPackagedRules({ rulesClient, isRuleRegistryEnabled }); const alertIds = ruleAlertTypes.map((rule) => rule.id); + + // Gather actions const legacyActions = await legacyGetBulkRuleActionsSavedObject({ alertIds, savedObjectsClient, logger, }); - const rules = transformAlertsToRules(ruleAlertTypes, legacyActions); - // We do not support importing/exporting actions. When we do, delete this line of code - const rulesWithoutActions = rules.map((rule) => ({ ...rule, actions: [] })); - const rulesNdjson = transformDataToNdjson(rulesWithoutActions); - const exportDetails = getExportDetailsNdjson(rules); - return { rulesNdjson, exportDetails }; + + // Gather exceptions + const exceptions = rules.flatMap((rule) => rule.exceptions_list ?? []); + const { exportData: exceptionLists, exportDetails: exceptionDetails } = + await getRuleExceptionsForExport(exceptions, exceptionsClient); + + const rulesNdjson = transformDataToNdjson(rules); + const exportDetails = getExportDetailsNdjson(rules, [], exceptionDetails); + return { rulesNdjson, exportDetails, exceptionLists }; }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts index 961f2c6a41866..7aa55a8163e1a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts @@ -16,6 +16,9 @@ import { rulesClientMock } from '../../../../../alerting/server/mocks'; import { getListArrayMock } from '../../../../common/detection_engine/schemas/types/lists.mock'; import { getThreatMock } from '../../../../common/detection_engine/schemas/types/threat.mock'; import { getQueryRuleParams } from '../schemas/rule_schemas.mock'; +import { getExceptionListClientMock } from '../../../../../lists/server/services/exception_lists/exception_list_client.mock'; + +const exceptionsClient = getExceptionListClientMock(); import { loggingSystemMock } from 'src/core/server/mocks'; import { requestContextMock } from '../routes/__mocks__/request_context'; @@ -42,6 +45,7 @@ describe.each([ const objects = [{ rule_id: 'rule-1' }]; const exports = await getExportByObjectIds( rulesClient, + exceptionsClient, clients.savedObjectsClient, objects, logger, @@ -94,7 +98,13 @@ describe.each([ exceptions_list: getListArrayMock(), }, exportDetails: { - exported_count: 1, + exported_exception_list_count: 0, + exported_exception_list_item_count: 0, + exported_rules_count: 1, + missing_exception_list_item_count: 0, + missing_exception_list_items: [], + missing_exception_lists: [], + missing_exception_lists_count: 0, missing_rules: [], missing_rules_count: 0, }, @@ -119,6 +129,7 @@ describe.each([ const objects = [{ rule_id: 'rule-1' }]; const exports = await getExportByObjectIds( rulesClient, + exceptionsClient, clients.savedObjectsClient, objects, logger, @@ -127,7 +138,8 @@ describe.each([ expect(exports).toEqual({ rulesNdjson: '', exportDetails: - '{"exported_count":0,"missing_rules":[{"rule_id":"rule-1"}],"missing_rules_count":1}\n', + '{"exported_rules_count":0,"missing_rules":[{"rule_id":"rule-1"}],"missing_rules_count":1,"exported_exception_list_count":0,"exported_exception_list_item_count":0,"missing_exception_list_item_count":0,"missing_exception_list_items":[],"missing_exception_lists":[],"missing_exception_lists_count":0}\n', + exceptionLists: '', }); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_by_object_ids.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_by_object_ids.ts index 8233fe6d4948c..81295c9197644 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_by_object_ids.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_by_object_ids.ts @@ -9,6 +9,7 @@ import { chunk } from 'lodash'; import { transformDataToNdjson } from '@kbn/securitysolution-utils'; import { Logger } from 'src/core/server'; +import { ExceptionListClient } from '../../../../../lists/server'; import { RulesSchema } from '../../../../common/detection_engine/schemas/response/rules_schema'; import { RulesClient, AlertServices } from '../../../../../alerting/server'; @@ -18,6 +19,7 @@ import { isAlertType } from '../rules/types'; import { transformAlertToRule } from '../routes/rules/utils'; import { INTERNAL_RULE_ID_KEY } from '../../../../common/constants'; import { findRules } from './find_rules'; +import { getRuleExceptionsForExport } from './get_export_rule_exceptions'; // eslint-disable-next-line no-restricted-imports import { legacyGetBulkRuleActionsSavedObject } from '../rule_actions/legacy_get_bulk_rule_actions_saved_object'; @@ -40,6 +42,7 @@ export interface RulesErrors { export const getExportByObjectIds = async ( rulesClient: RulesClient, + exceptionsClient: ExceptionListClient | undefined, savedObjectsClient: AlertServices['savedObjectsClient'], objects: Array<{ rule_id: string }>, logger: Logger, @@ -47,6 +50,7 @@ export const getExportByObjectIds = async ( ): Promise<{ rulesNdjson: string; exportDetails: string; + exceptionLists: string | null; }> => { const rulesAndErrors = await getRulesFromObjects( rulesClient, @@ -56,9 +60,19 @@ export const getExportByObjectIds = async ( isRuleRegistryEnabled ); + // Retrieve exceptions + const exceptions = rulesAndErrors.rules.flatMap((rule) => rule.exceptions_list ?? []); + const { exportData: exceptionLists, exportDetails: exceptionDetails } = + await getRuleExceptionsForExport(exceptions, exceptionsClient); + const rulesNdjson = transformDataToNdjson(rulesAndErrors.rules); - const exportDetails = getExportDetailsNdjson(rulesAndErrors.rules, rulesAndErrors.missingRules); - return { rulesNdjson, exportDetails }; + const exportDetails = getExportDetailsNdjson( + rulesAndErrors.rules, + rulesAndErrors.missingRules, + exceptionDetails + ); + + return { rulesNdjson, exportDetails, exceptionLists }; }; export const getRulesFromObjects = async ( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_details_ndjson.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_details_ndjson.test.ts index f4d50524d27b4..171233a861466 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_details_ndjson.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_details_ndjson.test.ts @@ -20,7 +20,7 @@ describe('getExportDetailsNdjson', () => { const details = getExportDetailsNdjson([rule]); const reParsed = JSON.parse(details); expect(reParsed).toEqual({ - exported_count: 1, + exported_rules_count: 1, missing_rules: [], missing_rules_count: 0, }); @@ -31,7 +31,7 @@ describe('getExportDetailsNdjson', () => { const details = getExportDetailsNdjson([], [missingRule]); const reParsed = JSON.parse(details); expect(reParsed).toEqual({ - exported_count: 0, + exported_rules_count: 0, missing_rules: [{ rule_id: 'rule-1' }], missing_rules_count: 1, }); @@ -49,7 +49,7 @@ describe('getExportDetailsNdjson', () => { const details = getExportDetailsNdjson([rule1, rule2], [missingRule1, missingRule2]); const reParsed = JSON.parse(details); expect(reParsed).toEqual({ - exported_count: 2, + exported_rules_count: 2, missing_rules: [missingRule1, missingRule2], missing_rules_count: 2, }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_details_ndjson.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_details_ndjson.ts index 7f9ec77e9df79..429bf4f2926bf 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_details_ndjson.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_details_ndjson.ts @@ -9,12 +9,14 @@ import { RulesSchema } from '../../../../common/detection_engine/schemas/respons export const getExportDetailsNdjson = ( rules: Array>, - missingRules: Array<{ rule_id: string }> = [] + missingRules: Array<{ rule_id: string }> = [], + extraMeta: Record = {} ): string => { const stringified = JSON.stringify({ - exported_count: rules.length, + exported_rules_count: rules.length, missing_rules: missingRules, missing_rules_count: missingRules.length, + ...extraMeta, }); return `${stringified}\n`; }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_rule_exceptions.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_rule_exceptions.test.ts new file mode 100644 index 0000000000000..dd7e59c74601c --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_rule_exceptions.test.ts @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ENDPOINT_LIST_ID } from '@kbn/securitysolution-list-constants'; + +import { getExceptionListClientMock } from '../../../../../lists/server/services/exception_lists/exception_list_client.mock'; +import { + getRuleExceptionsForExport, + getExportableExceptions, + getDefaultExportDetails, +} from './get_export_rule_exceptions'; +import { + getListArrayMock, + getListMock, +} from '../../../../common/detection_engine/schemas/types/lists.mock'; + +describe('get_export_rule_exceptions', () => { + describe('getRuleExceptionsForExport', () => { + test('it returns empty exceptions array if no rules have exceptions associated', async () => { + const { exportData, exportDetails } = await getRuleExceptionsForExport( + [], + getExceptionListClientMock() + ); + + expect(exportData).toEqual(''); + expect(exportDetails).toEqual(getDefaultExportDetails()); + }); + + test('it returns stringified exceptions ready for export', async () => { + const { exportData } = await getRuleExceptionsForExport( + [getListMock()], + getExceptionListClientMock() + ); + + expect(exportData).toEqual('exportString'); + }); + + test('it does not return a global endpoint list', async () => { + const { exportData } = await getRuleExceptionsForExport( + [ + { + id: ENDPOINT_LIST_ID, + list_id: ENDPOINT_LIST_ID, + namespace_type: 'agnostic', + type: 'endpoint', + }, + ], + getExceptionListClientMock() + ); + + expect(exportData).toEqual(''); + }); + }); + + describe('getExportableExceptions', () => { + test('it returns stringified exception lists and items', async () => { + // This rule has 2 exception lists tied to it + const { exportData } = await getExportableExceptions( + getListArrayMock(), + getExceptionListClientMock() + ); + + expect(exportData).toEqual('exportStringexportString'); + }); + + test('it throws error if error occurs in getting exceptions', async () => { + const exceptionsClient = getExceptionListClientMock(); + exceptionsClient.exportExceptionListAndItems = jest.fn().mockRejectedValue(new Error('oops')); + // This rule has 2 exception lists tied to it + await expect(async () => { + await getExportableExceptions(getListArrayMock(), exceptionsClient); + }).rejects.toThrowErrorMatchingInlineSnapshot(`"oops"`); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_rule_exceptions.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_rule_exceptions.ts new file mode 100644 index 0000000000000..719649d35c0f0 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_export_rule_exceptions.ts @@ -0,0 +1,102 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { chunk } from 'lodash/fp'; +import { ListArray } from '@kbn/securitysolution-io-ts-list-types'; +import { ENDPOINT_LIST_ID } from '@kbn/securitysolution-list-constants'; + +import { + ExceptionListClient, + ExportExceptionListAndItemsReturn, +} from '../../../../../lists/server'; + +const NON_EXPORTABLE_LIST_IDS = [ENDPOINT_LIST_ID]; +export const EXCEPTIONS_EXPORT_CHUNK_SIZE = 50; + +export const getRuleExceptionsForExport = async ( + exceptions: ListArray, + exceptionsListClient: ExceptionListClient | undefined +): Promise => { + if (exceptionsListClient != null) { + const exceptionsWithoutUnexportableLists = exceptions.filter( + ({ list_id: listId }) => !NON_EXPORTABLE_LIST_IDS.includes(listId) + ); + return getExportableExceptions(exceptionsWithoutUnexportableLists, exceptionsListClient); + } else { + return { exportData: '', exportDetails: getDefaultExportDetails() }; + } +}; + +export const getExportableExceptions = async ( + exceptions: ListArray, + exceptionsListClient: ExceptionListClient +): Promise => { + let exportString = ''; + const exportDetails = getDefaultExportDetails(); + + const exceptionChunks = chunk(EXCEPTIONS_EXPORT_CHUNK_SIZE, exceptions); + for await (const exceptionChunk of exceptionChunks) { + const promises = createPromises(exceptionsListClient, exceptionChunk); + + const responses = await Promise.all(promises); + + for (const res of responses) { + if (res != null) { + const { + exportDetails: { + exported_exception_list_count: exportedExceptionListCount, + exported_exception_list_item_count: exportedExceptionListItemCount, + }, + exportData, + } = res; + + exportDetails.exported_exception_list_count = + exportDetails.exported_exception_list_count + exportedExceptionListCount; + + exportDetails.exported_exception_list_item_count = + exportDetails.exported_exception_list_item_count + exportedExceptionListItemCount; + + exportString = `${exportString}${exportData}`; + } + } + } + + return { + exportDetails, + exportData: exportString, + }; +}; + +/** + * Creates promises of the rules and returns them. + * @param exceptionsListClient Exception Lists client + * @param exceptions The rules to apply the update for + * @returns Promise of export ready exceptions. + */ +export const createPromises = ( + exceptionsListClient: ExceptionListClient, + exceptions: ListArray +): Array> => { + return exceptions.map>( + async ({ id, list_id: listId, namespace_type: namespaceType }) => { + return exceptionsListClient.exportExceptionListAndItems({ + id, + listId, + namespaceType, + }); + } + ); +}; + +export const getDefaultExportDetails = () => ({ + exported_exception_list_count: 0, + exported_exception_list_item_count: 0, + missing_exception_list_item_count: 0, + missing_exception_list_items: [], + missing_exception_lists: [], + missing_exception_lists_count: 0, +}); diff --git a/x-pack/plugins/security_solution/server/utils/read_stream/create_stream_from_ndjson.ts b/x-pack/plugins/security_solution/server/utils/read_stream/create_stream_from_ndjson.ts index eb5abaee8cd3b..914c684fe8813 100644 --- a/x-pack/plugins/security_solution/server/utils/read_stream/create_stream_from_ndjson.ts +++ b/x-pack/plugins/security_solution/server/utils/read_stream/create_stream_from_ndjson.ts @@ -34,6 +34,18 @@ export const filterExportedCounts = (): Transform => { ); }; +export const filterExportedRulesCounts = (): Transform => { + return createFilterStream( + (obj) => obj != null && !has('exported_rules_count', obj) + ); +}; + +export const filterExceptions = (): Transform => { + return createFilterStream( + (obj) => obj != null && !has('list_id', obj) + ); +}; + // Adaptation from: saved_objects/import/create_limit_stream.ts export const createLimitStream = (limit: number): Transform => { let counter = 0; diff --git a/x-pack/test/detection_engine_api_integration/basic/tests/export_rules.ts b/x-pack/test/detection_engine_api_integration/basic/tests/export_rules.ts index 1071e9fae3417..03b1beffa7993 100644 --- a/x-pack/test/detection_engine_api_integration/basic/tests/export_rules.ts +++ b/x-pack/test/detection_engine_api_integration/basic/tests/export_rules.ts @@ -76,7 +76,13 @@ export default ({ getService }: FtrProviderContext): void => { const bodySplitAndParsed = JSON.parse(body.toString().split(/\n/)[1]); expect(bodySplitAndParsed).to.eql({ - exported_count: 1, + exported_exception_list_count: 0, + exported_exception_list_item_count: 0, + exported_rules_count: 1, + missing_exception_list_item_count: 0, + missing_exception_list_items: [], + missing_exception_lists: [], + missing_exception_lists_count: 0, missing_rules: [], missing_rules_count: 0, }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/export_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/export_rules.ts index 1071e9fae3417..03b1beffa7993 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/export_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/export_rules.ts @@ -76,7 +76,13 @@ export default ({ getService }: FtrProviderContext): void => { const bodySplitAndParsed = JSON.parse(body.toString().split(/\n/)[1]); expect(bodySplitAndParsed).to.eql({ - exported_count: 1, + exported_exception_list_count: 0, + exported_exception_list_item_count: 0, + exported_rules_count: 1, + missing_exception_list_item_count: 0, + missing_exception_list_items: [], + missing_exception_lists: [], + missing_exception_lists_count: 0, missing_rules: [], missing_rules_count: 0, }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/perform_bulk_action.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/perform_bulk_action.ts index 53613624067e1..83166619b152d 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/perform_bulk_action.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/perform_bulk_action.ts @@ -57,7 +57,13 @@ export default ({ getService }: FtrProviderContext): void => { const exportDetails = JSON.parse(exportDetailsJson); expect(exportDetails).to.eql({ - exported_count: 1, + exported_exception_list_count: 0, + exported_exception_list_item_count: 0, + exported_rules_count: 1, + missing_exception_list_item_count: 0, + missing_exception_list_items: [], + missing_exception_lists: [], + missing_exception_lists_count: 0, missing_rules: [], missing_rules_count: 0, }); diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/export_exception_list.ts b/x-pack/test/lists_api_integration/security_and_spaces/tests/export_exception_list.ts index d35d34fde5bcc..c21026d5df3d2 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/export_exception_list.ts +++ b/x-pack/test/lists_api_integration/security_and_spaces/tests/export_exception_list.ts @@ -77,7 +77,7 @@ export default ({ getService }: FtrProviderContext): void => { .expect(400); expect(exportBody).to.eql({ - message: 'exception list with list_id: not_exist does not exist', + message: 'exception list with list_id: not_exist or id: not_exist does not exist', status_code: 400, }); }); From 5894d75901844b964fe935af7aba22bbabf99f24 Mon Sep 17 00:00:00 2001 From: Aleh Zasypkin Date: Wed, 20 Oct 2021 08:14:55 +0200 Subject: [PATCH 108/204] Register `kibana_user` role deprecations in UA. (#115573) --- x-pack/plugins/security/common/index.ts | 2 +- .../security/server/deprecations/index.ts | 9 +- .../deprecations/kibana_user_role.test.ts | 328 ++++++++++++++++++ .../server/deprecations/kibana_user_role.ts | 238 +++++++++++++ x-pack/plugins/security/server/plugin.ts | 17 +- .../server/routes/deprecations/index.ts | 13 + .../deprecations/kibana_user_role.test.ts | 283 +++++++++++++++ .../routes/deprecations/kibana_user_role.ts | 145 ++++++++ .../plugins/security/server/routes/index.ts | 4 +- 9 files changed, 1030 insertions(+), 9 deletions(-) create mode 100644 x-pack/plugins/security/server/deprecations/kibana_user_role.test.ts create mode 100644 x-pack/plugins/security/server/deprecations/kibana_user_role.ts create mode 100644 x-pack/plugins/security/server/routes/deprecations/index.ts create mode 100644 x-pack/plugins/security/server/routes/deprecations/kibana_user_role.test.ts create mode 100644 x-pack/plugins/security/server/routes/deprecations/kibana_user_role.ts diff --git a/x-pack/plugins/security/common/index.ts b/x-pack/plugins/security/common/index.ts index ac5d252c98a8b..1d05036191635 100644 --- a/x-pack/plugins/security/common/index.ts +++ b/x-pack/plugins/security/common/index.ts @@ -6,4 +6,4 @@ */ export type { SecurityLicense } from './licensing'; -export type { AuthenticatedUser } from './model'; +export type { AuthenticatedUser, PrivilegeDeprecationsService } from './model'; diff --git a/x-pack/plugins/security/server/deprecations/index.ts b/x-pack/plugins/security/server/deprecations/index.ts index 05802a5a673c5..2c4b47ba41a0a 100644 --- a/x-pack/plugins/security/server/deprecations/index.ts +++ b/x-pack/plugins/security/server/deprecations/index.ts @@ -5,8 +5,9 @@ * 2.0. */ -/** - * getKibanaRolesByFeature - */ - export { getPrivilegeDeprecationsService } from './privilege_deprecations'; +export { + registerKibanaUserRoleDeprecation, + KIBANA_ADMIN_ROLE_NAME, + KIBANA_USER_ROLE_NAME, +} from './kibana_user_role'; diff --git a/x-pack/plugins/security/server/deprecations/kibana_user_role.test.ts b/x-pack/plugins/security/server/deprecations/kibana_user_role.test.ts new file mode 100644 index 0000000000000..da728b12fca91 --- /dev/null +++ b/x-pack/plugins/security/server/deprecations/kibana_user_role.test.ts @@ -0,0 +1,328 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { errors } from '@elastic/elasticsearch'; +import type { SecurityRoleMapping, SecurityUser } from '@elastic/elasticsearch/api/types'; + +import type { PackageInfo, RegisterDeprecationsConfig } from 'src/core/server'; +import { + deprecationsServiceMock, + elasticsearchServiceMock, + loggingSystemMock, + savedObjectsClientMock, +} from 'src/core/server/mocks'; + +import { licenseMock } from '../../common/licensing/index.mock'; +import { securityMock } from '../mocks'; +import { registerKibanaUserRoleDeprecation } from './kibana_user_role'; + +function getDepsMock() { + return { + logger: loggingSystemMock.createLogger(), + deprecationsService: deprecationsServiceMock.createSetupContract(), + license: licenseMock.create(), + packageInfo: { + branch: 'some-branch', + buildSha: 'sha', + dist: true, + version: '8.0.0', + buildNum: 1, + } as PackageInfo, + }; +} + +function getContextMock() { + return { + esClient: elasticsearchServiceMock.createScopedClusterClient(), + savedObjectsClient: savedObjectsClientMock.create(), + }; +} + +function createMockUser(user: Partial = {}) { + return { enabled: true, username: 'userA', roles: ['roleA'], metadata: {}, ...user }; +} + +function createMockRoleMapping(mapping: Partial = {}) { + return { enabled: true, roles: ['roleA'], rules: {}, metadata: {}, ...mapping }; +} + +describe('Kibana Dashboard Only User role deprecations', () => { + let mockDeps: ReturnType; + let mockContext: ReturnType; + let deprecationHandler: RegisterDeprecationsConfig; + beforeEach(() => { + mockContext = getContextMock(); + mockDeps = getDepsMock(); + registerKibanaUserRoleDeprecation(mockDeps); + + expect(mockDeps.deprecationsService.registerDeprecations).toHaveBeenCalledTimes(1); + deprecationHandler = mockDeps.deprecationsService.registerDeprecations.mock.calls[0][0]; + }); + + it('does not return any deprecations if security is not enabled', async () => { + mockDeps.license.isEnabled.mockReturnValue(false); + + await expect(deprecationHandler.getDeprecations(mockContext)).resolves.toEqual([]); + expect(mockContext.esClient.asCurrentUser.security.getUser).not.toHaveBeenCalled(); + expect(mockContext.esClient.asCurrentUser.security.getRoleMapping).not.toHaveBeenCalled(); + }); + + it('does not return any deprecations if none of the users and role mappings has a kibana user role', async () => { + mockContext.esClient.asCurrentUser.security.getUser.mockResolvedValue( + securityMock.createApiResponse({ body: { userA: createMockUser() } }) + ); + + mockContext.esClient.asCurrentUser.security.getRoleMapping.mockResolvedValue( + securityMock.createApiResponse({ + body: { + mappingA: { enabled: true, roles: ['roleA'], rules: {}, metadata: {} }, + }, + }) + ); + + await expect(deprecationHandler.getDeprecations(mockContext)).resolves.toEqual([]); + }); + + it('returns deprecations even if cannot retrieve users due to permission error', async () => { + mockContext.esClient.asCurrentUser.security.getUser.mockRejectedValue( + new errors.ResponseError(securityMock.createApiResponse({ statusCode: 403, body: {} })) + ); + mockContext.esClient.asCurrentUser.security.getRoleMapping.mockResolvedValue( + securityMock.createApiResponse({ body: { mappingA: createMockRoleMapping() } }) + ); + + await expect(deprecationHandler.getDeprecations(mockContext)).resolves.toMatchInlineSnapshot(` + Array [ + Object { + "correctiveActions": Object { + "manualSteps": Array [ + "Make sure you have a \\"manage_security\\" cluster privilege assigned.", + ], + }, + "deprecationType": "feature", + "documentationUrl": "https://www.elastic.co/guide/en/kibana/some-branch/xpack-security.html#_required_permissions_7", + "level": "fetch_error", + "message": "You do not have enough permissions to fix this deprecation.", + "title": "The \\"kibana_user\\" role is deprecated", + }, + ] + `); + }); + + it('returns deprecations even if cannot retrieve users due to unknown error', async () => { + mockContext.esClient.asCurrentUser.security.getUser.mockRejectedValue( + new errors.ResponseError(securityMock.createApiResponse({ statusCode: 500, body: {} })) + ); + mockContext.esClient.asCurrentUser.security.getRoleMapping.mockResolvedValue( + securityMock.createApiResponse({ body: { mappingA: createMockRoleMapping() } }) + ); + + await expect(deprecationHandler.getDeprecations(mockContext)).resolves.toMatchInlineSnapshot(` + Array [ + Object { + "correctiveActions": Object { + "manualSteps": Array [ + "Check Kibana logs for more details.", + ], + }, + "deprecationType": "feature", + "level": "fetch_error", + "message": "Failed to perform deprecation check. Check Kibana logs for more details.", + "title": "The \\"kibana_user\\" role is deprecated", + }, + ] + `); + }); + + it('returns deprecations even if cannot retrieve role mappings due to permission error', async () => { + mockContext.esClient.asCurrentUser.security.getUser.mockResolvedValue( + securityMock.createApiResponse({ body: { userA: createMockUser() } }) + ); + mockContext.esClient.asCurrentUser.security.getRoleMapping.mockRejectedValue( + new errors.ResponseError(securityMock.createApiResponse({ statusCode: 403, body: {} })) + ); + + await expect(deprecationHandler.getDeprecations(mockContext)).resolves.toMatchInlineSnapshot(` + Array [ + Object { + "correctiveActions": Object { + "manualSteps": Array [ + "Make sure you have a \\"manage_security\\" cluster privilege assigned.", + ], + }, + "deprecationType": "feature", + "documentationUrl": "https://www.elastic.co/guide/en/kibana/some-branch/xpack-security.html#_required_permissions_7", + "level": "fetch_error", + "message": "You do not have enough permissions to fix this deprecation.", + "title": "The \\"kibana_user\\" role is deprecated", + }, + ] + `); + }); + + it('returns deprecations even if cannot retrieve role mappings due to unknown error', async () => { + mockContext.esClient.asCurrentUser.security.getUser.mockResolvedValue( + securityMock.createApiResponse({ body: { userA: createMockUser() } }) + ); + mockContext.esClient.asCurrentUser.security.getRoleMapping.mockRejectedValue( + new errors.ResponseError(securityMock.createApiResponse({ statusCode: 500, body: {} })) + ); + + await expect(deprecationHandler.getDeprecations(mockContext)).resolves.toMatchInlineSnapshot(` + Array [ + Object { + "correctiveActions": Object { + "manualSteps": Array [ + "Check Kibana logs for more details.", + ], + }, + "deprecationType": "feature", + "level": "fetch_error", + "message": "Failed to perform deprecation check. Check Kibana logs for more details.", + "title": "The \\"kibana_user\\" role is deprecated", + }, + ] + `); + }); + + it('returns only user-related deprecations', async () => { + mockContext.esClient.asCurrentUser.security.getUser.mockResolvedValue( + securityMock.createApiResponse({ + body: { + userA: createMockUser({ username: 'userA', roles: ['roleA'] }), + userB: createMockUser({ username: 'userB', roles: ['roleB', 'kibana_user'] }), + userC: createMockUser({ username: 'userC', roles: ['roleC'] }), + userD: createMockUser({ username: 'userD', roles: ['kibana_user'] }), + }, + }) + ); + + mockContext.esClient.asCurrentUser.security.getRoleMapping.mockResolvedValue( + securityMock.createApiResponse({ body: { mappingA: createMockRoleMapping() } }) + ); + + await expect(deprecationHandler.getDeprecations(mockContext)).resolves.toMatchInlineSnapshot(` + Array [ + Object { + "correctiveActions": Object { + "api": Object { + "method": "POST", + "path": "/internal/security/deprecations/kibana_user_role/_fix_users", + }, + "manualSteps": Array [ + "Remove the \\"kibana_user\\" role from all users and add the \\"kibana_admin\\" role. The affected users are: userB, userD.", + ], + }, + "deprecationType": "feature", + "documentationUrl": "https://www.elastic.co/guide/en/elasticsearch/reference/some-branch/built-in-roles.html", + "level": "warning", + "message": "Use the \\"kibana_admin\\" role to grant access to all Kibana features in all spaces.", + "title": "The \\"kibana_user\\" role is deprecated", + }, + ] + `); + }); + + it('returns only role-mapping-related deprecations', async () => { + mockContext.esClient.asCurrentUser.security.getUser.mockResolvedValue( + securityMock.createApiResponse({ body: { userA: createMockUser() } }) + ); + + mockContext.esClient.asCurrentUser.security.getRoleMapping.mockResolvedValue( + securityMock.createApiResponse({ + body: { + mappingA: createMockRoleMapping({ roles: ['roleA'] }), + mappingB: createMockRoleMapping({ roles: ['roleB', 'kibana_user'] }), + mappingC: createMockRoleMapping({ roles: ['roleC'] }), + mappingD: createMockRoleMapping({ roles: ['kibana_user'] }), + }, + }) + ); + + await expect(deprecationHandler.getDeprecations(mockContext)).resolves.toMatchInlineSnapshot(` + Array [ + Object { + "correctiveActions": Object { + "api": Object { + "method": "POST", + "path": "/internal/security/deprecations/kibana_user_role/_fix_role_mappings", + }, + "manualSteps": Array [ + "Remove the \\"kibana_user\\" role from all role mappings and add the \\"kibana_admin\\" role. The affected role mappings are: mappingB, mappingD.", + ], + }, + "deprecationType": "feature", + "documentationUrl": "https://www.elastic.co/guide/en/elasticsearch/reference/some-branch/built-in-roles.html", + "level": "warning", + "message": "Use the \\"kibana_admin\\" role to grant access to all Kibana features in all spaces.", + "title": "The \\"kibana_user\\" role is deprecated", + }, + ] + `); + }); + + it('returns both user-related and role-mapping-related deprecations', async () => { + mockContext.esClient.asCurrentUser.security.getUser.mockResolvedValue( + securityMock.createApiResponse({ + body: { + userA: createMockUser({ username: 'userA', roles: ['roleA'] }), + userB: createMockUser({ username: 'userB', roles: ['roleB', 'kibana_user'] }), + userC: createMockUser({ username: 'userC', roles: ['roleC'] }), + userD: createMockUser({ username: 'userD', roles: ['kibana_user'] }), + }, + }) + ); + + mockContext.esClient.asCurrentUser.security.getRoleMapping.mockResolvedValue( + securityMock.createApiResponse({ + body: { + mappingA: createMockRoleMapping({ roles: ['roleA'] }), + mappingB: createMockRoleMapping({ roles: ['roleB', 'kibana_user'] }), + mappingC: createMockRoleMapping({ roles: ['roleC'] }), + mappingD: createMockRoleMapping({ roles: ['kibana_user'] }), + }, + }) + ); + + await expect(deprecationHandler.getDeprecations(mockContext)).resolves.toMatchInlineSnapshot(` + Array [ + Object { + "correctiveActions": Object { + "api": Object { + "method": "POST", + "path": "/internal/security/deprecations/kibana_user_role/_fix_users", + }, + "manualSteps": Array [ + "Remove the \\"kibana_user\\" role from all users and add the \\"kibana_admin\\" role. The affected users are: userB, userD.", + ], + }, + "deprecationType": "feature", + "documentationUrl": "https://www.elastic.co/guide/en/elasticsearch/reference/some-branch/built-in-roles.html", + "level": "warning", + "message": "Use the \\"kibana_admin\\" role to grant access to all Kibana features in all spaces.", + "title": "The \\"kibana_user\\" role is deprecated", + }, + Object { + "correctiveActions": Object { + "api": Object { + "method": "POST", + "path": "/internal/security/deprecations/kibana_user_role/_fix_role_mappings", + }, + "manualSteps": Array [ + "Remove the \\"kibana_user\\" role from all role mappings and add the \\"kibana_admin\\" role. The affected role mappings are: mappingB, mappingD.", + ], + }, + "deprecationType": "feature", + "documentationUrl": "https://www.elastic.co/guide/en/elasticsearch/reference/some-branch/built-in-roles.html", + "level": "warning", + "message": "Use the \\"kibana_admin\\" role to grant access to all Kibana features in all spaces.", + "title": "The \\"kibana_user\\" role is deprecated", + }, + ] + `); + }); +}); diff --git a/x-pack/plugins/security/server/deprecations/kibana_user_role.ts b/x-pack/plugins/security/server/deprecations/kibana_user_role.ts new file mode 100644 index 0000000000000..d659ea273f05f --- /dev/null +++ b/x-pack/plugins/security/server/deprecations/kibana_user_role.ts @@ -0,0 +1,238 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { + SecurityGetRoleMappingResponse, + SecurityGetUserResponse, +} from '@elastic/elasticsearch/api/types'; + +import { i18n } from '@kbn/i18n'; +import type { + DeprecationsDetails, + DeprecationsServiceSetup, + ElasticsearchClient, + Logger, + PackageInfo, +} from 'src/core/server'; + +import type { SecurityLicense } from '../../common'; +import { getDetailedErrorMessage, getErrorStatusCode } from '../errors'; + +export const KIBANA_USER_ROLE_NAME = 'kibana_user'; +export const KIBANA_ADMIN_ROLE_NAME = 'kibana_admin'; + +interface Deps { + deprecationsService: DeprecationsServiceSetup; + license: SecurityLicense; + logger: Logger; + packageInfo: PackageInfo; +} + +function getDeprecationTitle() { + return i18n.translate('xpack.security.deprecations.kibanaUser.deprecationTitle', { + defaultMessage: 'The "{userRoleName}" role is deprecated', + values: { userRoleName: KIBANA_USER_ROLE_NAME }, + }); +} + +function getDeprecationMessage() { + return i18n.translate('xpack.security.deprecations.kibanaUser.deprecationMessage', { + defaultMessage: + 'Use the "{adminRoleName}" role to grant access to all Kibana features in all spaces.', + values: { adminRoleName: KIBANA_ADMIN_ROLE_NAME }, + }); +} + +export const registerKibanaUserRoleDeprecation = ({ + deprecationsService, + logger, + license, + packageInfo, +}: Deps) => { + deprecationsService.registerDeprecations({ + getDeprecations: async (context) => { + // Nothing to do if security is disabled + if (!license.isEnabled()) { + return []; + } + + return [ + ...(await getUsersDeprecations(context.esClient.asCurrentUser, logger, packageInfo)), + ...(await getRoleMappingsDeprecations(context.esClient.asCurrentUser, logger, packageInfo)), + ]; + }, + }); +}; + +async function getUsersDeprecations( + client: ElasticsearchClient, + logger: Logger, + packageInfo: PackageInfo +): Promise { + let users: SecurityGetUserResponse; + try { + users = (await client.security.getUser()).body; + } catch (err) { + if (getErrorStatusCode(err) === 403) { + logger.warn( + `Failed to retrieve users when checking for deprecations: the "manage_security" cluster privilege is required.` + ); + } else { + logger.error( + `Failed to retrieve users when checking for deprecations, unexpected error: ${getDetailedErrorMessage( + err + )}.` + ); + } + return deprecationError(packageInfo, err); + } + + const usersWithKibanaUserRole = Object.values(users) + .filter((user) => user.roles.includes(KIBANA_USER_ROLE_NAME)) + .map((user) => user.username); + if (usersWithKibanaUserRole.length === 0) { + return []; + } + + return [ + { + title: getDeprecationTitle(), + message: getDeprecationMessage(), + level: 'warning', + deprecationType: 'feature', + documentationUrl: `https://www.elastic.co/guide/en/elasticsearch/reference/${packageInfo.branch}/built-in-roles.html`, + correctiveActions: { + api: { + method: 'POST', + path: '/internal/security/deprecations/kibana_user_role/_fix_users', + }, + manualSteps: [ + i18n.translate( + 'xpack.security.deprecations.kibanaUser.usersDeprecationCorrectiveAction', + { + defaultMessage: + 'Remove the "{userRoleName}" role from all users and add the "{adminRoleName}" role. The affected users are: {users}.', + values: { + userRoleName: KIBANA_USER_ROLE_NAME, + adminRoleName: KIBANA_ADMIN_ROLE_NAME, + users: usersWithKibanaUserRole.join(', '), + }, + } + ), + ], + }, + }, + ]; +} + +async function getRoleMappingsDeprecations( + client: ElasticsearchClient, + logger: Logger, + packageInfo: PackageInfo +): Promise { + let roleMappings: SecurityGetRoleMappingResponse; + try { + roleMappings = (await client.security.getRoleMapping()).body; + } catch (err) { + if (getErrorStatusCode(err) === 403) { + logger.warn( + `Failed to retrieve role mappings when checking for deprecations: the "manage_security" cluster privilege is required.` + ); + } else { + logger.error( + `Failed to retrieve role mappings when checking for deprecations, unexpected error: ${getDetailedErrorMessage( + err + )}.` + ); + } + return deprecationError(packageInfo, err); + } + + const roleMappingsWithKibanaUserRole = Object.entries(roleMappings) + .filter(([, roleMapping]) => roleMapping.roles.includes(KIBANA_USER_ROLE_NAME)) + .map(([mappingName]) => mappingName); + if (roleMappingsWithKibanaUserRole.length === 0) { + return []; + } + + return [ + { + title: getDeprecationTitle(), + message: getDeprecationMessage(), + level: 'warning', + deprecationType: 'feature', + documentationUrl: `https://www.elastic.co/guide/en/elasticsearch/reference/${packageInfo.branch}/built-in-roles.html`, + correctiveActions: { + api: { + method: 'POST', + path: '/internal/security/deprecations/kibana_user_role/_fix_role_mappings', + }, + manualSteps: [ + i18n.translate( + 'xpack.security.deprecations.kibanaUser.roleMappingsDeprecationCorrectiveAction', + { + defaultMessage: + 'Remove the "{userRoleName}" role from all role mappings and add the "{adminRoleName}" role. The affected role mappings are: {roleMappings}.', + values: { + userRoleName: KIBANA_USER_ROLE_NAME, + adminRoleName: KIBANA_ADMIN_ROLE_NAME, + roleMappings: roleMappingsWithKibanaUserRole.join(', '), + }, + } + ), + ], + }, + }, + ]; +} + +function deprecationError(packageInfo: PackageInfo, error: Error): DeprecationsDetails[] { + const title = getDeprecationTitle(); + + if (getErrorStatusCode(error) === 403) { + return [ + { + title, + level: 'fetch_error', + deprecationType: 'feature', + message: i18n.translate('xpack.security.deprecations.kibanaUser.forbiddenErrorMessage', { + defaultMessage: 'You do not have enough permissions to fix this deprecation.', + }), + documentationUrl: `https://www.elastic.co/guide/en/kibana/${packageInfo.branch}/xpack-security.html#_required_permissions_7`, + correctiveActions: { + manualSteps: [ + i18n.translate( + 'xpack.security.deprecations.kibanaUser.forbiddenErrorCorrectiveAction', + { + defaultMessage: + 'Make sure you have a "manage_security" cluster privilege assigned.', + } + ), + ], + }, + }, + ]; + } + + return [ + { + title, + level: 'fetch_error', + deprecationType: 'feature', + message: i18n.translate('xpack.security.deprecations.kibanaUser.unknownErrorMessage', { + defaultMessage: 'Failed to perform deprecation check. Check Kibana logs for more details.', + }), + correctiveActions: { + manualSteps: [ + i18n.translate('xpack.security.deprecations.kibanaUser.unknownErrorCorrectiveAction', { + defaultMessage: 'Check Kibana logs for more details.', + }), + ], + }, + }, + ]; +} diff --git a/x-pack/plugins/security/server/plugin.ts b/x-pack/plugins/security/server/plugin.ts index 98e77038f168a..1e42d10b205aa 100644 --- a/x-pack/plugins/security/server/plugin.ts +++ b/x-pack/plugins/security/server/plugin.ts @@ -27,9 +27,8 @@ import type { import type { LicensingPluginSetup, LicensingPluginStart } from '../../licensing/server'; import type { SpacesPluginSetup, SpacesPluginStart } from '../../spaces/server'; import type { TaskManagerSetupContract, TaskManagerStartContract } from '../../task_manager/server'; -import type { SecurityLicense } from '../common/licensing'; +import type { AuthenticatedUser, PrivilegeDeprecationsService, SecurityLicense } from '../common'; import { SecurityLicenseService } from '../common/licensing'; -import type { AuthenticatedUser, PrivilegeDeprecationsService } from '../common/model'; import type { AnonymousAccessServiceStart } from './anonymous_access'; import { AnonymousAccessService } from './anonymous_access'; import type { AuditServiceSetup } from './audit'; @@ -43,7 +42,7 @@ import type { AuthorizationServiceSetup, AuthorizationServiceSetupInternal } fro import { AuthorizationService } from './authorization'; import type { ConfigSchema, ConfigType } from './config'; import { createConfig } from './config'; -import { getPrivilegeDeprecationsService } from './deprecations'; +import { getPrivilegeDeprecationsService, registerKibanaUserRoleDeprecation } from './deprecations'; import { ElasticsearchService } from './elasticsearch'; import type { SecurityFeatureUsageServiceStart } from './feature_usage'; import { SecurityFeatureUsageService } from './feature_usage'; @@ -290,6 +289,8 @@ export class SecurityPlugin getSpacesService: () => spaces?.spacesService, }); + this.registerDeprecations(core, license); + defineRoutes({ router: core.http.createRouter(), basePath: core.http.basePath, @@ -414,4 +415,14 @@ export class SecurityPlugin this.authorizationService.stop(); this.sessionManagementService.stop(); } + + private registerDeprecations(core: CoreSetup, license: SecurityLicense) { + const logger = this.logger.get('deprecations'); + registerKibanaUserRoleDeprecation({ + deprecationsService: core.deprecations, + license, + logger, + packageInfo: this.initializerContext.env.packageInfo, + }); + } } diff --git a/x-pack/plugins/security/server/routes/deprecations/index.ts b/x-pack/plugins/security/server/routes/deprecations/index.ts new file mode 100644 index 0000000000000..cbc186ed2e925 --- /dev/null +++ b/x-pack/plugins/security/server/routes/deprecations/index.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { RouteDefinitionParams } from '../'; +import { defineKibanaUserRoleDeprecationRoutes } from './kibana_user_role'; + +export function defineDeprecationsRoutes(params: RouteDefinitionParams) { + defineKibanaUserRoleDeprecationRoutes(params); +} diff --git a/x-pack/plugins/security/server/routes/deprecations/kibana_user_role.test.ts b/x-pack/plugins/security/server/routes/deprecations/kibana_user_role.test.ts new file mode 100644 index 0000000000000..b2ae2543bd652 --- /dev/null +++ b/x-pack/plugins/security/server/routes/deprecations/kibana_user_role.test.ts @@ -0,0 +1,283 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { errors } from '@elastic/elasticsearch'; +import type { SecurityRoleMapping, SecurityUser } from '@elastic/elasticsearch/api/types'; + +import type { DeeplyMockedKeys } from '@kbn/utility-types/jest'; +import type { RequestHandler, RouteConfig } from 'src/core/server'; +import { kibanaResponseFactory } from 'src/core/server'; +import { coreMock, httpServerMock } from 'src/core/server/mocks'; + +import { securityMock } from '../../mocks'; +import type { SecurityRequestHandlerContext, SecurityRouter } from '../../types'; +import { routeDefinitionParamsMock } from '../index.mock'; +import { defineKibanaUserRoleDeprecationRoutes } from './kibana_user_role'; + +function createMockUser(user: Partial = {}) { + return { enabled: true, username: 'userA', roles: ['roleA'], metadata: {}, ...user }; +} + +function createMockRoleMapping(mapping: Partial = {}) { + return { enabled: true, roles: ['roleA'], rules: {}, metadata: {}, ...mapping }; +} + +describe('Kibana user deprecation routes', () => { + let router: jest.Mocked; + let mockContext: DeeplyMockedKeys; + beforeEach(() => { + const routeParamsMock = routeDefinitionParamsMock.create(); + router = routeParamsMock.router; + + mockContext = { + core: coreMock.createRequestHandlerContext(), + licensing: { license: { check: jest.fn().mockReturnValue({ state: 'valid' }) } }, + } as any; + + defineKibanaUserRoleDeprecationRoutes(routeParamsMock); + }); + + describe('Users with Kibana user role', () => { + let routeHandler: RequestHandler; + let routeConfig: RouteConfig; + beforeEach(() => { + const [fixUsersRouteConfig, fixUsersRouteHandler] = router.post.mock.calls.find( + ([{ path }]) => path === '/internal/security/deprecations/kibana_user_role/_fix_users' + )!; + + routeConfig = fixUsersRouteConfig; + routeHandler = fixUsersRouteHandler; + }); + + it('correctly defines route.', () => { + expect(routeConfig.options).toBeUndefined(); + expect(routeConfig.validate).toBe(false); + }); + + it('fails if cannot retrieve users', async () => { + mockContext.core.elasticsearch.client.asCurrentUser.security.getUser.mockRejectedValue( + new errors.ResponseError( + securityMock.createApiResponse({ statusCode: 500, body: new Error('Oh no') }) + ) + ); + + await expect( + routeHandler(mockContext, httpServerMock.createKibanaRequest(), kibanaResponseFactory) + ).resolves.toEqual(expect.objectContaining({ status: 500 })); + + expect( + mockContext.core.elasticsearch.client.asCurrentUser.security.putUser + ).not.toHaveBeenCalled(); + }); + + it('fails if fails to update user', async () => { + mockContext.core.elasticsearch.client.asCurrentUser.security.getUser.mockResolvedValue( + securityMock.createApiResponse({ + body: { + userA: createMockUser({ username: 'userA', roles: ['roleA', 'kibana_user'] }), + userB: createMockUser({ username: 'userB', roles: ['kibana_user'] }), + }, + }) + ); + mockContext.core.elasticsearch.client.asCurrentUser.security.putUser.mockRejectedValue( + new errors.ResponseError( + securityMock.createApiResponse({ statusCode: 500, body: new Error('Oh no') }) + ) + ); + + await expect( + routeHandler(mockContext, httpServerMock.createKibanaRequest(), kibanaResponseFactory) + ).resolves.toEqual(expect.objectContaining({ status: 500 })); + + expect( + mockContext.core.elasticsearch.client.asCurrentUser.security.putUser + ).toHaveBeenCalledTimes(1); + expect( + mockContext.core.elasticsearch.client.asCurrentUser.security.putUser + ).toHaveBeenCalledWith({ + username: 'userA', + body: createMockUser({ username: 'userA', roles: ['roleA', 'kibana_admin'] }), + }); + }); + + it('does nothing if there are no users with Kibana user role', async () => { + mockContext.core.elasticsearch.client.asCurrentUser.security.getUser.mockResolvedValue( + securityMock.createApiResponse({ body: { userA: createMockUser() } }) + ); + + await expect( + routeHandler(mockContext, httpServerMock.createKibanaRequest(), kibanaResponseFactory) + ).resolves.toEqual(expect.objectContaining({ status: 200 })); + + expect( + mockContext.core.elasticsearch.client.asCurrentUser.security.putUser + ).not.toHaveBeenCalled(); + }); + + it('updates users with Kibana user role', async () => { + mockContext.core.elasticsearch.client.asCurrentUser.security.getUser.mockResolvedValue( + securityMock.createApiResponse({ + body: { + userA: createMockUser({ username: 'userA', roles: ['roleA'] }), + userB: createMockUser({ username: 'userB', roles: ['roleB', 'kibana_user'] }), + userC: createMockUser({ username: 'userC', roles: ['roleC'] }), + userD: createMockUser({ username: 'userD', roles: ['kibana_user'] }), + userE: createMockUser({ + username: 'userE', + roles: ['kibana_user', 'kibana_admin', 'roleE'], + }), + }, + }) + ); + + await expect( + routeHandler(mockContext, httpServerMock.createKibanaRequest(), kibanaResponseFactory) + ).resolves.toEqual(expect.objectContaining({ status: 200 })); + + expect( + mockContext.core.elasticsearch.client.asCurrentUser.security.putUser + ).toHaveBeenCalledTimes(3); + expect( + mockContext.core.elasticsearch.client.asCurrentUser.security.putUser + ).toHaveBeenCalledWith({ + username: 'userB', + body: createMockUser({ username: 'userB', roles: ['roleB', 'kibana_admin'] }), + }); + expect( + mockContext.core.elasticsearch.client.asCurrentUser.security.putUser + ).toHaveBeenCalledWith({ + username: 'userD', + body: createMockUser({ username: 'userD', roles: ['kibana_admin'] }), + }); + expect( + mockContext.core.elasticsearch.client.asCurrentUser.security.putUser + ).toHaveBeenCalledWith({ + username: 'userE', + body: createMockUser({ username: 'userE', roles: ['kibana_admin', 'roleE'] }), + }); + }); + }); + + describe('Role mappings with Kibana user role', () => { + let routeHandler: RequestHandler; + let routeConfig: RouteConfig; + beforeEach(() => { + const [fixRoleMappingsRouteConfig, fixRoleMappingsRouteHandler] = router.post.mock.calls.find( + ([{ path }]) => + path === '/internal/security/deprecations/kibana_user_role/_fix_role_mappings' + )!; + + routeConfig = fixRoleMappingsRouteConfig; + routeHandler = fixRoleMappingsRouteHandler; + }); + + it('correctly defines route.', () => { + expect(routeConfig.options).toBeUndefined(); + expect(routeConfig.validate).toBe(false); + }); + + it('fails if cannot retrieve role mappings', async () => { + mockContext.core.elasticsearch.client.asCurrentUser.security.getRoleMapping.mockRejectedValue( + new errors.ResponseError( + securityMock.createApiResponse({ statusCode: 500, body: new Error('Oh no') }) + ) + ); + + await expect( + routeHandler(mockContext, httpServerMock.createKibanaRequest(), kibanaResponseFactory) + ).resolves.toEqual(expect.objectContaining({ status: 500 })); + + expect( + mockContext.core.elasticsearch.client.asCurrentUser.security.putRoleMapping + ).not.toHaveBeenCalled(); + }); + + it('fails if fails to update role mapping', async () => { + mockContext.core.elasticsearch.client.asCurrentUser.security.getRoleMapping.mockResolvedValue( + securityMock.createApiResponse({ + body: { + mappingA: createMockRoleMapping({ roles: ['roleA', 'kibana_user'] }), + mappingB: createMockRoleMapping({ roles: ['kibana_user'] }), + }, + }) + ); + mockContext.core.elasticsearch.client.asCurrentUser.security.putRoleMapping.mockRejectedValue( + new errors.ResponseError( + securityMock.createApiResponse({ statusCode: 500, body: new Error('Oh no') }) + ) + ); + + await expect( + routeHandler(mockContext, httpServerMock.createKibanaRequest(), kibanaResponseFactory) + ).resolves.toEqual(expect.objectContaining({ status: 500 })); + + expect( + mockContext.core.elasticsearch.client.asCurrentUser.security.putRoleMapping + ).toHaveBeenCalledTimes(1); + expect( + mockContext.core.elasticsearch.client.asCurrentUser.security.putRoleMapping + ).toHaveBeenCalledWith({ + name: 'mappingA', + body: createMockRoleMapping({ roles: ['roleA', 'kibana_admin'] }), + }); + }); + + it('does nothing if there are no role mappings with Kibana user role', async () => { + mockContext.core.elasticsearch.client.asCurrentUser.security.getRoleMapping.mockResolvedValue( + securityMock.createApiResponse({ body: { mappingA: createMockRoleMapping() } }) + ); + + await expect( + routeHandler(mockContext, httpServerMock.createKibanaRequest(), kibanaResponseFactory) + ).resolves.toEqual(expect.objectContaining({ status: 200 })); + + expect( + mockContext.core.elasticsearch.client.asCurrentUser.security.putRoleMapping + ).not.toHaveBeenCalled(); + }); + + it('updates role mappings with Kibana user role', async () => { + mockContext.core.elasticsearch.client.asCurrentUser.security.getRoleMapping.mockResolvedValue( + securityMock.createApiResponse({ + body: { + mappingA: createMockRoleMapping({ roles: ['roleA'] }), + mappingB: createMockRoleMapping({ roles: ['roleB', 'kibana_user'] }), + mappingC: createMockRoleMapping({ roles: ['roleC'] }), + mappingD: createMockRoleMapping({ roles: ['kibana_user'] }), + mappingE: createMockRoleMapping({ roles: ['kibana_user', 'kibana_admin', 'roleE'] }), + }, + }) + ); + + await expect( + routeHandler(mockContext, httpServerMock.createKibanaRequest(), kibanaResponseFactory) + ).resolves.toEqual(expect.objectContaining({ status: 200 })); + + expect( + mockContext.core.elasticsearch.client.asCurrentUser.security.putRoleMapping + ).toHaveBeenCalledTimes(3); + expect( + mockContext.core.elasticsearch.client.asCurrentUser.security.putRoleMapping + ).toHaveBeenCalledWith({ + name: 'mappingB', + body: createMockRoleMapping({ roles: ['roleB', 'kibana_admin'] }), + }); + expect( + mockContext.core.elasticsearch.client.asCurrentUser.security.putRoleMapping + ).toHaveBeenCalledWith({ + name: 'mappingD', + body: createMockRoleMapping({ roles: ['kibana_admin'] }), + }); + expect( + mockContext.core.elasticsearch.client.asCurrentUser.security.putRoleMapping + ).toHaveBeenCalledWith({ + name: 'mappingE', + body: createMockRoleMapping({ roles: ['kibana_admin', 'roleE'] }), + }); + }); + }); +}); diff --git a/x-pack/plugins/security/server/routes/deprecations/kibana_user_role.ts b/x-pack/plugins/security/server/routes/deprecations/kibana_user_role.ts new file mode 100644 index 0000000000000..21bb9db7329b6 --- /dev/null +++ b/x-pack/plugins/security/server/routes/deprecations/kibana_user_role.ts @@ -0,0 +1,145 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { + SecurityGetRoleMappingResponse, + SecurityGetUserResponse, +} from '@elastic/elasticsearch/api/types'; + +import type { RouteDefinitionParams } from '..'; +import { KIBANA_ADMIN_ROLE_NAME, KIBANA_USER_ROLE_NAME } from '../../deprecations'; +import { + getDetailedErrorMessage, + getErrorStatusCode, + wrapIntoCustomErrorResponse, +} from '../../errors'; +import { createLicensedRouteHandler } from '../licensed_route_handler'; + +/** + * Defines routes required to handle `kibana_user` deprecation. + */ +export function defineKibanaUserRoleDeprecationRoutes({ router, logger }: RouteDefinitionParams) { + router.post( + { + path: '/internal/security/deprecations/kibana_user_role/_fix_users', + validate: false, + }, + createLicensedRouteHandler(async (context, request, response) => { + let users: SecurityGetUserResponse; + try { + users = (await context.core.elasticsearch.client.asCurrentUser.security.getUser()).body; + } catch (err) { + if (getErrorStatusCode(err) === 403) { + logger.warn( + `Failed to retrieve users when checking for deprecations: the manage_security cluster privilege is required` + ); + } else { + logger.error( + `Failed to retrieve users when checking for deprecations, unexpected error: ${getDetailedErrorMessage( + err + )}` + ); + } + return response.customError(wrapIntoCustomErrorResponse(err)); + } + + const usersWithKibanaUserRole = Object.values(users).filter((user) => + user.roles.includes(KIBANA_USER_ROLE_NAME) + ); + + if (usersWithKibanaUserRole.length === 0) { + logger.debug(`No users with "${KIBANA_USER_ROLE_NAME}" role found.`); + } else { + logger.debug( + `The following users with "${KIBANA_USER_ROLE_NAME}" role found and will be migrated to "${KIBANA_ADMIN_ROLE_NAME}" role: ${usersWithKibanaUserRole + .map((user) => user.username) + .join(', ')}.` + ); + } + + for (const userToUpdate of usersWithKibanaUserRole) { + const roles = userToUpdate.roles.filter((role) => role !== KIBANA_USER_ROLE_NAME); + if (!roles.includes(KIBANA_ADMIN_ROLE_NAME)) { + roles.push(KIBANA_ADMIN_ROLE_NAME); + } + + try { + await context.core.elasticsearch.client.asCurrentUser.security.putUser({ + username: userToUpdate.username, + body: { ...userToUpdate, roles }, + }); + } catch (err) { + logger.error( + `Failed to update user "${userToUpdate.username}": ${getDetailedErrorMessage(err)}.` + ); + return response.customError(wrapIntoCustomErrorResponse(err)); + } + + logger.debug(`Successfully updated user "${userToUpdate.username}".`); + } + + return response.ok({ body: {} }); + }) + ); + + router.post( + { + path: '/internal/security/deprecations/kibana_user_role/_fix_role_mappings', + validate: false, + }, + createLicensedRouteHandler(async (context, request, response) => { + let roleMappings: SecurityGetRoleMappingResponse; + try { + roleMappings = ( + await context.core.elasticsearch.client.asCurrentUser.security.getRoleMapping() + ).body; + } catch (err) { + logger.error(`Failed to retrieve role mappings: ${getDetailedErrorMessage(err)}.`); + return response.customError(wrapIntoCustomErrorResponse(err)); + } + + const roleMappingsWithKibanaUserRole = Object.entries(roleMappings).filter(([, mapping]) => + mapping.roles.includes(KIBANA_USER_ROLE_NAME) + ); + + if (roleMappingsWithKibanaUserRole.length === 0) { + logger.debug(`No role mappings with "${KIBANA_USER_ROLE_NAME}" role found.`); + } else { + logger.debug( + `The following role mappings with "${KIBANA_USER_ROLE_NAME}" role found and will be migrated to "${KIBANA_ADMIN_ROLE_NAME}" role: ${roleMappingsWithKibanaUserRole + .map(([mappingName]) => mappingName) + .join(', ')}.` + ); + } + + for (const [mappingNameToUpdate, mappingToUpdate] of roleMappingsWithKibanaUserRole) { + const roles = mappingToUpdate.roles.filter((role) => role !== KIBANA_USER_ROLE_NAME); + if (!roles.includes(KIBANA_ADMIN_ROLE_NAME)) { + roles.push(KIBANA_ADMIN_ROLE_NAME); + } + + try { + await context.core.elasticsearch.client.asCurrentUser.security.putRoleMapping({ + name: mappingNameToUpdate, + body: { ...mappingToUpdate, roles }, + }); + } catch (err) { + logger.error( + `Failed to update role mapping "${mappingNameToUpdate}": ${getDetailedErrorMessage( + err + )}.` + ); + return response.customError(wrapIntoCustomErrorResponse(err)); + } + + logger.debug(`Successfully updated role mapping "${mappingNameToUpdate}".`); + } + + return response.ok({ body: {} }); + }) + ); +} diff --git a/x-pack/plugins/security/server/routes/index.ts b/x-pack/plugins/security/server/routes/index.ts index 851e70a357cf9..6785fe57c6b32 100644 --- a/x-pack/plugins/security/server/routes/index.ts +++ b/x-pack/plugins/security/server/routes/index.ts @@ -11,7 +11,7 @@ import type { PublicMethodsOf } from '@kbn/utility-types'; import type { HttpResources, IBasePath, Logger } from 'src/core/server'; import type { KibanaFeature } from '../../../features/server'; -import type { SecurityLicense } from '../../common/licensing'; +import type { SecurityLicense } from '../../common'; import type { AnonymousAccessServiceStart } from '../anonymous_access'; import type { InternalAuthenticationServiceStart } from '../authentication'; import type { AuthorizationServiceSetupInternal } from '../authorization'; @@ -23,6 +23,7 @@ import { defineAnonymousAccessRoutes } from './anonymous_access'; import { defineApiKeysRoutes } from './api_keys'; import { defineAuthenticationRoutes } from './authentication'; import { defineAuthorizationRoutes } from './authorization'; +import { defineDeprecationsRoutes } from './deprecations'; import { defineIndicesRoutes } from './indices'; import { defineRoleMappingRoutes } from './role_mapping'; import { defineSecurityCheckupGetStateRoutes } from './security_checkup'; @@ -58,6 +59,7 @@ export function defineRoutes(params: RouteDefinitionParams) { defineUsersRoutes(params); defineRoleMappingRoutes(params); defineViewRoutes(params); + defineDeprecationsRoutes(params); defineAnonymousAccessRoutes(params); defineSecurityCheckupGetStateRoutes(params); } From c954c3b1e6341d3eb407dfbd19ffd906dffc8ba0 Mon Sep 17 00:00:00 2001 From: Dominique Clarke Date: Wed, 20 Oct 2021 02:52:07 -0400 Subject: [PATCH 109/204] [Observability] [Exploratory View] add chart creation context (#114784) * add chart creation context * add chart creation tooltip, remove outer panel, and change default chart type for synthetics data * adjust types * remove extra translations * add panel back * update chart creation time date format * update time format * adjust tests Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- src/plugins/data/common/constants.ts | 1 + .../exploratory_view/exploratory_view.tsx | 12 ++-- .../header/chart_creation_info.test.tsx | 35 +++++++++++ .../header/chart_creation_info.tsx | 61 +++++++++++++++++++ .../shared/exploratory_view/header/header.tsx | 9 +-- .../exploratory_view/header/last_updated.tsx | 28 +++++++-- .../exploratory_view/lens_embeddable.tsx | 14 +++-- .../common/header/action_menu_content.tsx | 2 +- 8 files changed, 145 insertions(+), 17 deletions(-) create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/header/chart_creation_info.test.tsx create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/header/chart_creation_info.tsx diff --git a/src/plugins/data/common/constants.ts b/src/plugins/data/common/constants.ts index c236be18a8e41..e141bfbef3c89 100644 --- a/src/plugins/data/common/constants.ts +++ b/src/plugins/data/common/constants.ts @@ -34,4 +34,5 @@ export const UI_SETTINGS = { FILTERS_EDITOR_SUGGEST_VALUES: 'filterEditor:suggestValues', AUTOCOMPLETE_USE_TIMERANGE: 'autocomplete:useTimeRange', AUTOCOMPLETE_VALUE_SUGGESTION_METHOD: 'autocomplete:valueSuggestionMethod', + DATE_FORMAT: 'dateFormat', } as const; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/exploratory_view.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/exploratory_view.tsx index f3f332b5094b6..ceb87429f9de4 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/exploratory_view.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/exploratory_view.tsx @@ -7,8 +7,8 @@ import { i18n } from '@kbn/i18n'; import React, { useEffect, useRef, useState } from 'react'; -import { EuiButtonEmpty, EuiPanel, EuiResizableContainer, EuiTitle } from '@elastic/eui'; import styled from 'styled-components'; +import { EuiButtonEmpty, EuiResizableContainer, EuiTitle, EuiPanel } from '@elastic/eui'; import { PanelDirection } from '@elastic/eui/src/components/resizable_container/types'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; import { ObservabilityPublicPluginsStart } from '../../../plugin'; @@ -20,6 +20,7 @@ import { useAppIndexPatternContext } from './hooks/use_app_index_pattern'; import { SeriesViews } from './views/series_views'; import { LensEmbeddable } from './lens_embeddable'; import { EmptyView } from './components/empty_view'; +import type { ChartTimeRange } from './header/last_updated'; export type PanelId = 'seriesPanel' | 'chartPanel'; @@ -37,7 +38,7 @@ export function ExploratoryView({ const [height, setHeight] = useState('100vh'); - const [lastUpdated, setLastUpdated] = useState(); + const [chartTimeRangeContext, setChartTimeRangeContext] = useState(); const [lensAttributes, setLensAttributes] = useState( null @@ -96,7 +97,10 @@ export function ExploratoryView({ {lens ? ( <> - + {lensAttributes ? ( ) : ( diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/header/chart_creation_info.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/header/chart_creation_info.test.tsx new file mode 100644 index 0000000000000..570362a63c33f --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/header/chart_creation_info.test.tsx @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { screen } from '@testing-library/dom'; +import { render } from '../rtl_helpers'; +import { ChartCreationInfo } from './chart_creation_info'; + +const info = { + to: 1634071132571, + from: 1633406400000, + lastUpdated: 1634071140788, +}; + +describe('ChartCreationInfo', () => { + it('renders chart creation info', async () => { + render(); + + expect(screen.getByText('Chart created')).toBeInTheDocument(); + expect(screen.getByText('Oct 12, 2021 4:39 PM')).toBeInTheDocument(); + expect(screen.getByText('Displaying from')).toBeInTheDocument(); + expect(screen.getByText('Oct 5, 2021 12:00 AM → Oct 12, 2021 4:38 PM')).toBeInTheDocument(); + }); + + it('does not display info when props are falsey', async () => { + render(); + + expect(screen.queryByText('Chart created')).not.toBeInTheDocument(); + expect(screen.queryByText('Displaying from')).not.toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/header/chart_creation_info.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/header/chart_creation_info.tsx new file mode 100644 index 0000000000000..4814bc8d8630a --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/header/chart_creation_info.tsx @@ -0,0 +1,61 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import moment from 'moment'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiFlexGroup, EuiFlexItem, EuiText, EuiSpacer } from '@elastic/eui'; +import type { ChartTimeRange } from './last_updated'; + +export function ChartCreationInfo(props: Partial) { + const dateFormat = 'lll'; + const from = moment(props.from).format(dateFormat); + const to = moment(props.to).format(dateFormat); + const created = moment(props.lastUpdated).format(dateFormat); + + return ( + <> + {props.lastUpdated && ( + <> + + + + + + + + {created} + + + + + )} + {props.to && props.from && ( + <> + + + + + + + + + {from} → {to} + + + + + )} + + ); +} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/header/header.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/header/header.tsx index 181c8342b87af..22245f111293c 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/header/header.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/header/header.tsx @@ -10,16 +10,17 @@ import { i18n } from '@kbn/i18n'; import { EuiBetaBadge, EuiButton, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; import { TypedLensByValueInput } from '../../../../../../lens/public'; import { useSeriesStorage } from '../hooks/use_series_storage'; -import { LastUpdated } from './last_updated'; import { ExpViewActionMenu } from '../components/action_menu'; import { useExpViewTimeRange } from '../hooks/use_time_range'; +import { LastUpdated } from './last_updated'; +import type { ChartTimeRange } from './last_updated'; interface Props { - lastUpdated?: number; + chartTimeRange?: ChartTimeRange; lensAttributes: TypedLensByValueInput['attributes'] | null; } -export function ExploratoryViewHeader({ lensAttributes, lastUpdated }: Props) { +export function ExploratoryViewHeader({ lensAttributes, chartTimeRange }: Props) { const { setLastRefresh } = useSeriesStorage(); const timeRange = useExpViewTimeRange(); @@ -46,7 +47,7 @@ export function ExploratoryViewHeader({ lensAttributes, lastUpdated }: Props) { - + setLastRefresh(Date.now())}> diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/header/last_updated.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/header/last_updated.tsx index c352ec0423dd8..bc82c48214a01 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/header/last_updated.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/header/last_updated.tsx @@ -6,14 +6,24 @@ */ import React, { useEffect, useState } from 'react'; -import { EuiIcon, EuiText } from '@elastic/eui'; import moment from 'moment'; +import styled from 'styled-components'; +import { EuiIcon, EuiText, EuiToolTip } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; +import { ChartCreationInfo } from './chart_creation_info'; + +export interface ChartTimeRange { + lastUpdated: number; + to: number; + from: number; +} interface Props { - lastUpdated?: number; + chartTimeRange?: ChartTimeRange; } -export function LastUpdated({ lastUpdated }: Props) { + +export function LastUpdated({ chartTimeRange }: Props) { + const { lastUpdated } = chartTimeRange || {}; const [refresh, setRefresh] = useState(() => Date.now()); useEffect(() => { @@ -39,7 +49,13 @@ export function LastUpdated({ lastUpdated }: Props) { return ( - + } + > + + {' '} ); } + +export const StyledToolTipWrapper = styled.div` + min-width: 30vw; +`; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/lens_embeddable.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/lens_embeddable.tsx index 235790e72862c..b3ec7ee184f00 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/lens_embeddable.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/lens_embeddable.tsx @@ -13,14 +13,16 @@ import { useSeriesStorage } from './hooks/use_series_storage'; import { ObservabilityPublicPluginsStart } from '../../../plugin'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; import { useExpViewTimeRange } from './hooks/use_time_range'; +import { parseRelativeDate } from './components/date_range_picker'; +import type { ChartTimeRange } from './header/last_updated'; interface Props { lensAttributes: TypedLensByValueInput['attributes']; - setLastUpdated: Dispatch>; + setChartTimeRangeContext: Dispatch>; } export function LensEmbeddable(props: Props) { - const { lensAttributes, setLastUpdated } = props; + const { lensAttributes, setChartTimeRangeContext } = props; const { services: { lens, notifications }, @@ -35,8 +37,12 @@ export function LensEmbeddable(props: Props) { const timeRange = useExpViewTimeRange(); const onLensLoad = useCallback(() => { - setLastUpdated(Date.now()); - }, [setLastUpdated]); + setChartTimeRangeContext({ + lastUpdated: Date.now(), + to: parseRelativeDate(timeRange?.to || '').valueOf(), + from: parseRelativeDate(timeRange?.from || '').valueOf(), + }); + }, [setChartTimeRangeContext, timeRange]); const onBrushEnd = useCallback( ({ range }: { range: number[] }) => { diff --git a/x-pack/plugins/uptime/public/components/common/header/action_menu_content.tsx b/x-pack/plugins/uptime/public/components/common/header/action_menu_content.tsx index 789953258750b..26f9e28101ea4 100644 --- a/x-pack/plugins/uptime/public/components/common/header/action_menu_content.tsx +++ b/x-pack/plugins/uptime/public/components/common/header/action_menu_content.tsx @@ -51,7 +51,7 @@ export function ActionMenuContent(): React.ReactElement { allSeries: [ { dataType: 'synthetics', - seriesType: 'area_stacked', + seriesType: 'area', selectedMetricField: 'monitor.duration.us', time: { from: dateRangeStart, to: dateRangeEnd }, breakdown: monitorId ? 'observer.geo.name' : 'monitor.type', From 4ff3cb46f612107745f84b582ba9b67d550d5c7f Mon Sep 17 00:00:00 2001 From: Dmitry Tomashevich <39378793+Dmitriynj@users.noreply.github.com> Date: Wed, 20 Oct 2021 10:34:03 +0300 Subject: [PATCH 110/204] [Discover] Fix search highlighting of expanded document (#114884) * [Discover] fix searches highlighting of expanded document * [Discover] apply suggestion * [Discover] apply suggestion Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../components/discover_grid/discover_grid_flyout.tsx | 4 +++- .../application/components/doc_viewer/doc_viewer_tab.tsx | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/plugins/discover/public/application/components/discover_grid/discover_grid_flyout.tsx b/src/plugins/discover/public/application/components/discover_grid/discover_grid_flyout.tsx index e4b67c49689ab..f6e5e25f284ca 100644 --- a/src/plugins/discover/public/application/components/discover_grid/discover_grid_flyout.tsx +++ b/src/plugins/discover/public/application/components/discover_grid/discover_grid_flyout.tsx @@ -70,6 +70,8 @@ export function DiscoverGridFlyout({ services, setExpandedDoc, }: Props) { + // Get actual hit with updated highlighted searches + const actualHit = useMemo(() => hits?.find(({ _id }) => _id === hit?._id) || hit, [hit, hits]); const pageCount = useMemo(() => (hits ? hits.length : 0), [hits]); const activePage = useMemo(() => { const id = getDocFingerprintId(hit); @@ -188,7 +190,7 @@ export function DiscoverGridFlyout({ { diff --git a/src/plugins/discover/public/application/components/doc_viewer/doc_viewer_tab.tsx b/src/plugins/discover/public/application/components/doc_viewer/doc_viewer_tab.tsx index e2af88b91b3ff..75ec5a62e9299 100644 --- a/src/plugins/discover/public/application/components/doc_viewer/doc_viewer_tab.tsx +++ b/src/plugins/discover/public/application/components/doc_viewer/doc_viewer_tab.tsx @@ -45,6 +45,7 @@ export class DocViewerTab extends React.Component { shouldComponentUpdate(nextProps: Props, nextState: State) { return ( nextProps.renderProps.hit._id !== this.props.renderProps.hit._id || + !isEqual(nextProps.renderProps.hit.highlight, this.props.renderProps.hit.highlight) || nextProps.id !== this.props.id || !isEqual(nextProps.renderProps.columns, this.props.renderProps.columns) || nextState.hasError From 7fe9573c00af2d8ec6f856f71459e315fd799eef Mon Sep 17 00:00:00 2001 From: Artem Shelkovnikov Date: Wed, 20 Oct 2021 13:25:26 +0500 Subject: [PATCH 111/204] Enterprise Search - Add note about Cases to Salesforce Content Source (#114457) * Enterprise Search - Add note about Cases to Salesforce Content Source * Minor copy update for xpack.enterpriseSearch.workplaceSearch.sources.objTypes.cases Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../public/applications/workplace_search/constants.ts | 3 +++ .../workplace_search/views/content_sources/source_data.tsx | 2 ++ 2 files changed, 5 insertions(+) diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/constants.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/constants.ts index a43fb6f293457..a0df5337b2e2e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/constants.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/constants.ts @@ -288,6 +288,9 @@ export const SOURCE_OBJ_TYPES = { CAMPAIGNS: i18n.translate('xpack.enterpriseSearch.workplaceSearch.sources.objTypes.campaigns', { defaultMessage: 'Campaigns', }), + CASES: i18n.translate('xpack.enterpriseSearch.workplaceSearch.sources.objTypes.cases', { + defaultMessage: 'Cases (including feeds and comments)', + }), USERS: i18n.translate('xpack.enterpriseSearch.workplaceSearch.sources.objTypes.users', { defaultMessage: 'Users', }), diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_data.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_data.tsx index c303190651f57..244ce79135cab 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_data.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_data.tsx @@ -432,6 +432,7 @@ export const staticSourceData = [ SOURCE_OBJ_TYPES.LEADS, SOURCE_OBJ_TYPES.ACCOUNTS, SOURCE_OBJ_TYPES.CAMPAIGNS, + SOURCE_OBJ_TYPES.CASES, ], features: { basicOrgContext: [ @@ -466,6 +467,7 @@ export const staticSourceData = [ SOURCE_OBJ_TYPES.LEADS, SOURCE_OBJ_TYPES.ACCOUNTS, SOURCE_OBJ_TYPES.CAMPAIGNS, + SOURCE_OBJ_TYPES.CASES, ], features: { basicOrgContext: [ From 8fcfa79e73666bd59a5d28ba93739596a75a1367 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Louv-Jansen?= Date: Wed, 20 Oct 2021 10:29:23 +0200 Subject: [PATCH 112/204] Fix ESLint example (#115553) --- x-pack/plugins/apm/dev_docs/linting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/apm/dev_docs/linting.md b/x-pack/plugins/apm/dev_docs/linting.md index 7db7053e59061..edf3e813a88e9 100644 --- a/x-pack/plugins/apm/dev_docs/linting.md +++ b/x-pack/plugins/apm/dev_docs/linting.md @@ -17,5 +17,5 @@ yarn prettier "./x-pack/plugins/apm/**/*.{tsx,ts,js}" --write ### ESLint ``` -node scripts/eslint.js x-pack/legacy/plugins/apm +node scripts/eslint.js x-pack/plugins/apm ``` From 6e7dfcd99ae3109dc22c7a7f6b7d4cb84261f531 Mon Sep 17 00:00:00 2001 From: Thom Heymann <190132+thomheymann@users.noreply.github.com> Date: Wed, 20 Oct 2021 10:22:04 +0100 Subject: [PATCH 113/204] Fix maps font path (#115453) * fixed font path * fix functional tests * fix directory issue Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/maps/server/routes.js | 5 ++-- .../api_integration/apis/maps/fonts_api.js | 30 ++++++++++++++++--- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/maps/server/routes.js b/x-pack/plugins/maps/server/routes.js index a25a28c4da21c..162c305a69ca5 100644 --- a/x-pack/plugins/maps/server/routes.js +++ b/x-pack/plugins/maps/server/routes.js @@ -488,10 +488,11 @@ export async function initRoutes(core, getLicenseId, emsSettings, kbnVersion, lo }, (context, request, response) => { const range = path.normalize(request.params.range); - return range.startsWith('..') + const rootPath = path.resolve(__dirname, 'fonts', 'open_sans'); + const fontPath = path.resolve(rootPath, `${range}.pbf`); + return !fontPath.startsWith(rootPath) ? response.notFound() : new Promise((resolve) => { - const fontPath = path.join(__dirname, 'fonts', 'open_sans', `${range}.pbf`); fs.readFile(fontPath, (error, data) => { if (error) { resolve(response.notFound()); diff --git a/x-pack/test/api_integration/apis/maps/fonts_api.js b/x-pack/test/api_integration/apis/maps/fonts_api.js index afde003b05f2d..35017e6e37db8 100644 --- a/x-pack/test/api_integration/apis/maps/fonts_api.js +++ b/x-pack/test/api_integration/apis/maps/fonts_api.js @@ -6,11 +6,33 @@ */ import expect from '@kbn/expect'; +import path from 'path'; +import { copyFile, rm } from 'fs/promises'; export default function ({ getService }) { const supertest = getService('supertest'); + const log = getService('log'); describe('fonts', () => { + // [HACK]: On CI tests are run from the different directories than the built and running Kibana + // instance. To workaround that we use Kibana `process.cwd()` to construct font path manually. + // x-pack tests can be run from root directory or from within x-pack so need to cater for both possibilities. + const fontPath = path.join( + process.cwd().replace(/x-pack.*$/, ''), + 'x-pack/plugins/maps/server/fonts/open_sans/0-255.pbf' + ); + const destinationPath = path.join(path.dirname(fontPath), '..', path.basename(fontPath)); + + before(async () => { + log.debug(`Copying test file from '${fontPath}' to '${destinationPath}'`); + await copyFile(fontPath, destinationPath); + }); + + after(async () => { + log.debug(`Removing test file '${destinationPath}'`); + await rm(destinationPath); + }); + it('should return fonts', async () => { const resp = await supertest .get(`/api/maps/fonts/Open%20Sans%20Regular,Arial%20Unicode%20MS%20Regular/0-255`) @@ -25,12 +47,12 @@ export default function ({ getService }) { .expect(404); }); - it('should return 404 when file is not in font folder (../)', async () => { - await supertest.get(`/api/maps/fonts/open_sans/..%2fopen_sans%2f0-255`).expect(404); + it('should return 404 when file is not in font folder (..)', async () => { + await supertest.get(`/api/maps/fonts/open_sans/..%2f0-255`).expect(404); }); - it('should return 404 when file is not in font folder (./../)', async () => { - await supertest.get(`/api/maps/fonts/open_sans/.%2f..%2fopen_sans%2f0-255`).expect(404); + it('should return 404 when file is not in font folder (./..)', async () => { + await supertest.get(`/api/maps/fonts/open_sans/.%2f..%2f0-255`).expect(404); }); }); } From cea4504c873b3710c1a533b378e10d78345fb052 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ester=20Mart=C3=AD=20Vilaseca?= Date: Wed, 20 Oct 2021 11:22:18 +0200 Subject: [PATCH 114/204] [Stack monitoring] Setup mode cleaning post migration (#115556) * Update tests for setup mode * Remove old setup mode renderer and replace it with the react one * Remove unnecessary await * Update tests for setup mode renderer --- .../application/pages/apm/instances.tsx | 2 +- .../application/pages/beats/instances.tsx | 2 +- .../pages/cluster/overview_page.tsx | 2 +- .../pages/elasticsearch/ccr_page.tsx | 2 +- .../pages/elasticsearch/ccr_shard_page.tsx | 2 +- .../elasticsearch/index_advanced_page.tsx | 2 +- .../pages/elasticsearch/index_page.tsx | 2 +- .../pages/elasticsearch/indices_page.tsx | 2 +- .../pages/elasticsearch/ml_jobs_page.tsx | 2 +- .../pages/elasticsearch/node_page.tsx | 2 +- .../pages/elasticsearch/nodes_page.tsx | 2 +- .../application/pages/kibana/instances.tsx | 2 +- .../application/pages/logstash/nodes.tsx | 2 +- .../public/application/route_init.tsx | 2 +- .../public/application/setup_mode/index.ts | 8 - .../setup_mode/setup_mode_renderer.js | 225 ---------------- .../renderers/setup_mode.d.ts} | 0 .../public/components/renderers/setup_mode.js | 22 +- .../components/renderers/setup_mode.test.js | 60 ++--- .../monitoring/public/lib/setup_mode.test.js | 242 ++++++++++-------- 20 files changed, 195 insertions(+), 390 deletions(-) delete mode 100644 x-pack/plugins/monitoring/public/application/setup_mode/index.ts delete mode 100644 x-pack/plugins/monitoring/public/application/setup_mode/setup_mode_renderer.js rename x-pack/plugins/monitoring/public/{application/setup_mode/setup_mode_renderer.d.ts => components/renderers/setup_mode.d.ts} (100%) diff --git a/x-pack/plugins/monitoring/public/application/pages/apm/instances.tsx b/x-pack/plugins/monitoring/public/application/pages/apm/instances.tsx index fedb07fa65a40..2543b054ee5bb 100644 --- a/x-pack/plugins/monitoring/public/application/pages/apm/instances.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/apm/instances.tsx @@ -15,7 +15,7 @@ import { useTable } from '../../hooks/use_table'; import { ApmTemplate } from './apm_template'; // @ts-ignore import { ApmServerInstances } from '../../../components/apm/instances'; -import { SetupModeRenderer } from '../../setup_mode/setup_mode_renderer'; +import { SetupModeRenderer } from '../../../components/renderers/setup_mode'; import { SetupModeContext } from '../../../components/setup_mode/setup_mode_context'; import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; import { APM_SYSTEM_ID } from '../../../../common/constants'; diff --git a/x-pack/plugins/monitoring/public/application/pages/beats/instances.tsx b/x-pack/plugins/monitoring/public/application/pages/beats/instances.tsx index 4611f17159621..b33789f510f2e 100644 --- a/x-pack/plugins/monitoring/public/application/pages/beats/instances.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/beats/instances.tsx @@ -15,7 +15,7 @@ import { useTable } from '../../hooks/use_table'; import { BeatsTemplate } from './beats_template'; // @ts-ignore import { Listing } from '../../../components/beats/listing'; -import { SetupModeRenderer, SetupModeProps } from '../../setup_mode/setup_mode_renderer'; +import { SetupModeRenderer, SetupModeProps } from '../../../components/renderers/setup_mode'; import { SetupModeContext } from '../../../components/setup_mode/setup_mode_context'; import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; import { BEATS_SYSTEM_ID } from '../../../../common/constants'; diff --git a/x-pack/plugins/monitoring/public/application/pages/cluster/overview_page.tsx b/x-pack/plugins/monitoring/public/application/pages/cluster/overview_page.tsx index 04074762c8d22..2ffbc3a75ce05 100644 --- a/x-pack/plugins/monitoring/public/application/pages/cluster/overview_page.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/cluster/overview_page.tsx @@ -14,7 +14,7 @@ import { GlobalStateContext } from '../../contexts/global_state_context'; import { TabMenuItem } from '../page_template'; import { Overview } from '../../../components/cluster/overview'; import { ExternalConfigContext } from '../../contexts/external_config_context'; -import { SetupModeRenderer, SetupModeProps } from '../../setup_mode/setup_mode_renderer'; +import { SetupModeRenderer, SetupModeProps } from '../../../components/renderers/setup_mode'; import { SetupModeContext } from '../../../components/setup_mode/setup_mode_context'; import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; import { fetchClusters } from '../../../lib/fetch_clusters'; diff --git a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/ccr_page.tsx b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/ccr_page.tsx index cb37705c959aa..2ab18331d1cdb 100644 --- a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/ccr_page.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/ccr_page.tsx @@ -13,7 +13,7 @@ import { GlobalStateContext } from '../../contexts/global_state_context'; // @ts-ignore import { Ccr } from '../../../components/elasticsearch/ccr'; import { ComponentProps } from '../../route_init'; -import { SetupModeRenderer } from '../../setup_mode/setup_mode_renderer'; +import { SetupModeRenderer } from '../../../components/renderers/setup_mode'; import { SetupModeContext } from '../../../components/setup_mode/setup_mode_context'; import { AlertsByName } from '../../../alerts/types'; import { fetchAlerts } from '../../../lib/fetch_alerts'; diff --git a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/ccr_shard_page.tsx b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/ccr_shard_page.tsx index 29cf9ade8d997..2ded26df16323 100644 --- a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/ccr_shard_page.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/ccr_shard_page.tsx @@ -15,7 +15,7 @@ import { GlobalStateContext } from '../../contexts/global_state_context'; // @ts-ignore import { CcrShardReact } from '../../../components/elasticsearch/ccr_shard'; import { ComponentProps } from '../../route_init'; -import { SetupModeRenderer } from '../../setup_mode/setup_mode_renderer'; +import { SetupModeRenderer } from '../../../components/renderers/setup_mode'; import { SetupModeContext } from '../../../components/setup_mode/setup_mode_context'; import { AlertsByName } from '../../../alerts/types'; import { fetchAlerts } from '../../../lib/fetch_alerts'; diff --git a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/index_advanced_page.tsx b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/index_advanced_page.tsx index f2f2ec36b7cd9..c51027636b287 100644 --- a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/index_advanced_page.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/index_advanced_page.tsx @@ -11,7 +11,7 @@ import { useParams } from 'react-router-dom'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; import { GlobalStateContext } from '../../contexts/global_state_context'; import { ComponentProps } from '../../route_init'; -import { SetupModeRenderer, SetupModeProps } from '../../setup_mode/setup_mode_renderer'; +import { SetupModeRenderer, SetupModeProps } from '../../../components/renderers/setup_mode'; import { SetupModeContext } from '../../../components/setup_mode/setup_mode_context'; import { useCharts } from '../../hooks/use_charts'; import { ItemTemplate } from './item_template'; diff --git a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/index_page.tsx b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/index_page.tsx index 8e70a99e67914..422f051c7d718 100644 --- a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/index_page.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/index_page.tsx @@ -13,7 +13,7 @@ import { GlobalStateContext } from '../../contexts/global_state_context'; // @ts-ignore import { IndexReact } from '../../../components/elasticsearch/index/index_react'; import { ComponentProps } from '../../route_init'; -import { SetupModeRenderer, SetupModeProps } from '../../setup_mode/setup_mode_renderer'; +import { SetupModeRenderer, SetupModeProps } from '../../../components/renderers/setup_mode'; import { SetupModeContext } from '../../../components/setup_mode/setup_mode_context'; import { useCharts } from '../../hooks/use_charts'; import { ItemTemplate } from './item_template'; diff --git a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/indices_page.tsx b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/indices_page.tsx index 277bde2ac35cb..6618db7eebe66 100644 --- a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/indices_page.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/indices_page.tsx @@ -12,7 +12,7 @@ import { useKibana } from '../../../../../../../src/plugins/kibana_react/public' import { GlobalStateContext } from '../../contexts/global_state_context'; import { ElasticsearchIndices } from '../../../components/elasticsearch'; import { ComponentProps } from '../../route_init'; -import { SetupModeRenderer, SetupModeProps } from '../../setup_mode/setup_mode_renderer'; +import { SetupModeRenderer, SetupModeProps } from '../../../components/renderers/setup_mode'; import { SetupModeContext } from '../../../components/setup_mode/setup_mode_context'; import { useTable } from '../../hooks/use_table'; import { useLocalStorage } from '../../hooks/use_local_storage'; diff --git a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/ml_jobs_page.tsx b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/ml_jobs_page.tsx index b97007f1c1462..46bb4cc20242f 100644 --- a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/ml_jobs_page.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/ml_jobs_page.tsx @@ -12,7 +12,7 @@ import { useKibana } from '../../../../../../../src/plugins/kibana_react/public' import { GlobalStateContext } from '../../contexts/global_state_context'; import { ElasticsearchMLJobs } from '../../../components/elasticsearch'; import { ComponentProps } from '../../route_init'; -import { SetupModeRenderer } from '../../setup_mode/setup_mode_renderer'; +import { SetupModeRenderer } from '../../../components/renderers/setup_mode'; import { SetupModeContext } from '../../../components/setup_mode/setup_mode_context'; import { useTable } from '../../hooks/use_table'; import type { MLJobs } from '../../../types'; diff --git a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/node_page.tsx b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/node_page.tsx index b2d6fb94183ec..a75c8447a3561 100644 --- a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/node_page.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/node_page.tsx @@ -13,7 +13,7 @@ import { useKibana } from '../../../../../../../src/plugins/kibana_react/public' import { GlobalStateContext } from '../../contexts/global_state_context'; import { NodeReact } from '../../../components/elasticsearch'; import { ComponentProps } from '../../route_init'; -import { SetupModeRenderer, SetupModeProps } from '../../setup_mode/setup_mode_renderer'; +import { SetupModeRenderer, SetupModeProps } from '../../../components/renderers/setup_mode'; import { SetupModeContext } from '../../../components/setup_mode/setup_mode_context'; import { useLocalStorage } from '../../hooks/use_local_storage'; import { useCharts } from '../../hooks/use_charts'; diff --git a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/nodes_page.tsx b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/nodes_page.tsx index ac7e267cbc9ac..9933188b887d5 100644 --- a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/nodes_page.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/nodes_page.tsx @@ -13,7 +13,7 @@ import { GlobalStateContext } from '../../contexts/global_state_context'; import { ExternalConfigContext } from '../../contexts/external_config_context'; import { ElasticsearchNodes } from '../../../components/elasticsearch'; import { ComponentProps } from '../../route_init'; -import { SetupModeRenderer, SetupModeProps } from '../../setup_mode/setup_mode_renderer'; +import { SetupModeRenderer, SetupModeProps } from '../../../components/renderers/setup_mode'; import { SetupModeContext } from '../../../components/setup_mode/setup_mode_context'; import { useTable } from '../../hooks/use_table'; import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; diff --git a/x-pack/plugins/monitoring/public/application/pages/kibana/instances.tsx b/x-pack/plugins/monitoring/public/application/pages/kibana/instances.tsx index 076e9413216fb..a27c1418eabc1 100644 --- a/x-pack/plugins/monitoring/public/application/pages/kibana/instances.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/kibana/instances.tsx @@ -16,7 +16,7 @@ import { KibanaTemplate } from './kibana_template'; // @ts-ignore import { KibanaInstances } from '../../../components/kibana/instances'; // @ts-ignore -import { SetupModeRenderer, SetupModeProps } from '../../setup_mode/setup_mode_renderer'; +import { SetupModeRenderer, SetupModeProps } from '../../../components/renderers/setup_mode'; import { SetupModeContext } from '../../../components/setup_mode/setup_mode_context'; import { BreadcrumbContainer } from '../../hooks/use_breadcrumbs'; import { AlertsByName } from '../../../alerts/types'; diff --git a/x-pack/plugins/monitoring/public/application/pages/logstash/nodes.tsx b/x-pack/plugins/monitoring/public/application/pages/logstash/nodes.tsx index 0fd10a93bcd83..447a7b1792fb9 100644 --- a/x-pack/plugins/monitoring/public/application/pages/logstash/nodes.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/logstash/nodes.tsx @@ -13,7 +13,7 @@ import { ComponentProps } from '../../route_init'; // @ts-ignore import { Listing } from '../../../components/logstash/listing'; import { LogstashTemplate } from './logstash_template'; -import { SetupModeRenderer } from '../../setup_mode/setup_mode_renderer'; +import { SetupModeRenderer } from '../../../components/renderers/setup_mode'; import { SetupModeContext } from '../../../components/setup_mode/setup_mode_context'; import { useTable } from '../../hooks/use_table'; import { LOGSTASH_SYSTEM_ID, RULE_LOGSTASH_VERSION_MISMATCH } from '../../../../common/constants'; diff --git a/x-pack/plugins/monitoring/public/application/route_init.tsx b/x-pack/plugins/monitoring/public/application/route_init.tsx index 52780aa280707..92def5b2c36f2 100644 --- a/x-pack/plugins/monitoring/public/application/route_init.tsx +++ b/x-pack/plugins/monitoring/public/application/route_init.tsx @@ -9,7 +9,7 @@ import { Route, Redirect, useLocation } from 'react-router-dom'; import { useClusters } from './hooks/use_clusters'; import { GlobalStateContext } from './contexts/global_state_context'; import { getClusterFromClusters } from '../lib/get_cluster_from_clusters'; -import { isInSetupMode } from './setup_mode'; +import { isInSetupMode } from '../lib/setup_mode'; import { LoadingPage } from './pages/loading_page'; export interface ComponentProps { diff --git a/x-pack/plugins/monitoring/public/application/setup_mode/index.ts b/x-pack/plugins/monitoring/public/application/setup_mode/index.ts deleted file mode 100644 index 57d734fc6d056..0000000000000 --- a/x-pack/plugins/monitoring/public/application/setup_mode/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export * from '../../lib/setup_mode'; diff --git a/x-pack/plugins/monitoring/public/application/setup_mode/setup_mode_renderer.js b/x-pack/plugins/monitoring/public/application/setup_mode/setup_mode_renderer.js deleted file mode 100644 index df524fa99ae53..0000000000000 --- a/x-pack/plugins/monitoring/public/application/setup_mode/setup_mode_renderer.js +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { Fragment } from 'react'; -import { - getSetupModeState, - initSetupModeState, - updateSetupModeData, - disableElasticsearchInternalCollection, - toggleSetupMode, - setSetupModeMenuItem, -} from '../../lib/setup_mode'; -import { Flyout } from '../../components/metricbeat_migration/flyout'; -import { - EuiBottomBar, - EuiButton, - EuiFlexGroup, - EuiFlexItem, - EuiTextColor, - EuiIcon, - EuiSpacer, -} from '@elastic/eui'; -import { findNewUuid } from '../../components/renderers/lib/find_new_uuid'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { GlobalStateContext } from '../../application/contexts/global_state_context'; -import { withKibana } from '../../../../../../src/plugins/kibana_react/public'; -import { useRequestErrorHandler } from '../hooks/use_request_error_handler'; - -class WrappedSetupModeRenderer extends React.Component { - globalState; - state = { - renderState: false, - isFlyoutOpen: false, - instance: null, - newProduct: null, - isSettingUpNew: false, - }; - - UNSAFE_componentWillMount() { - this.globalState = this.context; - const { kibana, onHttpError } = this.props; - initSetupModeState(this.globalState, kibana.services.http, onHttpError, (_oldData) => { - const newState = { renderState: true }; - - const { productName } = this.props; - if (!productName) { - this.setState(newState); - return; - } - - const setupModeState = getSetupModeState(); - if (!setupModeState.enabled || !setupModeState.data) { - this.setState(newState); - return; - } - - const data = setupModeState.data[productName]; - const oldData = _oldData ? _oldData[productName] : null; - if (data && oldData) { - const newUuid = findNewUuid(Object.keys(oldData.byUuid), Object.keys(data.byUuid)); - if (newUuid) { - newState.newProduct = data.byUuid[newUuid]; - } - } - - this.setState(newState); - }); - setSetupModeMenuItem(); - } - - reset() { - this.setState({ - renderState: false, - isFlyoutOpen: false, - instance: null, - newProduct: null, - isSettingUpNew: false, - }); - } - - getFlyout(data, meta) { - const { productName } = this.props; - const { isFlyoutOpen, instance, isSettingUpNew, newProduct } = this.state; - if (!data || !isFlyoutOpen) { - return null; - } - - let product = null; - if (newProduct) { - product = newProduct; - } - // For new instance discovery flow, we pass in empty instance object - else if (instance && Object.keys(instance).length) { - product = data.byUuid[instance.uuid]; - } - - if (!product) { - const uuids = Object.values(data.byUuid); - if (uuids.length && !isSettingUpNew) { - product = uuids[0]; - } else { - product = { - isNetNewUser: true, - }; - } - } - - return ( - this.reset()} - productName={productName} - product={product} - meta={meta} - instance={instance} - updateProduct={updateSetupModeData} - isSettingUpNew={isSettingUpNew} - /> - ); - } - - getBottomBar(setupModeState) { - if (!setupModeState.enabled || setupModeState.hideBottomBar) { - return null; - } - - return ( - - - - - - - - - , - }} - /> - - - - - - - - toggleSetupMode(false)} - > - {i18n.translate('xpack.monitoring.setupMode.exit', { - defaultMessage: `Exit setup mode`, - })} - - - - - - - - ); - } - - async shortcutToFinishMigration() { - await disableElasticsearchInternalCollection(); - await updateSetupModeData(); - } - - render() { - const { render, productName } = this.props; - const setupModeState = getSetupModeState(); - - let data = { byUuid: {} }; - if (setupModeState.data) { - if (productName && setupModeState.data[productName]) { - data = setupModeState.data[productName]; - } else if (setupModeState.data) { - data = setupModeState.data; - } - } - - const meta = setupModeState.data ? setupModeState.data._meta : null; - - return render({ - setupMode: { - data, - meta, - enabled: setupModeState.enabled, - productName, - updateSetupModeData, - shortcutToFinishMigration: () => this.shortcutToFinishMigration(), - openFlyout: (instance, isSettingUpNew) => - this.setState({ isFlyoutOpen: true, instance, isSettingUpNew }), - closeFlyout: () => this.setState({ isFlyoutOpen: false }), - }, - flyoutComponent: this.getFlyout(data, meta), - bottomBarComponent: this.getBottomBar(setupModeState), - }); - } -} - -function withErrorHandler(Component) { - return function WrappedComponent(props) { - const handleRequestError = useRequestErrorHandler(); - return ; - }; -} - -WrappedSetupModeRenderer.contextType = GlobalStateContext; -export const SetupModeRenderer = withKibana(withErrorHandler(WrappedSetupModeRenderer)); diff --git a/x-pack/plugins/monitoring/public/application/setup_mode/setup_mode_renderer.d.ts b/x-pack/plugins/monitoring/public/components/renderers/setup_mode.d.ts similarity index 100% rename from x-pack/plugins/monitoring/public/application/setup_mode/setup_mode_renderer.d.ts rename to x-pack/plugins/monitoring/public/components/renderers/setup_mode.d.ts diff --git a/x-pack/plugins/monitoring/public/components/renderers/setup_mode.js b/x-pack/plugins/monitoring/public/components/renderers/setup_mode.js index 573f7e7e33c5e..cfa57559d2bc9 100644 --- a/x-pack/plugins/monitoring/public/components/renderers/setup_mode.js +++ b/x-pack/plugins/monitoring/public/components/renderers/setup_mode.js @@ -27,8 +27,12 @@ import { import { findNewUuid } from './lib/find_new_uuid'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; +import { GlobalStateContext } from '../../application/contexts/global_state_context'; +import { withKibana } from '../../../../../../src/plugins/kibana_react/public'; +import { useRequestErrorHandler } from '../../application/hooks/use_request_error_handler'; -export class SetupModeRenderer extends React.Component { +export class WrappedSetupModeRenderer extends React.Component { + globalState; state = { renderState: false, isFlyoutOpen: false, @@ -38,9 +42,11 @@ export class SetupModeRenderer extends React.Component { }; UNSAFE_componentWillMount() { - const { scope, injector } = this.props; - initSetupModeState(scope, injector, (_oldData) => { + this.globalState = this.context; + const { kibana, onHttpError } = this.props; + initSetupModeState(this.globalState, kibana.services.http, onHttpError, (_oldData) => { const newState = { renderState: true }; + const { productName } = this.props; if (!productName) { this.setState(newState); @@ -207,3 +213,13 @@ export class SetupModeRenderer extends React.Component { }); } } + +function withErrorHandler(Component) { + return function WrappedComponent(props) { + const handleRequestError = useRequestErrorHandler(); + return ; + }; +} + +WrappedSetupModeRenderer.contextType = GlobalStateContext; +export const SetupModeRenderer = withKibana(withErrorHandler(WrappedSetupModeRenderer)); diff --git a/x-pack/plugins/monitoring/public/components/renderers/setup_mode.test.js b/x-pack/plugins/monitoring/public/components/renderers/setup_mode.test.js index cb1593d084366..9672da8b8088a 100644 --- a/x-pack/plugins/monitoring/public/components/renderers/setup_mode.test.js +++ b/x-pack/plugins/monitoring/public/components/renderers/setup_mode.test.js @@ -9,6 +9,14 @@ import React, { Fragment } from 'react'; import { shallow } from 'enzyme'; import { ELASTICSEARCH_SYSTEM_ID } from '../../../common/constants'; +const kibanaMock = { + services: { + http: jest.fn(), + }, +}; + +const onHttpErrorMock = jest.fn(); + describe('SetupModeRenderer', () => { beforeEach(() => jest.resetModules()); @@ -21,16 +29,14 @@ describe('SetupModeRenderer', () => { updateSetupModeData: () => {}, setSetupModeMenuItem: () => {}, })); - const SetupModeRenderer = require('./setup_mode').SetupModeRenderer; + const SetupModeRenderer = require('./setup_mode').WrappedSetupModeRenderer; const ChildComponent = () =>

Hi

; - const scope = {}; - const injector = {}; const component = shallow( ( {flyoutComponent} @@ -57,16 +63,14 @@ describe('SetupModeRenderer', () => { updateSetupModeData: () => {}, setSetupModeMenuItem: () => {}, })); - const SetupModeRenderer = require('./setup_mode').SetupModeRenderer; + const SetupModeRenderer = require('./setup_mode').WrappedSetupModeRenderer; const ChildComponent = () =>

Hi

; - const scope = {}; - const injector = {}; const component = shallow( ( {flyoutComponent} @@ -95,16 +99,14 @@ describe('SetupModeRenderer', () => { updateSetupModeData: () => {}, setSetupModeMenuItem: () => {}, })); - const SetupModeRenderer = require('./setup_mode').SetupModeRenderer; + const SetupModeRenderer = require('./setup_mode').WrappedSetupModeRenderer; const ChildComponent = () =>

Hi

; - const scope = {}; - const injector = {}; const component = shallow( ( {flyoutComponent} @@ -135,16 +137,14 @@ describe('SetupModeRenderer', () => { updateSetupModeData: () => {}, setSetupModeMenuItem: () => {}, })); - const SetupModeRenderer = require('./setup_mode').SetupModeRenderer; + const SetupModeRenderer = require('./setup_mode').WrappedSetupModeRenderer; const ChildComponent = () =>

Hi

; - const scope = {}; - const injector = {}; const component = shallow( ( {flyoutComponent} @@ -176,7 +176,7 @@ describe('SetupModeRenderer', () => { _meta: {}, }, }), - initSetupModeState: (_scope, _injectir, cb) => { + initSetupModeState: (_globalState, _httpService, _onError, cb) => { setTimeout(() => { cb({ elasticsearch: { @@ -190,16 +190,14 @@ describe('SetupModeRenderer', () => { updateSetupModeData: () => {}, setSetupModeMenuItem: () => {}, })); - const SetupModeRenderer = require('./setup_mode').SetupModeRenderer; + const SetupModeRenderer = require('./setup_mode').WrappedSetupModeRenderer; const ChildComponent = () =>

Hi

; - const scope = {}; - const injector = {}; const component = shallow( ( {flyoutComponent} @@ -235,7 +233,7 @@ describe('SetupModeRenderer', () => { _meta: {}, }, }), - initSetupModeState: (_scope, _injectir, cb) => { + initSetupModeState: (_globalState, _httpService, _onError, cb) => { setTimeout(() => { cb({ elasticsearch: { @@ -249,16 +247,14 @@ describe('SetupModeRenderer', () => { updateSetupModeData: () => {}, setSetupModeMenuItem, })); - const SetupModeRenderer = require('./setup_mode').SetupModeRenderer; + const SetupModeRenderer = require('./setup_mode').WrappedSetupModeRenderer; const ChildComponent = () =>

Hi

; - const scope = {}; - const injector = {}; const component = shallow( ( {flyoutComponent} diff --git a/x-pack/plugins/monitoring/public/lib/setup_mode.test.js b/x-pack/plugins/monitoring/public/lib/setup_mode.test.js index 47cae9c4f0851..bacc305764d68 100644 --- a/x-pack/plugins/monitoring/public/lib/setup_mode.test.js +++ b/x-pack/plugins/monitoring/public/lib/setup_mode.test.js @@ -11,11 +11,8 @@ let getSetupModeState; let updateSetupModeData; let setSetupModeMenuItem; -jest.mock('./ajax_error_handler', () => ({ - ajaxErrorHandlersProvider: (err) => { - throw err; - }, -})); +const handleErrorsMock = jest.fn(); +const callbackMock = jest.fn(); jest.mock('react-dom', () => ({ render: jest.fn(), @@ -25,7 +22,6 @@ jest.mock('../legacy_shims', () => { return { Legacy: { shims: { - getAngularInjector: () => ({ get: () => ({ get: () => 'utc' }) }), toastNotifications: { addDanger: jest.fn(), }, @@ -35,41 +31,8 @@ jest.mock('../legacy_shims', () => { }; }); -let data = {}; - -const injectorModulesMock = { - globalState: { - save: jest.fn(), - }, - Private: (module) => module, - $http: { - post: jest.fn().mockImplementation(() => { - return { data }; - }), - }, - $executor: { - run: jest.fn(), - }, -}; - -const angularStateMock = { - injector: { - get: (module) => { - return injectorModulesMock[module] || {}; - }, - }, - scope: { - $apply: (fn) => fn && fn(), - $evalAsync: (fn) => fn && fn(), - }, -}; - -// We are no longer waiting for setup mode data to be fetched when enabling -// so we need to wait for the next tick for the async action to finish - function setModulesAndMocks() { jest.clearAllMocks().resetModules(); - injectorModulesMock.globalState.inSetupMode = false; const setupMode = require('./setup_mode'); toggleSetupMode = setupMode.toggleSetupMode; @@ -83,53 +46,76 @@ function waitForSetupModeData() { return new Promise((resolve) => process.nextTick(resolve)); } -xdescribe('setup_mode', () => { +describe('setup_mode', () => { beforeEach(async () => { setModulesAndMocks(); }); describe('setup', () => { - it('should require angular state', async () => { - let error; - try { - toggleSetupMode(true); - } catch (err) { - error = err; - } - expect(error.message).toEqual( - 'Unable to interact with setup ' + - 'mode because the angular injector was not previously set. This needs to be ' + - 'set by calling `initSetupModeState`.' - ); - }); - it('should enable toggle mode', async () => { - await initSetupModeState(angularStateMock.scope, angularStateMock.injector); + const globalState = { + inSetupMode: false, + save: jest.fn(), + }; + const httpServiceMock = { + post: jest.fn(), + }; + + await initSetupModeState(globalState, httpServiceMock, handleErrorsMock, callbackMock); toggleSetupMode(true); - expect(injectorModulesMock.globalState.inSetupMode).toBe(true); + expect(globalState.inSetupMode).toBe(true); }); it('should disable toggle mode', async () => { - await initSetupModeState(angularStateMock.scope, angularStateMock.injector); + const globalState = { + inSetupMode: true, + save: jest.fn(), + }; + const httpServiceMock = { + post: jest.fn(), + }; + const handleErrorsMock = jest.fn(); + const callbackMock = jest.fn(); + await initSetupModeState(globalState, httpServiceMock, handleErrorsMock, callbackMock); toggleSetupMode(false); - expect(injectorModulesMock.globalState.inSetupMode).toBe(false); + expect(globalState.inSetupMode).toBe(false); }); it('should set top nav config', async () => { + const globalState = { + inSetupMode: false, + save: jest.fn(), + }; + const httpServiceMock = { + post: jest.fn(), + }; + const render = require('react-dom').render; - await initSetupModeState(angularStateMock.scope, angularStateMock.injector); + + await initSetupModeState(globalState, httpServiceMock, handleErrorsMock, callbackMock); setSetupModeMenuItem(); toggleSetupMode(true); + expect(render.mock.calls.length).toBe(2); }); }); describe('in setup mode', () => { - afterEach(async () => { - data = {}; - }); - it('should not fetch data if the user does not have sufficient permissions', async () => { + const globalState = { + inSetupMode: false, + save: jest.fn(), + }; + const httpServiceMock = { + post: jest.fn().mockReturnValue( + Promise.resolve({ + _meta: { + hasPermissions: false, + }, + }) + ), + }; + const addDanger = jest.fn(); jest.doMock('../legacy_shims', () => ({ Legacy: { @@ -141,13 +127,9 @@ xdescribe('setup_mode', () => { }, }, })); - data = { - _meta: { - hasPermissions: false, - }, - }; + setModulesAndMocks(); - await initSetupModeState(angularStateMock.scope, angularStateMock.injector); + await initSetupModeState(globalState, httpServiceMock, handleErrorsMock, callbackMock); toggleSetupMode(true); await waitForSetupModeData(); @@ -160,78 +142,122 @@ xdescribe('setup_mode', () => { }); it('should set the newly discovered cluster uuid', async () => { + const globalState = { + inSetupMode: false, + cluster_uuid: undefined, + save: jest.fn(), + }; const clusterUuid = '1ajy'; - data = { - _meta: { - liveClusterUuid: clusterUuid, - hasPermissions: true, - }, - elasticsearch: { - byUuid: { - 123: { - isPartiallyMigrated: true, + const httpServiceMock = { + post: jest.fn().mockReturnValue( + Promise.resolve({ + _meta: { + liveClusterUuid: clusterUuid, + hasPermissions: true, }, - }, - }, + elasticsearch: { + byUuid: { + 123: { + isPartiallyMigrated: true, + }, + }, + }, + }) + ), }; - await initSetupModeState(angularStateMock.scope, angularStateMock.injector); + + await initSetupModeState(globalState, httpServiceMock, handleErrorsMock, callbackMock); toggleSetupMode(true); await waitForSetupModeData(); - expect(injectorModulesMock.globalState.cluster_uuid).toBe(clusterUuid); + expect(globalState.cluster_uuid).toBe(clusterUuid); }); it('should fetch data for a given cluster', async () => { const clusterUuid = '1ajy'; - data = { - _meta: { - liveClusterUuid: clusterUuid, - hasPermissions: true, - }, - elasticsearch: { - byUuid: { - 123: { - isPartiallyMigrated: true, + const globalState = { + inSetupMode: false, + cluster_uuid: clusterUuid, + save: jest.fn(), + }; + const httpServiceMock = { + post: jest.fn().mockReturnValue( + Promise.resolve({ + _meta: { + liveClusterUuid: clusterUuid, + hasPermissions: true, }, - }, - }, + elasticsearch: { + byUuid: { + 123: { + isPartiallyMigrated: true, + }, + }, + }, + }) + ), }; - await initSetupModeState(angularStateMock.scope, angularStateMock.injector); + await initSetupModeState(globalState, httpServiceMock, handleErrorsMock, callbackMock); toggleSetupMode(true); await waitForSetupModeData(); - expect(injectorModulesMock.$http.post).toHaveBeenCalledWith( + expect(httpServiceMock.post).toHaveBeenCalledWith( `../api/monitoring/v1/setup/collection/cluster/${clusterUuid}`, - { - ccs: undefined, - } + { body: '{}' } ); }); it('should fetch data for a single node', async () => { - await initSetupModeState(angularStateMock.scope, angularStateMock.injector); + const clusterUuid = '1ajy'; + const globalState = { + inSetupMode: false, + save: jest.fn(), + }; + const httpServiceMock = { + post: jest.fn().mockReturnValue( + Promise.resolve({ + _meta: { + liveClusterUuid: clusterUuid, + hasPermissions: true, + }, + elasticsearch: { + byUuid: { + 123: { + isPartiallyMigrated: true, + }, + }, + }, + }) + ), + }; + + await initSetupModeState(globalState, httpServiceMock, handleErrorsMock, callbackMock); toggleSetupMode(true); await waitForSetupModeData(); - injectorModulesMock.$http.post.mockClear(); await updateSetupModeData('45asd'); - expect(injectorModulesMock.$http.post).toHaveBeenCalledWith( + expect(httpServiceMock.post).toHaveBeenCalledWith( '../api/monitoring/v1/setup/collection/node/45asd', - { - ccs: undefined, - } + { body: '{}' } ); }); it('should fetch data without a cluster uuid', async () => { - initSetupModeState(angularStateMock.scope, angularStateMock.injector); + const globalState = { + inSetupMode: false, + save: jest.fn(), + }; + const httpServiceMock = { + post: jest.fn(), + }; + + await initSetupModeState(globalState, httpServiceMock, handleErrorsMock, callbackMock); await toggleSetupMode(true); - injectorModulesMock.$http.post.mockClear(); await updateSetupModeData(undefined, true); const url = '../api/monitoring/v1/setup/collection/cluster'; - const args = { ccs: undefined }; - expect(injectorModulesMock.$http.post).toHaveBeenCalledWith(url, args); + const args = { body: '{}' }; + expect(httpServiceMock.post).toHaveBeenCalledWith(url, args); }); }); }); From 22e41727810a677ba81de9d28aa91bd3d2ba3c6c Mon Sep 17 00:00:00 2001 From: Robert Oskamp Date: Wed, 20 Oct 2021 11:23:51 +0200 Subject: [PATCH 115/204] [ML] Functional tests - re-enable transform runtime mappings suite (#115547) This PR stabilizes and re-enables the transform runtime mappings tests. --- test/functional/page_objects/time_picker.ts | 3 ++- .../functional/apps/transform/creation_runtime_mappings.ts | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/functional/page_objects/time_picker.ts b/test/functional/page_objects/time_picker.ts index 6cc4fda513ea6..bf2db6c25ad12 100644 --- a/test/functional/page_objects/time_picker.ts +++ b/test/functional/page_objects/time_picker.ts @@ -134,7 +134,7 @@ export class TimePickerPageObject extends FtrService { }); // set from time - await this.retry.waitFor(`endDate is set to ${fromTime}`, async () => { + await this.retry.waitFor(`startDate is set to ${fromTime}`, async () => { await this.testSubjects.click('superDatePickerstartDatePopoverButton'); await this.waitPanelIsGone(panel); panel = await this.getTimePickerPanel(); @@ -150,6 +150,7 @@ export class TimePickerPageObject extends FtrService { }); await this.retry.waitFor('Timepicker popover to close', async () => { + await this.browser.pressKeys(this.browser.keys.ESCAPE); return !(await this.testSubjects.exists('superDatePickerAbsoluteDateInput')); }); diff --git a/x-pack/test/functional/apps/transform/creation_runtime_mappings.ts b/x-pack/test/functional/apps/transform/creation_runtime_mappings.ts index e244c907a76d6..a0b3c636a2f1a 100644 --- a/x-pack/test/functional/apps/transform/creation_runtime_mappings.ts +++ b/x-pack/test/functional/apps/transform/creation_runtime_mappings.ts @@ -34,8 +34,7 @@ export default function ({ getService }: FtrProviderContext) { }, }; - // FLAKY https://github.com/elastic/kibana/issues/113890 - describe.skip('creation with runtime mappings', function () { + describe('creation with runtime mappings', function () { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); await transform.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); From f69fa6a8a2398a72234ed98b4785bc46a1595998 Mon Sep 17 00:00:00 2001 From: Robert Oskamp Date: Wed, 20 Oct 2021 11:25:46 +0200 Subject: [PATCH 116/204] [ML] Functional tests - adjust test retries and waiting conditions (#115592) This PR fixes a few test stability issues, mostly by adding/adjusting retries. * Stabilize data viz full time range selection * Stabilize DFA wizard source data loading * Stabilize feature importance service methods * Stabilize DFA table row existence assertion * Stabilize dashboard embeddable service methods --- .../services/ml/dashboard_embeddables.ts | 10 ++++----- .../ml/data_frame_analytics_creation.ts | 5 ++++- .../ml/data_frame_analytics_results.ts | 22 ++++++++++--------- .../services/ml/data_frame_analytics_table.ts | 5 +++++ .../ml/data_visualizer_index_based.ts | 7 ++++-- x-pack/test/functional/services/ml/index.ts | 1 - 6 files changed, 30 insertions(+), 20 deletions(-) diff --git a/x-pack/test/functional/services/ml/dashboard_embeddables.ts b/x-pack/test/functional/services/ml/dashboard_embeddables.ts index c4fa9f643e69e..0dc5cc8fae2d5 100644 --- a/x-pack/test/functional/services/ml/dashboard_embeddables.ts +++ b/x-pack/test/functional/services/ml/dashboard_embeddables.ts @@ -7,12 +7,10 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; -import { MlCommonUI } from './common_ui'; import { MlDashboardJobSelectionTable } from './dashboard_job_selection_table'; export function MachineLearningDashboardEmbeddablesProvider( { getService }: FtrProviderContext, - mlCommonUI: MlCommonUI, mlDashboardJobSelectionTable: MlDashboardJobSelectionTable ) { const retry = getService('retry'); @@ -22,14 +20,14 @@ export function MachineLearningDashboardEmbeddablesProvider( return { async assertAnomalyChartsEmbeddableInitializerExists() { - await retry.tryForTime(5000, async () => { - await testSubjects.existOrFail('mlAnomalyChartsEmbeddableInitializer'); + await retry.tryForTime(10 * 1000, async () => { + await testSubjects.existOrFail('mlAnomalyChartsEmbeddableInitializer', { timeout: 1000 }); }); }, async assertAnomalyChartsEmbeddableInitializerNotExists() { - await retry.tryForTime(5000, async () => { - await testSubjects.missingOrFail('mlAnomalyChartsEmbeddableInitializer'); + await retry.tryForTime(10 * 1000, async () => { + await testSubjects.missingOrFail('mlAnomalyChartsEmbeddableInitializer', { timeout: 1000 }); }); }, diff --git a/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts b/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts index 45ba4c5c34833..2c375d47b0b3b 100644 --- a/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts +++ b/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts @@ -18,10 +18,11 @@ import { } from '../../../../plugins/ml/common/util/analytics_utils'; export function MachineLearningDataFrameAnalyticsCreationProvider( - { getService }: FtrProviderContext, + { getPageObject, getService }: FtrProviderContext, mlCommonUI: MlCommonUI, mlApi: MlApi ) { + const headerPage = getPageObject('header'); const testSubjects = getService('testSubjects'); const comboBox = getService('comboBox'); const retry = getService('retry'); @@ -111,10 +112,12 @@ export function MachineLearningDataFrameAnalyticsCreationProvider( }, async assertSourceDataPreviewExists() { + await headerPage.waitUntilLoadingHasFinished(); await testSubjects.existOrFail('mlAnalyticsCreationDataGrid loaded', { timeout: 5000 }); }, async assertIndexPreviewHistogramChartButtonExists() { + await headerPage.waitUntilLoadingHasFinished(); await testSubjects.existOrFail('mlAnalyticsCreationDataGridHistogramButton'); }, diff --git a/x-pack/test/functional/services/ml/data_frame_analytics_results.ts b/x-pack/test/functional/services/ml/data_frame_analytics_results.ts index ac728e6b88303..a1bf8c6a65d70 100644 --- a/x-pack/test/functional/services/ml/data_frame_analytics_results.ts +++ b/x-pack/test/functional/services/ml/data_frame_analytics_results.ts @@ -73,7 +73,7 @@ export function MachineLearningDataFrameAnalyticsResultsProvider( async assertTotalFeatureImportanceEvaluatePanelExists() { await testSubjects.existOrFail('mlDFExpandableSection-FeatureImportanceSummary'); - await testSubjects.existOrFail('mlTotalFeatureImportanceChart', { timeout: 5000 }); + await testSubjects.existOrFail('mlTotalFeatureImportanceChart', { timeout: 30 * 1000 }); }, async assertFeatureImportanceDecisionPathElementsExists() { @@ -167,17 +167,19 @@ export function MachineLearningDataFrameAnalyticsResultsProvider( async openFeatureImportancePopover() { this.assertResultsTableNotEmpty(); - const featureImportanceCell = await this.getFirstFeatureImportanceCell(); - await featureImportanceCell.focus(); - const interactionButton = await featureImportanceCell.findByTagName('button'); + await retry.tryForTime(30 * 1000, async () => { + const featureImportanceCell = await this.getFirstFeatureImportanceCell(); + await featureImportanceCell.focus(); + const interactionButton = await featureImportanceCell.findByTagName('button'); - // simulate hover and wait for button to appear - await featureImportanceCell.moveMouseTo(); - await this.waitForInteractionButtonToDisplay(interactionButton); + // simulate hover and wait for button to appear + await featureImportanceCell.moveMouseTo(); + await this.waitForInteractionButtonToDisplay(interactionButton); - // open popover - await interactionButton.click(); - await testSubjects.existOrFail('mlDFAFeatureImportancePopover'); + // open popover + await interactionButton.click(); + await testSubjects.existOrFail('mlDFAFeatureImportancePopover', { timeout: 1000 }); + }); }, async getFirstFeatureImportanceCell(): Promise { diff --git a/x-pack/test/functional/services/ml/data_frame_analytics_table.ts b/x-pack/test/functional/services/ml/data_frame_analytics_table.ts index 5850919f2adc3..0bfb37c6c94f8 100644 --- a/x-pack/test/functional/services/ml/data_frame_analytics_table.ts +++ b/x-pack/test/functional/services/ml/data_frame_analytics_table.ts @@ -196,6 +196,11 @@ export function MachineLearningDataFrameAnalyticsTableProvider({ getService }: F analyticsId: string, shouldBeDisplayed: boolean ) { + await this.waitForRefreshButtonLoaded(); + await testSubjects.click('~mlAnalyticsRefreshListButton'); + await this.waitForRefreshButtonLoaded(); + await testSubjects.existOrFail('mlAnalyticsJobList', { timeout: 30 * 1000 }); + if (shouldBeDisplayed) { await this.filterWithSearchString(analyticsId, 1); } else { diff --git a/x-pack/test/functional/services/ml/data_visualizer_index_based.ts b/x-pack/test/functional/services/ml/data_visualizer_index_based.ts index 7f32968ec4326..6883946452629 100644 --- a/x-pack/test/functional/services/ml/data_visualizer_index_based.ts +++ b/x-pack/test/functional/services/ml/data_visualizer_index_based.ts @@ -33,8 +33,11 @@ export function MachineLearningDataVisualizerIndexBasedProvider({ }, async clickUseFullDataButton(expectedFormattedTotalDocCount: string) { - await testSubjects.clickWhenNotDisabled('dataVisualizerButtonUseFullData'); - await this.assertTotalDocumentCount(expectedFormattedTotalDocCount); + await retry.tryForTime(30 * 1000, async () => { + await testSubjects.clickWhenNotDisabled('dataVisualizerButtonUseFullData'); + await testSubjects.clickWhenNotDisabled('superDatePickerApplyTimeButton'); + await this.assertTotalDocumentCount(expectedFormattedTotalDocCount); + }); }, async assertTotalDocCountHeaderExist() { diff --git a/x-pack/test/functional/services/ml/index.ts b/x-pack/test/functional/services/ml/index.ts index d50ec371d7c23..17302b2782223 100644 --- a/x-pack/test/functional/services/ml/index.ts +++ b/x-pack/test/functional/services/ml/index.ts @@ -67,7 +67,6 @@ export function MachineLearningProvider(context: FtrProviderContext) { const dashboardJobSelectionTable = MachineLearningDashboardJobSelectionTableProvider(context); const dashboardEmbeddables = MachineLearningDashboardEmbeddablesProvider( context, - commonUI, dashboardJobSelectionTable ); From cdd9d0cdef530d31a235724c9154cc2fd08de997 Mon Sep 17 00:00:00 2001 From: Tre Date: Wed, 20 Oct 2021 05:28:02 -0400 Subject: [PATCH 117/204] [QA][refactor] Use ui settings - saved queries (#114820) --- test/functional/apps/discover/_saved_queries.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/functional/apps/discover/_saved_queries.ts b/test/functional/apps/discover/_saved_queries.ts index 832d895fcea3d..b7d19807e563e 100644 --- a/test/functional/apps/discover/_saved_queries.ts +++ b/test/functional/apps/discover/_saved_queries.ts @@ -28,9 +28,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const setUpQueriesWithFilters = async () => { // set up a query with filters and a time filter log.debug('set up a query with filters to save'); - const fromTime = 'Sep 20, 2015 @ 08:00:00.000'; - const toTime = 'Sep 21, 2015 @ 08:00:00.000'; - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + const from = 'Sep 20, 2015 @ 08:00:00.000'; + const to = 'Sep 21, 2015 @ 08:00:00.000'; + await PageObjects.common.setTime({ from, to }); + await PageObjects.common.navigateToApp('discover'); await filterBar.addFilter('extension.raw', 'is one of', 'jpg'); await queryBar.setQuery('response:200'); }; @@ -54,6 +55,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await kibanaServer.importExport.unload('test/functional/fixtures/kbn_archiver/discover'); await esArchiver.unload('test/functional/fixtures/es_archiver/date_nested'); await esArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional'); + await PageObjects.common.unsetTime(); }); describe('saved query selection', () => { From c761909c1e15e68c17f4d067068fa308021f5470 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Wed, 20 Oct 2021 12:28:18 +0300 Subject: [PATCH 118/204] [Cases] Fix connector's cypress test (#115531) --- .../cypress/integration/cases/connectors.spec.ts | 3 +-- .../security_solution/cypress/screens/configure_cases.ts | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/cases/connectors.spec.ts b/x-pack/plugins/security_solution/cypress/integration/cases/connectors.spec.ts index 69b623de0b43c..287d86c6fba9e 100644 --- a/x-pack/plugins/security_solution/cypress/integration/cases/connectors.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/cases/connectors.spec.ts @@ -20,8 +20,7 @@ import { loginAndWaitForPageWithoutDateRange } from '../../tasks/login'; import { CASES_URL } from '../../urls/navigation'; -// Skipping flakey test: https://github.com/elastic/kibana/issues/115438 -describe.skip('Cases connectors', () => { +describe('Cases connectors', () => { const configureResult = { connector: { id: 'e271c3b8-f702-4fbc-98e0-db942b573bbd', diff --git a/x-pack/plugins/security_solution/cypress/screens/configure_cases.ts b/x-pack/plugins/security_solution/cypress/screens/configure_cases.ts index 1014835f81efe..c9ed5299c0336 100644 --- a/x-pack/plugins/security_solution/cypress/screens/configure_cases.ts +++ b/x-pack/plugins/security_solution/cypress/screens/configure_cases.ts @@ -24,7 +24,7 @@ export const SERVICE_NOW_CONNECTOR_CARD = '[data-test-subj=".servicenow-card"]'; export const TOASTER = '[data-test-subj="euiToastHeader"]'; -export const URL = '[data-test-subj="apiUrlFromInput"]'; +export const URL = '[data-test-subj="credentialsApiUrlFromInput"]'; export const USERNAME = '[data-test-subj="connector-servicenow-username-form-input"]'; From d2c6c4104cd7587ef2342fac755ff6eca7092862 Mon Sep 17 00:00:00 2001 From: Brian Seeders Date: Wed, 20 Oct 2021 05:43:24 -0400 Subject: [PATCH 119/204] [buildkite] Add uptime playwright tests to PR pipeline (#115590) --- .buildkite/pipelines/pull_request/uptime.yml | 11 +++++++++++ .../scripts/pipelines/pull_request/pipeline.js | 4 ++++ .buildkite/scripts/steps/functional/common.sh | 2 ++ .buildkite/scripts/steps/functional/uptime.sh | 17 +++++++++++++++++ x-pack/plugins/uptime/e2e/config.ts | 2 +- x-pack/plugins/uptime/e2e/playwright_start.ts | 15 +++++---------- x-pack/plugins/uptime/scripts/e2e.js | 18 +++++++++++++----- 7 files changed, 53 insertions(+), 16 deletions(-) create mode 100644 .buildkite/pipelines/pull_request/uptime.yml create mode 100755 .buildkite/scripts/steps/functional/uptime.sh diff --git a/.buildkite/pipelines/pull_request/uptime.yml b/.buildkite/pipelines/pull_request/uptime.yml new file mode 100644 index 0000000000000..60fdea1add04c --- /dev/null +++ b/.buildkite/pipelines/pull_request/uptime.yml @@ -0,0 +1,11 @@ +steps: + - command: .buildkite/scripts/steps/functional/uptime.sh + label: 'Uptime @elastic/synthetics Tests' + agents: + queue: ci-group-6 + depends_on: build + timeout_in_minutes: 120 + retry: + automatic: + - exit_status: '*' + limit: 1 diff --git a/.buildkite/scripts/pipelines/pull_request/pipeline.js b/.buildkite/scripts/pipelines/pull_request/pipeline.js index 028c90020a0b8..7b5c944d31c1c 100644 --- a/.buildkite/scripts/pipelines/pull_request/pipeline.js +++ b/.buildkite/scripts/pipelines/pull_request/pipeline.js @@ -73,6 +73,10 @@ const uploadPipeline = (pipelineContent) => { // pipeline.push(getPipeline('.buildkite/pipelines/pull_request/apm_cypress.yml')); // } + if (await doAnyChangesMatch([/^x-pack\/plugins\/uptime/])) { + pipeline.push(getPipeline('.buildkite/pipelines/pull_request/uptime.yml')); + } + pipeline.push(getPipeline('.buildkite/pipelines/pull_request/post_build.yml')); uploadPipeline(pipeline.join('\n')); diff --git a/.buildkite/scripts/steps/functional/common.sh b/.buildkite/scripts/steps/functional/common.sh index b60ed835799e5..bedd22c53c7ec 100755 --- a/.buildkite/scripts/steps/functional/common.sh +++ b/.buildkite/scripts/steps/functional/common.sh @@ -2,6 +2,8 @@ set -euo pipefail +# Note, changes here might also need to be made in other scripts, e.g. uptime.sh + source .buildkite/scripts/common/util.sh .buildkite/scripts/bootstrap.sh diff --git a/.buildkite/scripts/steps/functional/uptime.sh b/.buildkite/scripts/steps/functional/uptime.sh new file mode 100755 index 0000000000000..5a59f4dfa48bd --- /dev/null +++ b/.buildkite/scripts/steps/functional/uptime.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +set -euo pipefail + +source .buildkite/scripts/common/util.sh + +.buildkite/scripts/bootstrap.sh +.buildkite/scripts/download_build_artifacts.sh + +export JOB=kibana-uptime-playwright + +echo "--- Uptime @elastic/synthetics Tests" + +cd "$XPACK_DIR" + +checks-reporter-with-killswitch "Uptime @elastic/synthetics Tests" \ + node plugins/uptime/scripts/e2e.js --kibana-install-dir "$KIBANA_BUILD_LOCATION" diff --git a/x-pack/plugins/uptime/e2e/config.ts b/x-pack/plugins/uptime/e2e/config.ts index 70cc57247d490..c5d573afccd96 100644 --- a/x-pack/plugins/uptime/e2e/config.ts +++ b/x-pack/plugins/uptime/e2e/config.ts @@ -39,7 +39,7 @@ async function config({ readConfigFile }: FtrConfigProviderContext) { '--csp.warnLegacyBrowsers=false', // define custom kibana server args here `--elasticsearch.ssl.certificateAuthorities=${CA_CERT_PATH}`, - `--elasticsearch.ignoreVersionMismatch=true`, + `--elasticsearch.ignoreVersionMismatch=${process.env.CI ? 'false' : 'true'}`, `--uiSettings.overrides.theme:darkMode=true`, `--elasticsearch.username=kibana_system`, `--elasticsearch.password=changeme`, diff --git a/x-pack/plugins/uptime/e2e/playwright_start.ts b/x-pack/plugins/uptime/e2e/playwright_start.ts index aedb255b058be..5949339c1ba25 100644 --- a/x-pack/plugins/uptime/e2e/playwright_start.ts +++ b/x-pack/plugins/uptime/e2e/playwright_start.ts @@ -15,15 +15,10 @@ import './journeys'; export function playwrightRunTests() { return async ({ getService }: any) => { - try { - const result = await playwrightStart(getService); - - if (result && result.uptime.status !== 'succeeded') { - process.exit(1); - } - } catch (error) { - console.error('errors: ', error); - process.exit(1); + const result = await playwrightStart(getService); + + if (result && result.uptime.status !== 'succeeded') { + throw new Error('Tests failed'); } }; } @@ -42,7 +37,7 @@ async function playwrightStart(getService: any) { const res = await playwrightRun({ params: { kibanaUrl }, - playwrightOptions: { chromiumSandbox: false, timeout: 60 * 1000 }, + playwrightOptions: { headless: true, chromiumSandbox: false, timeout: 60 * 1000 }, }); console.log('Removing esArchiver...'); diff --git a/x-pack/plugins/uptime/scripts/e2e.js b/x-pack/plugins/uptime/scripts/e2e.js index e2a8dfaf25c93..e7c0cb612646d 100644 --- a/x-pack/plugins/uptime/scripts/e2e.js +++ b/x-pack/plugins/uptime/scripts/e2e.js @@ -28,9 +28,14 @@ const { argv } = yargs(process.argv.slice(2)) type: 'boolean', description: 'Opens the Playwright Test Runner', }) + .option('kibana-install-dir', { + default: '', + type: 'string', + description: 'Path to the Kibana install directory', + }) .help(); -const { server, runner, open } = argv; +const { server, runner, open, kibanaInstallDir } = argv; const e2eDir = path.join(__dirname, '../e2e'); @@ -44,9 +49,12 @@ if (server) { const config = './playwright_run.ts'; function executeRunner() { - childProcess.execSync(`node ../../../scripts/${ftrScript} --config ${config}`, { - cwd: e2eDir, - stdio: 'inherit', - }); + childProcess.execSync( + `node ../../../scripts/${ftrScript} --config ${config} --kibana-install-dir '${kibanaInstallDir}'`, + { + cwd: e2eDir, + stdio: 'inherit', + } + ); } executeRunner(); From eb5af49de1cafca9204211f61b4ee0695099f11f Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 20 Oct 2021 13:57:12 +0300 Subject: [PATCH 120/204] [TSVB] Fixes the long text problem that appears behind the gauge chart (#115516) --- .../timeseries/public/application/visualizations/views/gauge.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/vis_types/timeseries/public/application/visualizations/views/gauge.js b/src/plugins/vis_types/timeseries/public/application/visualizations/views/gauge.js index ca5021a882932..0e4322a7ba82c 100644 --- a/src/plugins/vis_types/timeseries/public/application/visualizations/views/gauge.js +++ b/src/plugins/vis_types/timeseries/public/application/visualizations/views/gauge.js @@ -75,6 +75,7 @@ export class Gauge extends Component { top: this.state.top || 0, left: this.state.left || 0, transform: `matrix(${scale}, 0, 0, ${scale}, ${translateX}, ${translateY})`, + zIndex: 1, }, valueColor: { color: this.props.valueColor, From 109e966a7a6fca3c46ccf0132a9ba6027c424d06 Mon Sep 17 00:00:00 2001 From: Jason Stoltzfus Date: Wed, 20 Oct 2021 07:35:25 -0400 Subject: [PATCH 121/204] [App Search] Put History tab behind platinum check (#115636) --- .../curations/views/curations.test.tsx | 17 +++++ .../components/curations/views/curations.tsx | 72 +++++++++++-------- 2 files changed, 58 insertions(+), 31 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.test.tsx index f7e9f5437fc3f..42d808da6d9ee 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.test.tsx @@ -25,6 +25,7 @@ import { CurationsSettings } from './curations_settings'; describe('Curations', () => { const values = { + // CurationsLogic dataLoading: false, curations: [ { @@ -46,6 +47,8 @@ describe('Curations', () => { }, }, selectedPageTab: 'overview', + // LicensingLogic + hasPlatinumLicense: true, }; const actions = { @@ -75,6 +78,20 @@ describe('Curations', () => { tabs.at(2).simulate('click'); expect(actions.onSelectPageTab).toHaveBeenNthCalledWith(3, 'settings'); + // The settings tab should NOT have an icon next to it + expect(tabs.at(2).prop('prepend')).toBeUndefined(); + }); + + it('renders less tabs when less than platinum license', () => { + setMockValues({ ...values, hasPlatinumLicense: false }); + const wrapper = shallow(); + + expect(getPageTitle(wrapper)).toEqual('Curated results'); + + const tabs = getPageHeaderTabs(wrapper).find(EuiTab); + expect(tabs.length).toBe(2); + // The settings tab should have an icon next to it + expect(tabs.at(1).prop('prepend')).not.toBeUndefined(); }); it('renders an overview view', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.tsx index c55fde7626488..7440e0cf42b44 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.tsx @@ -9,8 +9,10 @@ import React, { useEffect } from 'react'; import { useValues, useActions } from 'kea'; +import { EuiIcon } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { LicensingLogic } from '../../../../shared/licensing'; import { EuiButtonTo } from '../../../../shared/react_router_helpers'; import { ENGINE_CURATIONS_NEW_PATH } from '../../../routes'; @@ -28,39 +30,47 @@ import { CurationsSettings } from './curations_settings'; export const Curations: React.FC = () => { const { dataLoading, curations, meta, selectedPageTab } = useValues(CurationsLogic); const { loadCurations, onSelectPageTab } = useActions(CurationsLogic); + const { hasPlatinumLicense } = useValues(LicensingLogic); - const pageTabs = [ - { - label: i18n.translate( - 'xpack.enterpriseSearch.appSearch.engine.curations.overviewPageTabLabel', - { - defaultMessage: 'Overview', - } - ), - isSelected: selectedPageTab === 'overview', - onClick: () => onSelectPageTab('overview'), - }, - { - label: i18n.translate( - 'xpack.enterpriseSearch.appSearch.engine.curations.historyPageTabLabel', - { - defaultMessage: 'History', - } - ), - isSelected: selectedPageTab === 'history', - onClick: () => onSelectPageTab('history'), - }, - { - label: i18n.translate( - 'xpack.enterpriseSearch.appSearch.engine.curations.settingsPageTabLabel', + const OVERVIEW_TAB = { + label: i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.curations.overviewPageTabLabel', + { + defaultMessage: 'Overview', + } + ), + isSelected: selectedPageTab === 'overview', + onClick: () => onSelectPageTab('overview'), + }; + + const HISTORY_TAB = { + label: i18n.translate('xpack.enterpriseSearch.appSearch.engine.curations.historyPageTabLabel', { + defaultMessage: 'History', + }), + isSelected: selectedPageTab === 'history', + onClick: () => onSelectPageTab('history'), + }; + + const SETTINGS_TAB = { + label: i18n.translate( + 'xpack.enterpriseSearch.appSearch.engine.curations.settingsPageTabLabel', + { + defaultMessage: 'Settings', + } + ), + isSelected: selectedPageTab === 'settings', + onClick: () => onSelectPageTab('settings'), + }; + + const pageTabs = hasPlatinumLicense + ? [OVERVIEW_TAB, HISTORY_TAB, SETTINGS_TAB] + : [ + OVERVIEW_TAB, { - defaultMessage: 'Settings', - } - ), - isSelected: selectedPageTab === 'settings', - onClick: () => onSelectPageTab('settings'), - }, - ]; + ...SETTINGS_TAB, + prepend: , + }, + ]; useEffect(() => { loadCurations(); From 73a0fc0948bb4e5cd8bc4b3ccbcb60f9270bc23f Mon Sep 17 00:00:00 2001 From: Peter Pisljar Date: Wed, 20 Oct 2021 13:56:37 +0200 Subject: [PATCH 122/204] [expressions] decrease bundle size (#114229) --- .../public/actions_and_expressions.tsx | 13 +- .../public/actions_and_expressions2.tsx | 13 +- .../public/render_expressions.tsx | 16 +- .../public/run_expressions.tsx | 28 ++- .../data/public/search/expressions/esaggs.ts | 13 +- .../common/execution/execution_contract.ts | 3 +- .../expressions/common/execution/types.ts | 4 +- .../specs/cumulative_sum.ts | 38 +--- .../specs/cumulative_sum_fn.ts | 43 +++++ .../expression_functions/specs/derivative.ts | 44 +---- .../specs/derivative_fn.ts | 77 ++++++++ .../common/expression_functions/specs/math.ts | 103 +--------- .../expression_functions/specs/math_column.ts | 56 +++--- .../expression_functions/specs/math_fn.ts | 109 +++++++++++ .../specs/moving_average.ts | 44 +---- .../specs/moving_average_fn.ts | 74 +++++++ .../specs/overall_metric.ts | 82 +------- .../specs/overall_metric_fn.ts | 113 +++++++++++ .../specs/tests/cumulative_sum.test.ts | 62 +++--- .../specs/tests/derivative.test.ts | 62 +++--- .../specs/tests/math.test.ts | 182 +++++++++++------- .../specs/tests/math_column.test.ts | 46 +++-- .../specs/tests/moving_average.test.ts | 68 +++---- .../specs/tests/overall_metric.test.ts | 86 ++++----- src/plugins/expressions/public/index.ts | 31 +-- src/plugins/expressions/public/loader.test.ts | 4 +- src/plugins/expressions/public/loader.ts | 8 +- src/plugins/expressions/public/mocks.tsx | 4 +- src/plugins/expressions/public/plugin.ts | 30 ++- .../public/react_expression_renderer.test.tsx | 5 +- .../public/react_expression_renderer.tsx | 10 +- .../react_expression_renderer_wrapper.tsx | 19 ++ src/plugins/expressions/public/render.test.ts | 4 +- src/plugins/expressions/public/render.ts | 25 ++- src/plugins/expressions/public/types/index.ts | 4 + .../public/embeddable/visualize_embeddable.ts | 6 +- .../plugins/kbn_tp_run_pipeline/kibana.json | 2 +- .../public/app/components/main.tsx | 12 +- .../merge_tables/merge_tables.test.ts | 1 + 39 files changed, 873 insertions(+), 671 deletions(-) create mode 100644 src/plugins/expressions/common/expression_functions/specs/cumulative_sum_fn.ts create mode 100644 src/plugins/expressions/common/expression_functions/specs/derivative_fn.ts create mode 100644 src/plugins/expressions/common/expression_functions/specs/math_fn.ts create mode 100644 src/plugins/expressions/common/expression_functions/specs/moving_average_fn.ts create mode 100644 src/plugins/expressions/common/expression_functions/specs/overall_metric_fn.ts create mode 100644 src/plugins/expressions/public/react_expression_renderer_wrapper.tsx diff --git a/examples/expressions_explorer/public/actions_and_expressions.tsx b/examples/expressions_explorer/public/actions_and_expressions.tsx index 6d0c8886a79f3..4d6d8b9f05ed3 100644 --- a/examples/expressions_explorer/public/actions_and_expressions.tsx +++ b/examples/expressions_explorer/public/actions_and_expressions.tsx @@ -19,11 +19,7 @@ import { EuiText, EuiTitle, } from '@elastic/eui'; -import { - ExpressionsStart, - ReactExpressionRenderer, - ExpressionsInspectorAdapter, -} from '../../../src/plugins/expressions/public'; +import { ExpressionsStart } from '../../../src/plugins/expressions/public'; import { ExpressionEditor } from './editor/expression_editor'; import { UiActionsStart } from '../../../src/plugins/ui_actions/public'; import { NAVIGATE_TRIGGER_ID } from './actions/navigate_trigger'; @@ -42,10 +38,6 @@ export function ActionsExpressionsExample({ expressions, actions }: Props) { updateExpression(value); }; - const inspectorAdapters = { - expression: new ExpressionsInspectorAdapter(), - }; - const handleEvents = (event: any) => { if (event.name !== 'NAVIGATE') return; // enrich event context with some extra data @@ -83,10 +75,9 @@ export function ActionsExpressionsExample({ expressions, actions }: Props) {
- { return
{message}
; diff --git a/examples/expressions_explorer/public/actions_and_expressions2.tsx b/examples/expressions_explorer/public/actions_and_expressions2.tsx index e7dc28b8b97cd..28c698df38d1b 100644 --- a/examples/expressions_explorer/public/actions_and_expressions2.tsx +++ b/examples/expressions_explorer/public/actions_and_expressions2.tsx @@ -19,11 +19,7 @@ import { EuiText, EuiTitle, } from '@elastic/eui'; -import { - ExpressionsStart, - ReactExpressionRenderer, - ExpressionsInspectorAdapter, -} from '../../../src/plugins/expressions/public'; +import { ExpressionsStart } from '../../../src/plugins/expressions/public'; import { ExpressionEditor } from './editor/expression_editor'; import { UiActionsStart } from '../../../src/plugins/ui_actions/public'; @@ -45,10 +41,6 @@ export function ActionsExpressionsExample2({ expressions, actions }: Props) { updateExpression(value); }; - const inspectorAdapters = { - expression: new ExpressionsInspectorAdapter(), - }; - const handleEvents = (event: any) => { updateVariables({ color: event.data.href === 'http://www.google.com' ? 'red' : 'blue' }); }; @@ -81,11 +73,10 @@ export function ActionsExpressionsExample2({ expressions, actions }: Props) {
- { diff --git a/examples/expressions_explorer/public/render_expressions.tsx b/examples/expressions_explorer/public/render_expressions.tsx index d91db964c5352..b7780939e1913 100644 --- a/examples/expressions_explorer/public/render_expressions.tsx +++ b/examples/expressions_explorer/public/render_expressions.tsx @@ -20,11 +20,7 @@ import { EuiTitle, EuiButton, } from '@elastic/eui'; -import { - ExpressionsStart, - ReactExpressionRenderer, - ExpressionsInspectorAdapter, -} from '../../../src/plugins/expressions/public'; +import { ExpressionsStart } from '../../../src/plugins/expressions/public'; import { ExpressionEditor } from './editor/expression_editor'; import { Start as InspectorStart } from '../../../src/plugins/inspector/public'; @@ -42,9 +38,7 @@ export function RenderExpressionsExample({ expressions, inspector }: Props) { updateExpression(value); }; - const inspectorAdapters = { - expression: new ExpressionsInspectorAdapter(), - }; + const inspectorAdapters = {}; return ( @@ -83,10 +77,12 @@ export function RenderExpressionsExample({ expressions, inspector }: Props) { - { + Object.assign(inspectorAdapters, panels); + }} renderError={(message: any) => { return
{message}
; }} diff --git a/examples/expressions_explorer/public/run_expressions.tsx b/examples/expressions_explorer/public/run_expressions.tsx index 93cab0e9f2b6f..18caa97171847 100644 --- a/examples/expressions_explorer/public/run_expressions.tsx +++ b/examples/expressions_explorer/public/run_expressions.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import React, { useState, useEffect, useMemo } from 'react'; +import React, { useState, useEffect } from 'react'; import { pluck } from 'rxjs/operators'; import { EuiCodeBlock, @@ -22,12 +22,9 @@ import { EuiTitle, EuiButton, } from '@elastic/eui'; -import { - ExpressionsStart, - ExpressionsInspectorAdapter, -} from '../../../src/plugins/expressions/public'; +import { ExpressionsStart } from '../../../src/plugins/expressions/public'; import { ExpressionEditor } from './editor/expression_editor'; -import { Start as InspectorStart } from '../../../src/plugins/inspector/public'; +import { Adapters, Start as InspectorStart } from '../../../src/plugins/inspector/public'; interface Props { expressions: ExpressionsStart; @@ -37,25 +34,24 @@ interface Props { export function RunExpressionsExample({ expressions, inspector }: Props) { const [expression, updateExpression] = useState('markdownVis "## expressions explorer"'); const [result, updateResult] = useState({}); + const [inspectorAdapters, updateInspectorAdapters] = useState({}); const expressionChanged = (value: string) => { updateExpression(value); }; - const inspectorAdapters = useMemo( - () => ({ - expression: new ExpressionsInspectorAdapter(), - }), - [] - ); - useEffect(() => { const execution = expressions.execute(expression, null, { debug: true, - inspectorAdapters, }); - const subscription = execution.getData().pipe(pluck('result')).subscribe(updateResult); - + const subscription = execution + .getData() + .pipe(pluck('result')) + .subscribe((data) => { + updateResult(data); + updateInspectorAdapters(execution.inspect()); + }); + execution.inspect(); return () => subscription.unsubscribe(); }, [expression, expressions, inspectorAdapters]); diff --git a/src/plugins/data/public/search/expressions/esaggs.ts b/src/plugins/data/public/search/expressions/esaggs.ts index 64b3f3c8dd7de..b691dc2237c22 100644 --- a/src/plugins/data/public/search/expressions/esaggs.ts +++ b/src/plugins/data/public/search/expressions/esaggs.ts @@ -14,7 +14,6 @@ import { EsaggsExpressionFunctionDefinition, EsaggsStartDependencies, getEsaggsMeta, - handleEsaggsRequest, } from '../../../common/search/expressions'; import { DataPublicPluginStart, DataStartDependencies } from '../../types'; @@ -48,10 +47,12 @@ export function getFunctionDefinition({ ); aggConfigs.hierarchical = args.metricsAtAllLevels; - return { aggConfigs, indexPattern, searchSource, getNow }; + const { handleEsaggsRequest } = await import('../../../common/search/expressions'); + + return { aggConfigs, indexPattern, searchSource, getNow, handleEsaggsRequest }; }).pipe( - switchMap(({ aggConfigs, indexPattern, searchSource, getNow }) => - handleEsaggsRequest({ + switchMap(({ aggConfigs, indexPattern, searchSource, getNow, handleEsaggsRequest }) => { + return handleEsaggsRequest({ abortSignal, aggs: aggConfigs, filters: get(input, 'filters', undefined), @@ -65,8 +66,8 @@ export function getFunctionDefinition({ timeRange: get(input, 'timeRange', undefined), getNow, executionContext: getExecutionContext(), - }) - ) + }); + }) ); }, }); diff --git a/src/plugins/expressions/common/execution/execution_contract.ts b/src/plugins/expressions/common/execution/execution_contract.ts index 445ceb30b58db..14ab7bebbf057 100644 --- a/src/plugins/expressions/common/execution/execution_contract.ts +++ b/src/plugins/expressions/common/execution/execution_contract.ts @@ -11,6 +11,7 @@ import { catchError } from 'rxjs/operators'; import { Execution, ExecutionResult } from './execution'; import { ExpressionValueError } from '../expression_types/specs'; import { ExpressionAstExpression } from '../ast'; +import { Adapters } from '../../../inspector/common/adapters'; /** * `ExecutionContract` is a wrapper around `Execution` class. It provides the @@ -75,5 +76,5 @@ export class ExecutionContract this.execution.inspectorAdapters; + inspect = (): Adapters => this.execution.inspectorAdapters; } diff --git a/src/plugins/expressions/common/execution/types.ts b/src/plugins/expressions/common/execution/types.ts index 9264891b2e0b8..e1e65ada632a9 100644 --- a/src/plugins/expressions/common/execution/types.ts +++ b/src/plugins/expressions/common/execution/types.ts @@ -14,6 +14,7 @@ import type { KibanaExecutionContext } from 'src/core/public'; import { Datatable, ExpressionType } from '../expression_types'; import { Adapters, RequestAdapter } from '../../../inspector/common'; import { TablesAdapter } from '../util/tables_adapter'; +import { ExpressionsInspectorAdapter } from '../util'; /** * `ExecutionContext` is an object available to all functions during a single execution; @@ -79,7 +80,8 @@ export interface ExecutionContext< /** * Default inspector adapters created if inspector adapters are not set explicitly. */ -export interface DefaultInspectorAdapters extends Adapters { +export interface DefaultInspectorAdapters { requests: RequestAdapter; tables: TablesAdapter; + expression: ExpressionsInspectorAdapter; } diff --git a/src/plugins/expressions/common/expression_functions/specs/cumulative_sum.ts b/src/plugins/expressions/common/expression_functions/specs/cumulative_sum.ts index 9568ba3a32f29..3269ac3c61864 100644 --- a/src/plugins/expressions/common/expression_functions/specs/cumulative_sum.ts +++ b/src/plugins/expressions/common/expression_functions/specs/cumulative_sum.ts @@ -9,7 +9,6 @@ import { i18n } from '@kbn/i18n'; import { ExpressionFunctionDefinition } from '../types'; import { Datatable } from '../../expression_types'; -import { buildResultColumns, getBucketIdentifier } from '../series_calculation_helpers'; export interface CumulativeSumArgs { by?: string[]; @@ -22,7 +21,7 @@ export type ExpressionFunctionCumulativeSum = ExpressionFunctionDefinition< 'cumulative_sum', Datatable, CumulativeSumArgs, - Datatable + Promise >; /** @@ -94,37 +93,8 @@ export const cumulativeSum: ExpressionFunctionCumulativeSum = { }, }, - fn(input, { by, inputColumnId, outputColumnId, outputColumnName }) { - const resultColumns = buildResultColumns( - input, - outputColumnId, - inputColumnId, - outputColumnName - ); - - if (!resultColumns) { - return input; - } - - const accumulators: Partial> = {}; - return { - ...input, - columns: resultColumns, - rows: input.rows.map((row) => { - const newRow = { ...row }; - - const bucketIdentifier = getBucketIdentifier(row, by); - const accumulatorValue = accumulators[bucketIdentifier] ?? 0; - const currentValue = newRow[inputColumnId]; - if (currentValue != null) { - newRow[outputColumnId] = Number(currentValue) + accumulatorValue; - accumulators[bucketIdentifier] = newRow[outputColumnId]; - } else { - newRow[outputColumnId] = accumulatorValue; - } - - return newRow; - }), - }; + async fn(input, args) { + const { cumulativeSumFn } = await import('./cumulative_sum_fn'); + return cumulativeSumFn(input, args); }, }; diff --git a/src/plugins/expressions/common/expression_functions/specs/cumulative_sum_fn.ts b/src/plugins/expressions/common/expression_functions/specs/cumulative_sum_fn.ts new file mode 100644 index 0000000000000..3d2d9740c89c9 --- /dev/null +++ b/src/plugins/expressions/common/expression_functions/specs/cumulative_sum_fn.ts @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { Datatable } from '../../expression_types'; +import { buildResultColumns, getBucketIdentifier } from '../series_calculation_helpers'; +import { CumulativeSumArgs } from './cumulative_sum'; + +export const cumulativeSumFn = ( + input: Datatable, + { by, inputColumnId, outputColumnId, outputColumnName }: CumulativeSumArgs +): Datatable => { + const resultColumns = buildResultColumns(input, outputColumnId, inputColumnId, outputColumnName); + + if (!resultColumns) { + return input; + } + + const accumulators: Partial> = {}; + return { + ...input, + columns: resultColumns, + rows: input.rows.map((row) => { + const newRow = { ...row }; + + const bucketIdentifier = getBucketIdentifier(row, by); + const accumulatorValue = accumulators[bucketIdentifier] ?? 0; + const currentValue = newRow[inputColumnId]; + if (currentValue != null) { + newRow[outputColumnId] = Number(currentValue) + accumulatorValue; + accumulators[bucketIdentifier] = newRow[outputColumnId]; + } else { + newRow[outputColumnId] = accumulatorValue; + } + + return newRow; + }), + }; +}; diff --git a/src/plugins/expressions/common/expression_functions/specs/derivative.ts b/src/plugins/expressions/common/expression_functions/specs/derivative.ts index 50c9cc32ee4f3..1e139c93ab326 100644 --- a/src/plugins/expressions/common/expression_functions/specs/derivative.ts +++ b/src/plugins/expressions/common/expression_functions/specs/derivative.ts @@ -9,7 +9,6 @@ import { i18n } from '@kbn/i18n'; import { ExpressionFunctionDefinition } from '../types'; import { Datatable } from '../../expression_types'; -import { buildResultColumns, getBucketIdentifier } from '../series_calculation_helpers'; export interface DerivativeArgs { by?: string[]; @@ -22,7 +21,7 @@ export type ExpressionFunctionDerivative = ExpressionFunctionDefinition< 'derivative', Datatable, DerivativeArgs, - Datatable + Promise >; /** @@ -95,43 +94,8 @@ export const derivative: ExpressionFunctionDerivative = { }, }, - fn(input, { by, inputColumnId, outputColumnId, outputColumnName }) { - const resultColumns = buildResultColumns( - input, - outputColumnId, - inputColumnId, - outputColumnName - ); - - if (!resultColumns) { - return input; - } - - const previousValues: Partial> = {}; - return { - ...input, - columns: resultColumns, - rows: input.rows.map((row) => { - const newRow = { ...row }; - - const bucketIdentifier = getBucketIdentifier(row, by); - const previousValue = previousValues[bucketIdentifier]; - const currentValue = newRow[inputColumnId]; - - if (currentValue != null && previousValue != null) { - newRow[outputColumnId] = Number(currentValue) - previousValue; - } else { - newRow[outputColumnId] = undefined; - } - - if (currentValue != null) { - previousValues[bucketIdentifier] = Number(currentValue); - } else { - previousValues[bucketIdentifier] = undefined; - } - - return newRow; - }), - }; + async fn(input, args) { + const { derivativeFn } = await import('./derivative_fn'); + return derivativeFn(input, args); }, }; diff --git a/src/plugins/expressions/common/expression_functions/specs/derivative_fn.ts b/src/plugins/expressions/common/expression_functions/specs/derivative_fn.ts new file mode 100644 index 0000000000000..b2694ebdf4013 --- /dev/null +++ b/src/plugins/expressions/common/expression_functions/specs/derivative_fn.ts @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { Datatable } from '../../expression_types'; +import { buildResultColumns, getBucketIdentifier } from '../series_calculation_helpers'; +import { DerivativeArgs } from './derivative'; + +/** + * Calculates the derivative of a specified column in the data table. + * + * Also supports multiple series in a single data table - use the `by` argument + * to specify the columns to split the calculation by. + * For each unique combination of all `by` columns a separate derivative will be calculated. + * The order of rows won't be changed - this function is not modifying any existing columns, it's only + * adding the specified `outputColumnId` column to every row of the table without adding or removing rows. + * + * Behavior: + * * Will write the derivative of `inputColumnId` into `outputColumnId` + * * If provided will use `outputColumnName` as name for the newly created column. Otherwise falls back to `outputColumnId` + * * Derivative always start with an undefined value for the first row of a series, a cell will contain its own value minus the + * value of the previous cell of the same series. + * + * Edge cases: + * * Will return the input table if `inputColumnId` does not exist + * * Will throw an error if `outputColumnId` exists already in provided data table + * * If there is no previous row of the current series with a non `null` or `undefined` value, the output cell of the current row + * will be set to `undefined`. + * * If the row value contains `null` or `undefined`, it will be ignored and the output cell will be set to `undefined` + * * If the value of the previous row of the same series contains `null` or `undefined`, the output cell of the current row will be set to `undefined` as well + * * For all values besides `null` and `undefined`, the value will be cast to a number before it's used in the + * calculation of the current series even if this results in `NaN` (like in case of objects). + * * To determine separate series defined by the `by` columns, the values of these columns will be cast to strings + * before comparison. If the values are objects, the return value of their `toString` method will be used for comparison. + * Missing values (`null` and `undefined`) will be treated as empty strings. + */ +export const derivativeFn = ( + input: Datatable, + { by, inputColumnId, outputColumnId, outputColumnName }: DerivativeArgs +): Datatable => { + const resultColumns = buildResultColumns(input, outputColumnId, inputColumnId, outputColumnName); + + if (!resultColumns) { + return input; + } + + const previousValues: Partial> = {}; + return { + ...input, + columns: resultColumns, + rows: input.rows.map((row) => { + const newRow = { ...row }; + + const bucketIdentifier = getBucketIdentifier(row, by); + const previousValue = previousValues[bucketIdentifier]; + const currentValue = newRow[inputColumnId]; + + if (currentValue != null && previousValue != null) { + newRow[outputColumnId] = Number(currentValue) - previousValue; + } else { + newRow[outputColumnId] = undefined; + } + + if (currentValue != null) { + previousValues[bucketIdentifier] = Number(currentValue); + } else { + previousValues[bucketIdentifier] = undefined; + } + + return newRow; + }), + }; +}; diff --git a/src/plugins/expressions/common/expression_functions/specs/math.ts b/src/plugins/expressions/common/expression_functions/specs/math.ts index f843f53e4dd88..f79fc43488487 100644 --- a/src/plugins/expressions/common/expression_functions/specs/math.ts +++ b/src/plugins/expressions/common/expression_functions/specs/math.ts @@ -6,11 +6,9 @@ * Side Public License, v 1. */ -import { map, zipObject, isString } from 'lodash'; import { i18n } from '@kbn/i18n'; -import { evaluate } from '@kbn/tinymath'; import { ExpressionFunctionDefinition } from '../types'; -import { Datatable, isDatatable } from '../../expression_types'; +import { Datatable } from '../../expression_types'; export type MathArguments = { expression: string; @@ -23,63 +21,11 @@ const TINYMATH = '`TinyMath`'; const TINYMATH_URL = 'https://www.elastic.co/guide/en/kibana/current/canvas-tinymath-functions.html'; -function pivotObjectArray< - RowType extends { [key: string]: unknown }, - ReturnColumns extends keyof RowType & string ->(rows: RowType[], columns?: ReturnColumns[]) { - const columnNames = columns || Object.keys(rows[0]); - if (!columnNames.every(isString)) { - throw new Error('Columns should be an array of strings'); - } - - const columnValues = map(columnNames, (name) => map(rows, name)); - - return zipObject(columnNames, columnValues) as { [K in ReturnColumns]: Array }; -} - -export const errors = { - emptyExpression: () => - new Error( - i18n.translate('expressions.functions.math.emptyExpressionErrorMessage', { - defaultMessage: 'Empty expression', - }) - ), - tooManyResults: () => - new Error( - i18n.translate('expressions.functions.math.tooManyResultsErrorMessage', { - defaultMessage: - 'Expressions must return a single number. Try wrapping your expression in {mean} or {sum}', - values: { - mean: 'mean()', - sum: 'sum()', - }, - }) - ), - executionFailed: () => - new Error( - i18n.translate('expressions.functions.math.executionFailedErrorMessage', { - defaultMessage: 'Failed to execute math expression. Check your column names', - }) - ), - emptyDatatable: () => - new Error( - i18n.translate('expressions.functions.math.emptyDatatableErrorMessage', { - defaultMessage: 'Empty datatable', - }) - ), -}; - -const fallbackValue = { - null: null, - zero: 0, - false: false, -} as const; - export const math: ExpressionFunctionDefinition< 'math', MathInput, MathArguments, - boolean | number | null + Promise > = { name: 'math', type: undefined, @@ -121,47 +67,8 @@ export const math: ExpressionFunctionDefinition< }), }, }, - fn: (input, args) => { - const { expression, onError } = args; - const onErrorValue = onError ?? 'throw'; - - if (!expression || expression.trim() === '') { - throw errors.emptyExpression(); - } - - // Use unique ID if available, otherwise fall back to names - const mathContext = isDatatable(input) - ? pivotObjectArray( - input.rows, - input.columns.map((col) => col.id) - ) - : { value: input }; - - try { - const result = evaluate(expression, mathContext); - if (Array.isArray(result)) { - if (result.length === 1) { - return result[0]; - } - throw errors.tooManyResults(); - } - if (isNaN(result)) { - // make TS happy - if (onErrorValue !== 'throw' && onErrorValue in fallbackValue) { - return fallbackValue[onErrorValue]; - } - throw errors.executionFailed(); - } - return result; - } catch (e) { - if (onErrorValue !== 'throw' && onErrorValue in fallbackValue) { - return fallbackValue[onErrorValue]; - } - if (isDatatable(input) && input.rows.length === 0) { - throw errors.emptyDatatable(); - } else { - throw e; - } - } + fn: async (input, args) => { + const { mathFn } = await import('./math_fn'); + return mathFn(input, args); }, }; diff --git a/src/plugins/expressions/common/expression_functions/specs/math_column.ts b/src/plugins/expressions/common/expression_functions/specs/math_column.ts index a2a79ef3f0286..fe6049b49c969 100644 --- a/src/plugins/expressions/common/expression_functions/specs/math_column.ts +++ b/src/plugins/expressions/common/expression_functions/specs/math_column.ts @@ -21,7 +21,7 @@ export const mathColumn: ExpressionFunctionDefinition< 'mathColumn', Datatable, MathColumnArguments, - Datatable + Promise > = { name: 'mathColumn', type: 'datatable', @@ -63,7 +63,7 @@ export const mathColumn: ExpressionFunctionDefinition< default: null, }, }, - fn: (input, args, context) => { + fn: async (input, args, context) => { const columns = [...input.columns]; const existingColumnIndex = columns.findIndex(({ id }) => { return id === args.id; @@ -76,34 +76,36 @@ export const mathColumn: ExpressionFunctionDefinition< ); } - const newRows = input.rows.map((row) => { - const result = math.fn( - { - type: 'datatable', - columns: input.columns, - rows: [row], - }, - { - expression: args.expression, - onError: args.onError, - }, - context - ); + const newRows = await Promise.all( + input.rows.map(async (row) => { + const result = await math.fn( + { + type: 'datatable', + columns: input.columns, + rows: [row], + }, + { + expression: args.expression, + onError: args.onError, + }, + context + ); - if (Array.isArray(result)) { - if (result.length === 1) { - return { ...row, [args.id]: result[0] }; + if (Array.isArray(result)) { + if (result.length === 1) { + return { ...row, [args.id]: result[0] }; + } + throw new Error( + i18n.translate('expressions.functions.mathColumn.arrayValueError', { + defaultMessage: 'Cannot perform math on array values at {name}', + values: { name: args.name }, + }) + ); } - throw new Error( - i18n.translate('expressions.functions.mathColumn.arrayValueError', { - defaultMessage: 'Cannot perform math on array values at {name}', - values: { name: args.name }, - }) - ); - } - return { ...row, [args.id]: result }; - }); + return { ...row, [args.id]: result }; + }) + ); let type: DatatableColumnType = 'null'; if (newRows.length) { for (const row of newRows) { diff --git a/src/plugins/expressions/common/expression_functions/specs/math_fn.ts b/src/plugins/expressions/common/expression_functions/specs/math_fn.ts new file mode 100644 index 0000000000000..72ac5a6da445a --- /dev/null +++ b/src/plugins/expressions/common/expression_functions/specs/math_fn.ts @@ -0,0 +1,109 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { map, zipObject, isString } from 'lodash'; +import { i18n } from '@kbn/i18n'; +import { evaluate } from '@kbn/tinymath'; +import { isDatatable } from '../../expression_types'; +import { MathArguments, MathInput } from './math'; + +function pivotObjectArray< + RowType extends { [key: string]: unknown }, + ReturnColumns extends keyof RowType & string +>(rows: RowType[], columns?: ReturnColumns[]) { + const columnNames = columns || Object.keys(rows[0]); + if (!columnNames.every(isString)) { + throw new Error('Columns should be an array of strings'); + } + + const columnValues = map(columnNames, (name) => map(rows, name)); + + return zipObject(columnNames, columnValues) as { [K in ReturnColumns]: Array }; +} + +export const errors = { + emptyExpression: () => + new Error( + i18n.translate('expressions.functions.math.emptyExpressionErrorMessage', { + defaultMessage: 'Empty expression', + }) + ), + tooManyResults: () => + new Error( + i18n.translate('expressions.functions.math.tooManyResultsErrorMessage', { + defaultMessage: + 'Expressions must return a single number. Try wrapping your expression in {mean} or {sum}', + values: { + mean: 'mean()', + sum: 'sum()', + }, + }) + ), + executionFailed: () => + new Error( + i18n.translate('expressions.functions.math.executionFailedErrorMessage', { + defaultMessage: 'Failed to execute math expression. Check your column names', + }) + ), + emptyDatatable: () => + new Error( + i18n.translate('expressions.functions.math.emptyDatatableErrorMessage', { + defaultMessage: 'Empty datatable', + }) + ), +}; + +const fallbackValue = { + null: null, + zero: 0, + false: false, +} as const; + +export const mathFn = (input: MathInput, args: MathArguments): boolean | number | null => { + const { expression, onError } = args; + const onErrorValue = onError ?? 'throw'; + + if (!expression || expression.trim() === '') { + throw errors.emptyExpression(); + } + + // Use unique ID if available, otherwise fall back to names + const mathContext = isDatatable(input) + ? pivotObjectArray( + input.rows, + input.columns.map((col) => col.id) + ) + : { value: input }; + + try { + const result = evaluate(expression, mathContext); + if (Array.isArray(result)) { + if (result.length === 1) { + return result[0]; + } + throw errors.tooManyResults(); + } + if (isNaN(result)) { + // make TS happy + if (onErrorValue !== 'throw' && onErrorValue in fallbackValue) { + return fallbackValue[onErrorValue]; + } + throw errors.executionFailed(); + } + return result; + } catch (e) { + if (onErrorValue !== 'throw' && onErrorValue in fallbackValue) { + return fallbackValue[onErrorValue]; + } + if (isDatatable(input) && input.rows.length === 0) { + throw errors.emptyDatatable(); + } else { + throw e; + } + } +}; diff --git a/src/plugins/expressions/common/expression_functions/specs/moving_average.ts b/src/plugins/expressions/common/expression_functions/specs/moving_average.ts index 1fa69501401de..08c978fa53bd1 100644 --- a/src/plugins/expressions/common/expression_functions/specs/moving_average.ts +++ b/src/plugins/expressions/common/expression_functions/specs/moving_average.ts @@ -9,7 +9,6 @@ import { i18n } from '@kbn/i18n'; import { ExpressionFunctionDefinition } from '../types'; import { Datatable } from '../../expression_types'; -import { buildResultColumns, getBucketIdentifier } from '../series_calculation_helpers'; export interface MovingAverageArgs { by?: string[]; @@ -23,7 +22,7 @@ export type ExpressionFunctionMovingAverage = ExpressionFunctionDefinition< 'moving_average', Datatable, MovingAverageArgs, - Datatable + Promise >; /** @@ -100,43 +99,8 @@ export const movingAverage: ExpressionFunctionMovingAverage = { }, }, - fn(input, { by, inputColumnId, outputColumnId, outputColumnName, window }) { - const resultColumns = buildResultColumns( - input, - outputColumnId, - inputColumnId, - outputColumnName - ); - - if (!resultColumns) { - return input; - } - - const lastNValuesByBucket: Partial> = {}; - return { - ...input, - columns: resultColumns, - rows: input.rows.map((row) => { - const newRow = { ...row }; - const bucketIdentifier = getBucketIdentifier(row, by); - const lastNValues = lastNValuesByBucket[bucketIdentifier]; - const currentValue = newRow[inputColumnId]; - if (lastNValues != null && currentValue != null) { - const sum = lastNValues.reduce((acc, current) => acc + current, 0); - newRow[outputColumnId] = sum / lastNValues.length; - } else { - newRow[outputColumnId] = undefined; - } - - if (currentValue != null) { - lastNValuesByBucket[bucketIdentifier] = [ - ...(lastNValues || []), - Number(currentValue), - ].slice(-window); - } - - return newRow; - }), - }; + async fn(input, args) { + const { movingAverageFn } = await import('./moving_average_fn'); + return movingAverageFn(input, args); }, }; diff --git a/src/plugins/expressions/common/expression_functions/specs/moving_average_fn.ts b/src/plugins/expressions/common/expression_functions/specs/moving_average_fn.ts new file mode 100644 index 0000000000000..43e6cb140a350 --- /dev/null +++ b/src/plugins/expressions/common/expression_functions/specs/moving_average_fn.ts @@ -0,0 +1,74 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { Datatable } from '../../expression_types'; +import { buildResultColumns, getBucketIdentifier } from '../series_calculation_helpers'; +import { MovingAverageArgs } from './moving_average'; + +/** + * Calculates the moving average of a specified column in the data table. + * + * Also supports multiple series in a single data table - use the `by` argument + * to specify the columns to split the calculation by. + * For each unique combination of all `by` columns a separate moving average will be calculated. + * The order of rows won't be changed - this function is not modifying any existing columns, it's only + * adding the specified `outputColumnId` column to every row of the table without adding or removing rows. + * + * Behavior: + * * Will write the moving average of `inputColumnId` into `outputColumnId` + * * If provided will use `outputColumnName` as name for the newly created column. Otherwise falls back to `outputColumnId` + * * Moving average always starts with an undefined value for the first row of a series. Each next cell will contain sum of the last + * * [window] of values divided by [window] excluding the current bucket. + * If either of window edges moves outside the borders of data series, the window shrinks to include available values only. + * + * Edge cases: + * * Will return the input table if `inputColumnId` does not exist + * * Will throw an error if `outputColumnId` exists already in provided data table + * * If null or undefined value is encountered, skip the current row and do not change the window + * * For all values besides `null` and `undefined`, the value will be cast to a number before it's used in the + * calculation of the current series even if this results in `NaN` (like in case of objects). + * * To determine separate series defined by the `by` columns, the values of these columns will be cast to strings + * before comparison. If the values are objects, the return value of their `toString` method will be used for comparison. + */ +export const movingAverageFn = ( + input: Datatable, + { by, inputColumnId, outputColumnId, outputColumnName, window }: MovingAverageArgs +): Datatable => { + const resultColumns = buildResultColumns(input, outputColumnId, inputColumnId, outputColumnName); + + if (!resultColumns) { + return input; + } + + const lastNValuesByBucket: Partial> = {}; + return { + ...input, + columns: resultColumns, + rows: input.rows.map((row) => { + const newRow = { ...row }; + const bucketIdentifier = getBucketIdentifier(row, by); + const lastNValues = lastNValuesByBucket[bucketIdentifier]; + const currentValue = newRow[inputColumnId]; + if (lastNValues != null && currentValue != null) { + const sum = lastNValues.reduce((acc, current) => acc + current, 0); + newRow[outputColumnId] = sum / lastNValues.length; + } else { + newRow[outputColumnId] = undefined; + } + + if (currentValue != null) { + lastNValuesByBucket[bucketIdentifier] = [ + ...(lastNValues || []), + Number(currentValue), + ].slice(-window); + } + + return newRow; + }), + }; +}; diff --git a/src/plugins/expressions/common/expression_functions/specs/overall_metric.ts b/src/plugins/expressions/common/expression_functions/specs/overall_metric.ts index f30f0e1e1b1d6..4bb1d97cb2f17 100644 --- a/src/plugins/expressions/common/expression_functions/specs/overall_metric.ts +++ b/src/plugins/expressions/common/expression_functions/specs/overall_metric.ts @@ -9,7 +9,6 @@ import { i18n } from '@kbn/i18n'; import { ExpressionFunctionDefinition } from '../types'; import { Datatable } from '../../expression_types'; -import { buildResultColumns, getBucketIdentifier } from '../series_calculation_helpers'; export interface OverallMetricArgs { by?: string[]; @@ -23,17 +22,9 @@ export type ExpressionFunctionOverallMetric = ExpressionFunctionDefinition< 'overall_metric', Datatable, OverallMetricArgs, - Datatable + Promise >; -function getValueAsNumberArray(value: unknown) { - if (Array.isArray(value)) { - return value.map((innerVal) => Number(innerVal)); - } else { - return [Number(value)]; - } -} - /** * Calculates the overall metric of a specified column in the data table. * @@ -109,73 +100,8 @@ export const overallMetric: ExpressionFunctionOverallMetric = { }, }, - fn(input, { by, inputColumnId, outputColumnId, outputColumnName, metric }) { - const resultColumns = buildResultColumns( - input, - outputColumnId, - inputColumnId, - outputColumnName - ); - - if (!resultColumns) { - return input; - } - - const accumulators: Partial> = {}; - const valueCounter: Partial> = {}; - input.rows.forEach((row) => { - const bucketIdentifier = getBucketIdentifier(row, by); - const accumulatorValue = accumulators[bucketIdentifier]; - - const currentValue = row[inputColumnId]; - if (currentValue != null) { - const currentNumberValues = getValueAsNumberArray(currentValue); - switch (metric) { - case 'average': - valueCounter[bucketIdentifier] = - (valueCounter[bucketIdentifier] ?? 0) + currentNumberValues.length; - case 'sum': - accumulators[bucketIdentifier] = currentNumberValues.reduce( - (a, b) => a + b, - accumulatorValue || 0 - ); - break; - case 'min': - if (typeof accumulatorValue !== 'undefined') { - accumulators[bucketIdentifier] = Math.min(accumulatorValue, ...currentNumberValues); - } else { - accumulators[bucketIdentifier] = Math.min(...currentNumberValues); - } - break; - case 'max': - if (typeof accumulatorValue !== 'undefined') { - accumulators[bucketIdentifier] = Math.max(accumulatorValue, ...currentNumberValues); - } else { - accumulators[bucketIdentifier] = Math.max(...currentNumberValues); - } - break; - } - } - }); - if (metric === 'average') { - Object.keys(accumulators).forEach((bucketIdentifier) => { - const accumulatorValue = accumulators[bucketIdentifier]; - const valueCount = valueCounter[bucketIdentifier]; - if (typeof accumulatorValue !== 'undefined' && typeof valueCount !== 'undefined') { - accumulators[bucketIdentifier] = accumulatorValue / valueCount; - } - }); - } - return { - ...input, - columns: resultColumns, - rows: input.rows.map((row) => { - const newRow = { ...row }; - const bucketIdentifier = getBucketIdentifier(row, by); - newRow[outputColumnId] = accumulators[bucketIdentifier]; - - return newRow; - }), - }; + async fn(input, args) { + const { overallMetricFn } = await import('./overall_metric_fn'); + return overallMetricFn(input, args); }, }; diff --git a/src/plugins/expressions/common/expression_functions/specs/overall_metric_fn.ts b/src/plugins/expressions/common/expression_functions/specs/overall_metric_fn.ts new file mode 100644 index 0000000000000..002b3ad9667cc --- /dev/null +++ b/src/plugins/expressions/common/expression_functions/specs/overall_metric_fn.ts @@ -0,0 +1,113 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { Datatable } from '../../expression_types'; +import { buildResultColumns, getBucketIdentifier } from '../series_calculation_helpers'; +import { OverallMetricArgs } from './overall_metric'; + +function getValueAsNumberArray(value: unknown) { + if (Array.isArray(value)) { + return value.map((innerVal) => Number(innerVal)); + } else { + return [Number(value)]; + } +} + +/** + * Calculates the overall metric of a specified column in the data table. + * + * Also supports multiple series in a single data table - use the `by` argument + * to specify the columns to split the calculation by. + * For each unique combination of all `by` columns a separate overall metric will be calculated. + * The order of rows won't be changed - this function is not modifying any existing columns, it's only + * adding the specified `outputColumnId` column to every row of the table without adding or removing rows. + * + * Behavior: + * * Will write the overall metric of `inputColumnId` into `outputColumnId` + * * If provided will use `outputColumnName` as name for the newly created column. Otherwise falls back to `outputColumnId` + * * Each cell will contain the calculated metric based on the values of all cells belonging to the current series. + * + * Edge cases: + * * Will return the input table if `inputColumnId` does not exist + * * Will throw an error if `outputColumnId` exists already in provided data table + * * If the row value contains `null` or `undefined`, it will be ignored and overwritten with the overall metric of + * all cells of the same series. + * * For all values besides `null` and `undefined`, the value will be cast to a number before it's added to the + * overall metric of the current series - if this results in `NaN` (like in case of objects), all cells of the + * current series will be set to `NaN`. + * * To determine separate series defined by the `by` columns, the values of these columns will be cast to strings + * before comparison. If the values are objects, the return value of their `toString` method will be used for comparison. + * Missing values (`null` and `undefined`) will be treated as empty strings. + */ +export const overallMetricFn = ( + input: Datatable, + { by, inputColumnId, outputColumnId, outputColumnName, metric }: OverallMetricArgs +): Datatable => { + const resultColumns = buildResultColumns(input, outputColumnId, inputColumnId, outputColumnName); + + if (!resultColumns) { + return input; + } + + const accumulators: Partial> = {}; + const valueCounter: Partial> = {}; + input.rows.forEach((row) => { + const bucketIdentifier = getBucketIdentifier(row, by); + const accumulatorValue = accumulators[bucketIdentifier]; + + const currentValue = row[inputColumnId]; + if (currentValue != null) { + const currentNumberValues = getValueAsNumberArray(currentValue); + switch (metric) { + case 'average': + valueCounter[bucketIdentifier] = + (valueCounter[bucketIdentifier] ?? 0) + currentNumberValues.length; + case 'sum': + accumulators[bucketIdentifier] = currentNumberValues.reduce( + (a, b) => a + b, + accumulatorValue || 0 + ); + break; + case 'min': + if (typeof accumulatorValue !== 'undefined') { + accumulators[bucketIdentifier] = Math.min(accumulatorValue, ...currentNumberValues); + } else { + accumulators[bucketIdentifier] = Math.min(...currentNumberValues); + } + break; + case 'max': + if (typeof accumulatorValue !== 'undefined') { + accumulators[bucketIdentifier] = Math.max(accumulatorValue, ...currentNumberValues); + } else { + accumulators[bucketIdentifier] = Math.max(...currentNumberValues); + } + break; + } + } + }); + if (metric === 'average') { + Object.keys(accumulators).forEach((bucketIdentifier) => { + const accumulatorValue = accumulators[bucketIdentifier]; + const valueCount = valueCounter[bucketIdentifier]; + if (typeof accumulatorValue !== 'undefined' && typeof valueCount !== 'undefined') { + accumulators[bucketIdentifier] = accumulatorValue / valueCount; + } + }); + } + return { + ...input, + columns: resultColumns, + rows: input.rows.map((row) => { + const newRow = { ...row }; + const bucketIdentifier = getBucketIdentifier(row, by); + newRow[outputColumnId] = accumulators[bucketIdentifier]; + + return newRow; + }), + }; +}; diff --git a/src/plugins/expressions/common/expression_functions/specs/tests/cumulative_sum.test.ts b/src/plugins/expressions/common/expression_functions/specs/tests/cumulative_sum.test.ts index 23fb9abb4fb4d..67210c742989f 100644 --- a/src/plugins/expressions/common/expression_functions/specs/tests/cumulative_sum.test.ts +++ b/src/plugins/expressions/common/expression_functions/specs/tests/cumulative_sum.test.ts @@ -14,10 +14,10 @@ import { Datatable } from '../../../expression_types/specs/datatable'; describe('interpreter/functions#cumulative_sum', () => { const fn = functionWrapper(cumulativeSum); const runFn = (input: Datatable, args: CumulativeSumArgs) => - fn(input, args, {} as ExecutionContext) as Datatable; + fn(input, args, {} as ExecutionContext) as Promise; - it('calculates cumulative sum', () => { - const result = runFn( + it('calculates cumulative sum', async () => { + const result = await runFn( { type: 'datatable', columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }], @@ -33,8 +33,8 @@ describe('interpreter/functions#cumulative_sum', () => { expect(result.rows.map((row) => row.output)).toEqual([5, 12, 15, 17]); }); - it('replaces null or undefined data with zeroes until there is real data', () => { - const result = runFn( + it('replaces null or undefined data with zeroes until there is real data', async () => { + const result = await runFn( { type: 'datatable', columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }], @@ -50,8 +50,8 @@ describe('interpreter/functions#cumulative_sum', () => { expect(result.rows.map((row) => row.output)).toEqual([0, 0, 0, 1]); }); - it('calculates cumulative sum for multiple series', () => { - const result = runFn( + it('calculates cumulative sum for multiple series', async () => { + const result = await runFn( { type: 'datatable', columns: [ @@ -84,8 +84,8 @@ describe('interpreter/functions#cumulative_sum', () => { ]); }); - it('treats missing split column as separate series', () => { - const result = runFn( + it('treats missing split column as separate series', async () => { + const result = await runFn( { type: 'datatable', columns: [ @@ -117,8 +117,8 @@ describe('interpreter/functions#cumulative_sum', () => { ]); }); - it('treats null like undefined and empty string for split columns', () => { - const result = runFn( + it('treats null like undefined and empty string for split columns', async () => { + const result = await runFn( { type: 'datatable', columns: [ @@ -152,8 +152,8 @@ describe('interpreter/functions#cumulative_sum', () => { ]); }); - it('calculates cumulative sum for multiple series by multiple split columns', () => { - const result = runFn( + it('calculates cumulative sum for multiple series by multiple split columns', async () => { + const result = await runFn( { type: 'datatable', columns: [ @@ -177,8 +177,8 @@ describe('interpreter/functions#cumulative_sum', () => { expect(result.rows.map((row) => row.output)).toEqual([1, 2, 3, 1 + 4, 5, 6, 7, 7 + 8]); }); - it('splits separate series by the string representation of the cell values', () => { - const result = runFn( + it('splits separate series by the string representation of the cell values', async () => { + const result = await runFn( { type: 'datatable', columns: [ @@ -198,8 +198,8 @@ describe('interpreter/functions#cumulative_sum', () => { expect(result.rows.map((row) => row.output)).toEqual([1, 1 + 2, 10, 21]); }); - it('casts values to number before calculating cumulative sum', () => { - const result = runFn( + it('casts values to number before calculating cumulative sum', async () => { + const result = await runFn( { type: 'datatable', columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }], @@ -210,8 +210,8 @@ describe('interpreter/functions#cumulative_sum', () => { expect(result.rows.map((row) => row.output)).toEqual([5, 12, 15, 17]); }); - it('casts values to number before calculating cumulative sum for NaN like values', () => { - const result = runFn( + it('casts values to number before calculating cumulative sum for NaN like values', async () => { + const result = await runFn( { type: 'datatable', columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }], @@ -222,8 +222,8 @@ describe('interpreter/functions#cumulative_sum', () => { expect(result.rows.map((row) => row.output)).toEqual([5, 12, NaN, NaN]); }); - it('skips undefined and null values', () => { - const result = runFn( + it('skips undefined and null values', async () => { + const result = await runFn( { type: 'datatable', columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }], @@ -244,8 +244,8 @@ describe('interpreter/functions#cumulative_sum', () => { expect(result.rows.map((row) => row.output)).toEqual([0, 7, 7, 7, 7, 7, 10, 12, 12]); }); - it('copies over meta information from the source column', () => { - const result = runFn( + it('copies over meta information from the source column', async () => { + const result = await runFn( { type: 'datatable', columns: [ @@ -286,8 +286,8 @@ describe('interpreter/functions#cumulative_sum', () => { }); }); - it('sets output name on output column if specified', () => { - const result = runFn( + it('sets output name on output column if specified', async () => { + const result = await runFn( { type: 'datatable', columns: [ @@ -310,7 +310,7 @@ describe('interpreter/functions#cumulative_sum', () => { }); }); - it('returns source table if input column does not exist', () => { + it('returns source table if input column does not exist', async () => { const input: Datatable = { type: 'datatable', columns: [ @@ -324,11 +324,13 @@ describe('interpreter/functions#cumulative_sum', () => { ], rows: [{ val: 5 }], }; - expect(runFn(input, { inputColumnId: 'nonexisting', outputColumnId: 'output' })).toBe(input); + expect(await runFn(input, { inputColumnId: 'nonexisting', outputColumnId: 'output' })).toBe( + input + ); }); - it('throws an error if output column exists already', () => { - expect(() => + it('throws an error if output column exists already', async () => { + await expect( runFn( { type: 'datatable', @@ -345,6 +347,6 @@ describe('interpreter/functions#cumulative_sum', () => { }, { inputColumnId: 'val', outputColumnId: 'val' } ) - ).toThrow(); + ).rejects.toBeDefined(); }); }); diff --git a/src/plugins/expressions/common/expression_functions/specs/tests/derivative.test.ts b/src/plugins/expressions/common/expression_functions/specs/tests/derivative.test.ts index 27601f533b64e..615f4eb51493b 100644 --- a/src/plugins/expressions/common/expression_functions/specs/tests/derivative.test.ts +++ b/src/plugins/expressions/common/expression_functions/specs/tests/derivative.test.ts @@ -14,10 +14,10 @@ import { Datatable } from '../../../expression_types/specs/datatable'; describe('interpreter/functions#derivative', () => { const fn = functionWrapper(derivative); const runFn = (input: Datatable, args: DerivativeArgs) => - fn(input, args, {} as ExecutionContext) as Datatable; + fn(input, args, {} as ExecutionContext) as Promise; - it('calculates derivative', () => { - const result = runFn( + it('calculates derivative', async () => { + const result = await runFn( { type: 'datatable', columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }], @@ -33,8 +33,8 @@ describe('interpreter/functions#derivative', () => { expect(result.rows.map((row) => row.output)).toEqual([undefined, 2, -4, -1]); }); - it('skips null or undefined values until there is real data', () => { - const result = runFn( + it('skips null or undefined values until there is real data', async () => { + const result = await runFn( { type: 'datatable', columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }], @@ -70,8 +70,8 @@ describe('interpreter/functions#derivative', () => { ]); }); - it('treats 0 as real data', () => { - const result = runFn( + it('treats 0 as real data', async () => { + const result = await runFn( { type: 'datatable', columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }], @@ -108,8 +108,8 @@ describe('interpreter/functions#derivative', () => { ]); }); - it('calculates derivative for multiple series', () => { - const result = runFn( + it('calculates derivative for multiple series', async () => { + const result = await runFn( { type: 'datatable', columns: [ @@ -142,8 +142,8 @@ describe('interpreter/functions#derivative', () => { ]); }); - it('treats missing split column as separate series', () => { - const result = runFn( + it('treats missing split column as separate series', async () => { + const result = await runFn( { type: 'datatable', columns: [ @@ -175,8 +175,8 @@ describe('interpreter/functions#derivative', () => { ]); }); - it('treats null like undefined and empty string for split columns', () => { - const result = runFn( + it('treats null like undefined and empty string for split columns', async () => { + const result = await runFn( { type: 'datatable', columns: [ @@ -210,8 +210,8 @@ describe('interpreter/functions#derivative', () => { ]); }); - it('calculates derivative for multiple series by multiple split columns', () => { - const result = runFn( + it('calculates derivative for multiple series by multiple split columns', async () => { + const result = await runFn( { type: 'datatable', columns: [ @@ -244,8 +244,8 @@ describe('interpreter/functions#derivative', () => { ]); }); - it('splits separate series by the string representation of the cell values', () => { - const result = runFn( + it('splits separate series by the string representation of the cell values', async () => { + const result = await runFn( { type: 'datatable', columns: [ @@ -265,8 +265,8 @@ describe('interpreter/functions#derivative', () => { expect(result.rows.map((row) => row.output)).toEqual([undefined, 2 - 1, undefined, 11 - 10]); }); - it('casts values to number before calculating derivative', () => { - const result = runFn( + it('casts values to number before calculating derivative', async () => { + const result = await runFn( { type: 'datatable', columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }], @@ -277,8 +277,8 @@ describe('interpreter/functions#derivative', () => { expect(result.rows.map((row) => row.output)).toEqual([undefined, 7 - 5, 3 - 7, 2 - 3]); }); - it('casts values to number before calculating derivative for NaN like values', () => { - const result = runFn( + it('casts values to number before calculating derivative for NaN like values', async () => { + const result = await runFn( { type: 'datatable', columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }], @@ -289,8 +289,8 @@ describe('interpreter/functions#derivative', () => { expect(result.rows.map((row) => row.output)).toEqual([undefined, 7 - 5, NaN, NaN, 5 - 2]); }); - it('copies over meta information from the source column', () => { - const result = runFn( + it('copies over meta information from the source column', async () => { + const result = await runFn( { type: 'datatable', columns: [ @@ -331,8 +331,8 @@ describe('interpreter/functions#derivative', () => { }); }); - it('sets output name on output column if specified', () => { - const result = runFn( + it('sets output name on output column if specified', async () => { + const result = await runFn( { type: 'datatable', columns: [ @@ -355,7 +355,7 @@ describe('interpreter/functions#derivative', () => { }); }); - it('returns source table if input column does not exist', () => { + it('returns source table if input column does not exist', async () => { const input: Datatable = { type: 'datatable', columns: [ @@ -369,11 +369,13 @@ describe('interpreter/functions#derivative', () => { ], rows: [{ val: 5 }], }; - expect(runFn(input, { inputColumnId: 'nonexisting', outputColumnId: 'output' })).toBe(input); + expect(await runFn(input, { inputColumnId: 'nonexisting', outputColumnId: 'output' })).toBe( + input + ); }); - it('throws an error if output column exists already', () => { - expect(() => + it('throws an error if output column exists already', async () => { + await expect( runFn( { type: 'datatable', @@ -390,6 +392,6 @@ describe('interpreter/functions#derivative', () => { }, { inputColumnId: 'val', outputColumnId: 'val' } ) - ).toThrow(); + ).rejects.toBeDefined(); }); }); diff --git a/src/plugins/expressions/common/expression_functions/specs/tests/math.test.ts b/src/plugins/expressions/common/expression_functions/specs/tests/math.test.ts index 3761fe0a4f909..f0dadad15b67e 100644 --- a/src/plugins/expressions/common/expression_functions/specs/tests/math.test.ts +++ b/src/plugins/expressions/common/expression_functions/specs/tests/math.test.ts @@ -6,129 +6,163 @@ * Side Public License, v 1. */ -import { errors, math, MathArguments, MathInput } from '../math'; +import { math, MathArguments, MathInput } from '../math'; +import { errors } from '../math_fn'; import { emptyTable, functionWrapper, testTable } from './utils'; describe('math', () => { const fn = functionWrapper(math); - it('evaluates math expressions without reference to context', () => { - expect(fn(null as unknown as MathInput, { expression: '10.5345' })).toBe(10.5345); - expect(fn(null as unknown as MathInput, { expression: '123 + 456' })).toBe(579); - expect(fn(null as unknown as MathInput, { expression: '100 - 46' })).toBe(54); - expect(fn(1, { expression: '100 / 5' })).toBe(20); - expect(fn('foo' as unknown as MathInput, { expression: '100 / 5' })).toBe(20); - expect(fn(true as unknown as MathInput, { expression: '100 / 5' })).toBe(20); - expect(fn(testTable, { expression: '100 * 5' })).toBe(500); - expect(fn(emptyTable, { expression: '100 * 5' })).toBe(500); + it('evaluates math expressions without reference to context', async () => { + expect(await fn(null as unknown as MathInput, { expression: '10.5345' })).toBe(10.5345); + expect(await fn(null as unknown as MathInput, { expression: '123 + 456' })).toBe(579); + expect(await fn(null as unknown as MathInput, { expression: '100 - 46' })).toBe(54); + expect(await fn(1, { expression: '100 / 5' })).toBe(20); + expect(await fn('foo' as unknown as MathInput, { expression: '100 / 5' })).toBe(20); + expect(await fn(true as unknown as MathInput, { expression: '100 / 5' })).toBe(20); + expect(await fn(testTable, { expression: '100 * 5' })).toBe(500); + expect(await fn(emptyTable, { expression: '100 * 5' })).toBe(500); }); - it('evaluates math expressions with reference to the value of the context, must be a number', () => { - expect(fn(-103, { expression: 'abs(value)' })).toBe(103); + it('evaluates math expressions with reference to the value of the context, must be a number', async () => { + expect(await fn(-103, { expression: 'abs(value)' })).toBe(103); }); - it('evaluates math expressions with references to columns by id in a datatable', () => { - expect(fn(testTable, { expression: 'unique(in_stock)' })).toBe(2); - expect(fn(testTable, { expression: 'sum(quantity)' })).toBe(2508); - expect(fn(testTable, { expression: 'mean(price)' })).toBe(320); - expect(fn(testTable, { expression: 'min(price)' })).toBe(67); - expect(fn(testTable, { expression: 'median(quantity)' })).toBe(256); - expect(fn(testTable, { expression: 'max(price)' })).toBe(605); + it('evaluates math expressions with references to columns by id in a datatable', async () => { + expect(await fn(testTable, { expression: 'unique(in_stock)' })).toBe(2); + expect(await fn(testTable, { expression: 'sum(quantity)' })).toBe(2508); + expect(await fn(testTable, { expression: 'mean(price)' })).toBe(320); + expect(await fn(testTable, { expression: 'min(price)' })).toBe(67); + expect(await fn(testTable, { expression: 'median(quantity)' })).toBe(256); + expect(await fn(testTable, { expression: 'max(price)' })).toBe(605); }); - it('does not use the name for math', () => { - expect(() => fn(testTable, { expression: 'unique("in_stock label")' })).toThrow( - 'Unknown variable' + it('does not use the name for math', async () => { + await expect(fn(testTable, { expression: 'unique("in_stock label")' })).rejects.toHaveProperty( + 'message', + 'Unknown variable: in_stock label' ); - expect(() => fn(testTable, { expression: 'sum("quantity label")' })).toThrow( - 'Unknown variable' + + await expect(fn(testTable, { expression: 'sum("quantity label")' })).rejects.toHaveProperty( + 'message', + 'Unknown variable: quantity label' + ); + + await expect(fn(testTable, { expression: 'mean("price label")' })).rejects.toHaveProperty( + 'message', + 'Unknown variable: price label' + ); + await expect(fn(testTable, { expression: 'min("price label")' })).rejects.toHaveProperty( + 'message', + 'Unknown variable: price label' + ); + + await expect(fn(testTable, { expression: 'median("quantity label")' })).rejects.toHaveProperty( + 'message', + 'Unknown variable: quantity label' ); - expect(() => fn(testTable, { expression: 'mean("price label")' })).toThrow('Unknown variable'); - expect(() => fn(testTable, { expression: 'min("price label")' })).toThrow('Unknown variable'); - expect(() => fn(testTable, { expression: 'median("quantity label")' })).toThrow( - 'Unknown variable' + + await expect(fn(testTable, { expression: 'max("price label")' })).rejects.toHaveProperty( + 'message', + 'Unknown variable: price label' ); - expect(() => fn(testTable, { expression: 'max("price label")' })).toThrow('Unknown variable'); }); describe('args', () => { describe('expression', () => { - it('sets the math expression to be evaluted', () => { - expect(fn(null as unknown as MathInput, { expression: '10' })).toBe(10); - expect(fn(23.23, { expression: 'floor(value)' })).toBe(23); - expect(fn(testTable, { expression: 'count(price)' })).toBe(9); - expect(fn(testTable, { expression: 'count(name)' })).toBe(9); + it('sets the math expression to be evaluted', async () => { + expect(await fn(null as unknown as MathInput, { expression: '10' })).toBe(10); + expect(await fn(23.23, { expression: 'floor(value)' })).toBe(23); + expect(await fn(testTable, { expression: 'count(price)' })).toBe(9); + expect(await fn(testTable, { expression: 'count(name)' })).toBe(9); }); }); describe('onError', () => { - it('should return the desired fallback value, for invalid expressions', () => { - expect(fn(testTable, { expression: 'mean(name)', onError: 'zero' })).toBe(0); - expect(fn(testTable, { expression: 'mean(name)', onError: 'null' })).toBe(null); - expect(fn(testTable, { expression: 'mean(name)', onError: 'false' })).toBe(false); + it('should return the desired fallback value, for invalid expressions', async () => { + expect(await fn(testTable, { expression: 'mean(name)', onError: 'zero' })).toBe(0); + expect(await fn(testTable, { expression: 'mean(name)', onError: 'null' })).toBe(null); + expect(await fn(testTable, { expression: 'mean(name)', onError: 'false' })).toBe(false); }); - it('should return the desired fallback value, for division by zero', () => { - expect(fn(testTable, { expression: '1/0', onError: 'zero' })).toBe(0); - expect(fn(testTable, { expression: '1/0', onError: 'null' })).toBe(null); - expect(fn(testTable, { expression: '1/0', onError: 'false' })).toBe(false); + it('should return the desired fallback value, for division by zero', async () => { + expect(await fn(testTable, { expression: '1/0', onError: 'zero' })).toBe(0); + expect(await fn(testTable, { expression: '1/0', onError: 'null' })).toBe(null); + expect(await fn(testTable, { expression: '1/0', onError: 'false' })).toBe(false); }); }); }); describe('invalid expressions', () => { - it('throws when expression evaluates to an array', () => { - expect(() => fn(testTable, { expression: 'multiply(price, 2)' })).toThrow( - new RegExp(errors.tooManyResults().message.replace(/[()]/g, '\\$&')) + it('throws when expression evaluates to an array', async () => { + await expect(fn(testTable, { expression: 'multiply(price, 2)' })).rejects.toHaveProperty( + 'message', + errors.tooManyResults().message ); }); - it('throws when using an unknown context variable', () => { - expect(() => fn(testTable, { expression: 'sum(foo)' })).toThrow('Unknown variable: foo'); + it('throws when using an unknown context variable', async () => { + await expect(fn(testTable, { expression: 'sum(foo)' })).rejects.toHaveProperty( + 'message', + 'Unknown variable: foo' + ); }); - it('throws when using non-numeric data', () => { - expect(() => fn(testTable, { expression: 'mean(name)' })).toThrow( - new RegExp(errors.executionFailed().message) + it('throws when using non-numeric data', async () => { + await expect(fn(testTable, { expression: 'mean(name)' })).rejects.toHaveProperty( + 'message', + errors.executionFailed().message ); - expect(() => fn(testTable, { expression: 'mean(in_stock)' })).toThrow( - new RegExp(errors.executionFailed().message) + await expect(fn(testTable, { expression: 'mean(in_stock)' })).rejects.toHaveProperty( + 'message', + errors.executionFailed().message ); }); - it('throws when missing expression', () => { - expect(() => fn(testTable)).toThrow(new RegExp(errors.emptyExpression().message)); - - expect(() => fn(testTable, { expession: '' } as unknown as MathArguments)).toThrow( - new RegExp(errors.emptyExpression().message) + it('throws when missing expression', async () => { + await expect(fn(testTable)).rejects.toHaveProperty( + 'message', + errors.emptyExpression().message ); - expect(() => fn(testTable, { expession: ' ' } as unknown as MathArguments)).toThrow( - new RegExp(errors.emptyExpression().message) - ); + await expect( + fn(testTable, { expession: '' } as unknown as MathArguments) + ).rejects.toHaveProperty('message', errors.emptyExpression().message); + + await expect( + fn(testTable, { expession: ' ' } as unknown as MathArguments) + ).rejects.toHaveProperty('message', errors.emptyExpression().message); }); - it('throws when passing a context variable from an empty datatable', () => { - expect(() => fn(emptyTable, { expression: 'mean(foo)' })).toThrow( - new RegExp(errors.emptyDatatable().message) + it('throws when passing a context variable from an empty datatable', async () => { + await expect(() => fn(emptyTable, { expression: 'mean(foo)' })).rejects.toHaveProperty( + 'message', + errors.emptyDatatable().message ); }); - it('should not throw when requesting fallback values for invalid expression', () => { - expect(() => fn(testTable, { expression: 'mean(name)', onError: 'zero' })).not.toThrow(); - expect(() => fn(testTable, { expression: 'mean(name)', onError: 'false' })).not.toThrow(); - expect(() => fn(testTable, { expression: 'mean(name)', onError: 'null' })).not.toThrow(); + it('should not throw when requesting fallback values for invalid expression', async () => { + await expect( + fn(testTable, { expression: 'mean(name)', onError: 'zero' }) + ).resolves.toBeDefined(); + await expect( + fn(testTable, { expression: 'mean(name)', onError: 'false' }) + ).resolves.toBeDefined(); + await expect( + fn(testTable, { expression: 'mean(name)', onError: 'null' }) + ).resolves.toBeDefined(); }); - it('should throw when declared in the onError argument', () => { - expect(() => fn(testTable, { expression: 'mean(name)', onError: 'throw' })).toThrow( - new RegExp(errors.executionFailed().message) - ); + it('should throw when declared in the onError argument', async () => { + await expect( + fn(testTable, { expression: 'mean(name)', onError: 'throw' }) + ).rejects.toHaveProperty('message', errors.executionFailed().message); }); - it('should throw when dividing by zero', () => { - expect(() => fn(testTable, { expression: '1/0', onError: 'throw' })).toThrow( - new RegExp('Cannot divide by 0') + it('should throw when dividing by zero', async () => { + await expect(fn(testTable, { expression: '1/0', onError: 'throw' })).rejects.toHaveProperty( + 'message', + 'Cannot divide by 0' ); }); }); diff --git a/src/plugins/expressions/common/expression_functions/specs/tests/math_column.test.ts b/src/plugins/expressions/common/expression_functions/specs/tests/math_column.test.ts index 3464736fe0ad3..739e31199d1f8 100644 --- a/src/plugins/expressions/common/expression_functions/specs/tests/math_column.test.ts +++ b/src/plugins/expressions/common/expression_functions/specs/tests/math_column.test.ts @@ -12,14 +12,18 @@ import { functionWrapper, testTable, tableWithNulls } from './utils'; describe('mathColumn', () => { const fn = functionWrapper(mathColumn); - it('throws if the id is used', () => { - expect(() => fn(testTable, { id: 'price', name: 'price', expression: 'price * 2' })).toThrow( - `ID must be unique` - ); + it('throws if the id is used', async () => { + await expect( + fn(testTable, { id: 'price', name: 'price', expression: 'price * 2' }) + ).rejects.toHaveProperty('message', `ID must be unique`); }); - it('applies math to each row by id', () => { - const result = fn(testTable, { id: 'output', name: 'output', expression: 'quantity * price' }); + it('applies math to each row by id', async () => { + const result = await fn(testTable, { + id: 'output', + name: 'output', + expression: 'quantity * price', + }); expect(result.columns).toEqual([ ...testTable.columns, { id: 'output', name: 'output', meta: { params: { id: 'number' }, type: 'number' } }, @@ -34,7 +38,7 @@ describe('mathColumn', () => { }); }); - it('extracts a single array value, but not a multi-value array', () => { + it('extracts a single array value, but not a multi-value array', async () => { const arrayTable = { ...testTable, rows: [ @@ -52,23 +56,24 @@ describe('mathColumn', () => { name: 'output', expression: 'quantity', }; - expect(fn(arrayTable, args).rows[0].output).toEqual(100); - expect(() => fn(arrayTable, { ...args, expression: 'price' })).toThrowError( - `Cannot perform math on array values` + expect((await fn(arrayTable, args)).rows[0].output).toEqual(100); + await expect(fn(arrayTable, { ...args, expression: 'price' })).rejects.toHaveProperty( + 'message', + `Cannot perform math on array values at output` ); }); - it('handles onError', () => { + it('handles onError', async () => { const args = { id: 'output', name: 'output', expression: 'quantity / 0', }; - expect(() => fn(testTable, args)).toThrowError(`Cannot divide by 0`); - expect(() => fn(testTable, { ...args, onError: 'throw' })).toThrow(); - expect(fn(testTable, { ...args, onError: 'zero' }).rows[0].output).toEqual(0); - expect(fn(testTable, { ...args, onError: 'false' }).rows[0].output).toEqual(false); - expect(fn(testTable, { ...args, onError: 'null' }).rows[0].output).toEqual(null); + await expect(fn(testTable, args)).rejects.toHaveProperty('message', `Cannot divide by 0`); + await expect(fn(testTable, { ...args, onError: 'throw' })).rejects.toBeDefined(); + expect((await fn(testTable, { ...args, onError: 'zero' })).rows[0].output).toEqual(0); + expect((await fn(testTable, { ...args, onError: 'false' })).rows[0].output).toEqual(false); + expect((await fn(testTable, { ...args, onError: 'null' })).rows[0].output).toEqual(null); }); it('should copy over the meta information from the specified column', async () => { @@ -96,9 +101,14 @@ describe('mathColumn', () => { }); }); - it('should correctly infer the type from the first non-null row', () => { + it('should correctly infer the type from the first non-null row', async () => { expect( - fn(tableWithNulls, { id: 'value', name: 'value', expression: 'price + 2', onError: 'null' }) + await fn(tableWithNulls, { + id: 'value', + name: 'value', + expression: 'price + 2', + onError: 'null', + }) ).toEqual( expect.objectContaining({ type: 'datatable', diff --git a/src/plugins/expressions/common/expression_functions/specs/tests/moving_average.test.ts b/src/plugins/expressions/common/expression_functions/specs/tests/moving_average.test.ts index 6f816f8310314..5c2463b90f137 100644 --- a/src/plugins/expressions/common/expression_functions/specs/tests/moving_average.test.ts +++ b/src/plugins/expressions/common/expression_functions/specs/tests/moving_average.test.ts @@ -16,10 +16,10 @@ const defaultArgs = { window: 5, inputColumnId: 'val', outputColumnId: 'output' describe('interpreter/functions#movingAverage', () => { const fn = functionWrapper(movingAverage); const runFn = (input: Datatable, args: MovingAverageArgs) => - fn(input, args, {} as ExecutionContext) as Datatable; + fn(input, args, {} as ExecutionContext) as Promise; - it('calculates movingAverage', () => { - const result = runFn( + it('calculates movingAverage', async () => { + const result = await runFn( { type: 'datatable', columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }], @@ -40,8 +40,8 @@ describe('interpreter/functions#movingAverage', () => { ]); }); - it('skips null or undefined values until there is real data', () => { - const result = runFn( + it('skips null or undefined values until there is real data', async () => { + const result = await runFn( { type: 'datatable', columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }], @@ -77,8 +77,8 @@ describe('interpreter/functions#movingAverage', () => { ]); }); - it('treats 0 as real data', () => { - const result = runFn( + it('treats 0 as real data', async () => { + const result = await runFn( { type: 'datatable', columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }], @@ -115,8 +115,8 @@ describe('interpreter/functions#movingAverage', () => { ]); }); - it('calculates movingAverage for multiple series', () => { - const result = runFn( + it('calculates movingAverage for multiple series', async () => { + const result = await runFn( { type: 'datatable', columns: [ @@ -149,8 +149,8 @@ describe('interpreter/functions#movingAverage', () => { ]); }); - it('treats missing split column as separate series', () => { - const result = runFn( + it('treats missing split column as separate series', async () => { + const result = await runFn( { type: 'datatable', columns: [ @@ -182,8 +182,8 @@ describe('interpreter/functions#movingAverage', () => { ]); }); - it('treats null like undefined and empty string for split columns', () => { - const result = runFn( + it('treats null like undefined and empty string for split columns', async () => { + const result = await runFn( { type: 'datatable', columns: [ @@ -217,8 +217,8 @@ describe('interpreter/functions#movingAverage', () => { ]); }); - it('calculates movingAverage for multiple series by multiple split columns', () => { - const result = runFn( + it('calculates movingAverage for multiple series by multiple split columns', async () => { + const result = await runFn( { type: 'datatable', columns: [ @@ -251,8 +251,8 @@ describe('interpreter/functions#movingAverage', () => { ]); }); - it('splits separate series by the string representation of the cell values', () => { - const result = runFn( + it('splits separate series by the string representation of the cell values', async () => { + const result = await runFn( { type: 'datatable', columns: [ @@ -272,8 +272,8 @@ describe('interpreter/functions#movingAverage', () => { expect(result.rows.map((row) => row.output)).toEqual([undefined, 1, undefined, 10]); }); - it('casts values to number before calculating movingAverage', () => { - const result = runFn( + it('casts values to number before calculating movingAverage', async () => { + const result = await runFn( { type: 'datatable', columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }], @@ -289,8 +289,8 @@ describe('interpreter/functions#movingAverage', () => { ]); }); - it('skips NaN like values', () => { - const result = runFn( + it('skips NaN like values', async () => { + const result = await runFn( { type: 'datatable', columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }], @@ -301,8 +301,8 @@ describe('interpreter/functions#movingAverage', () => { expect(result.rows.map((row) => row.output)).toEqual([undefined, 5, (5 + 7) / 2, NaN, NaN]); }); - it('copies over meta information from the source column', () => { - const result = runFn( + it('copies over meta information from the source column', async () => { + const result = await runFn( { type: 'datatable', columns: [ @@ -343,8 +343,8 @@ describe('interpreter/functions#movingAverage', () => { }); }); - it('sets output name on output column if specified', () => { - const result = runFn( + it('sets output name on output column if specified', async () => { + const result = await runFn( { type: 'datatable', columns: [ @@ -367,7 +367,7 @@ describe('interpreter/functions#movingAverage', () => { }); }); - it('returns source table if input column does not exist', () => { + it('returns source table if input column does not exist', async () => { const input: Datatable = { type: 'datatable', columns: [ @@ -382,12 +382,12 @@ describe('interpreter/functions#movingAverage', () => { rows: [{ val: 5 }], }; expect( - runFn(input, { ...defaultArgs, inputColumnId: 'nonexisting', outputColumnId: 'output' }) + await runFn(input, { ...defaultArgs, inputColumnId: 'nonexisting', outputColumnId: 'output' }) ).toBe(input); }); - it('throws an error if output column exists already', () => { - expect(() => + it('throws an error if output column exists already', async () => { + await expect( runFn( { type: 'datatable', @@ -404,11 +404,11 @@ describe('interpreter/functions#movingAverage', () => { }, { ...defaultArgs, inputColumnId: 'val', outputColumnId: 'val' } ) - ).toThrow(); + ).rejects.toBeDefined(); }); - it('calculates moving average for window equal to 1', () => { - const result = runFn( + it('calculates moving average for window equal to 1', async () => { + const result = await runFn( { type: 'datatable', columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }], @@ -441,8 +441,8 @@ describe('interpreter/functions#movingAverage', () => { ]); }); - it('calculates moving average for window bigger than array', () => { - const result = runFn( + it('calculates moving average for window bigger than array', async () => { + const result = await runFn( { type: 'datatable', columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }], diff --git a/src/plugins/expressions/common/expression_functions/specs/tests/overall_metric.test.ts b/src/plugins/expressions/common/expression_functions/specs/tests/overall_metric.test.ts index 94bd3a7edba5d..d43c0650dc9d0 100644 --- a/src/plugins/expressions/common/expression_functions/specs/tests/overall_metric.test.ts +++ b/src/plugins/expressions/common/expression_functions/specs/tests/overall_metric.test.ts @@ -14,10 +14,10 @@ import { overallMetric, OverallMetricArgs } from '../overall_metric'; describe('interpreter/functions#overall_metric', () => { const fn = functionWrapper(overallMetric); const runFn = (input: Datatable, args: OverallMetricArgs) => - fn(input, args, {} as ExecutionContext) as Datatable; + fn(input, args, {} as ExecutionContext) as Promise; - it('ignores null or undefined with sum', () => { - const result = runFn( + it('ignores null or undefined with sum', async () => { + const result = await runFn( { type: 'datatable', columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }], @@ -33,8 +33,8 @@ describe('interpreter/functions#overall_metric', () => { expect(result.rows.map((row) => row.output)).toEqual([12, 12, 12, 12]); }); - it('ignores null or undefined with average', () => { - const result = runFn( + it('ignores null or undefined with average', async () => { + const result = await runFn( { type: 'datatable', columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }], @@ -50,8 +50,8 @@ describe('interpreter/functions#overall_metric', () => { expect(result.rows.map((row) => row.output)).toEqual([3, 3, 3, 3, 3]); }); - it('ignores null or undefined with min', () => { - const result = runFn( + it('ignores null or undefined with min', async () => { + const result = await runFn( { type: 'datatable', columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }], @@ -67,8 +67,8 @@ describe('interpreter/functions#overall_metric', () => { expect(result.rows.map((row) => row.output)).toEqual([1, 1, 1, 1, 1]); }); - it('ignores null or undefined with max', () => { - const result = runFn( + it('ignores null or undefined with max', async () => { + const result = await runFn( { type: 'datatable', columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }], @@ -84,8 +84,8 @@ describe('interpreter/functions#overall_metric', () => { expect(result.rows.map((row) => row.output)).toEqual([-1, -1, -1, -1, -1]); }); - it('calculates overall sum for multiple series', () => { - const result = runFn( + it('calculates overall sum for multiple series', async () => { + const result = await runFn( { type: 'datatable', columns: [ @@ -118,8 +118,8 @@ describe('interpreter/functions#overall_metric', () => { ]); }); - it('treats missing split column as separate series', () => { - const result = runFn( + it('treats missing split column as separate series', async () => { + const result = await runFn( { type: 'datatable', columns: [ @@ -142,7 +142,7 @@ describe('interpreter/functions#overall_metric', () => { expect(result.rows.map((row) => row.output)).toEqual([1, 2, 3, 1, 3, 1, 2, 2]); }); - it('treats null like undefined and empty string for split columns', () => { + it('treats null like undefined and empty string for split columns', async () => { const table: Datatable = { type: 'datatable', columns: [ @@ -162,7 +162,7 @@ describe('interpreter/functions#overall_metric', () => { ], }; - const result = runFn(table, { + const result = await runFn(table, { inputColumnId: 'val', outputColumnId: 'output', by: ['split'], @@ -180,7 +180,7 @@ describe('interpreter/functions#overall_metric', () => { 3 + 5 + 7 + 9, ]); - const result2 = runFn(table, { + const result2 = await runFn(table, { inputColumnId: 'val', outputColumnId: 'output', by: ['split'], @@ -188,7 +188,7 @@ describe('interpreter/functions#overall_metric', () => { }); expect(result2.rows.map((row) => row.output)).toEqual([6, 8, 9, 6, 9, 6, 9, 8, 9]); - const result3 = runFn(table, { + const result3 = await runFn(table, { inputColumnId: 'val', outputColumnId: 'output', by: ['split'], @@ -207,8 +207,8 @@ describe('interpreter/functions#overall_metric', () => { ]); }); - it('handles array values', () => { - const result = runFn( + it('handles array values', async () => { + const result = await runFn( { type: 'datatable', columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }], @@ -224,8 +224,8 @@ describe('interpreter/functions#overall_metric', () => { expect(result.rows.map((row) => row.output)).toEqual([28, 28, 28, 28]); }); - it('takes array values into account for average calculation', () => { - const result = runFn( + it('takes array values into account for average calculation', async () => { + const result = await runFn( { type: 'datatable', columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }], @@ -241,7 +241,7 @@ describe('interpreter/functions#overall_metric', () => { expect(result.rows.map((row) => row.output)).toEqual([3, 3]); }); - it('handles array values for split columns', () => { + it('handles array values for split columns', async () => { const table: Datatable = { type: 'datatable', columns: [ @@ -261,7 +261,7 @@ describe('interpreter/functions#overall_metric', () => { ], }; - const result = runFn(table, { + const result = await runFn(table, { inputColumnId: 'val', outputColumnId: 'output', by: ['split'], @@ -279,7 +279,7 @@ describe('interpreter/functions#overall_metric', () => { 3 + 5 + 7 + 9 + 99, ]); - const result2 = runFn(table, { + const result2 = await runFn(table, { inputColumnId: 'val', outputColumnId: 'output', by: ['split'], @@ -288,8 +288,8 @@ describe('interpreter/functions#overall_metric', () => { expect(result2.rows.map((row) => row.output)).toEqual([6, 11, 99, 6, 99, 6, 99, 11, 99]); }); - it('calculates cumulative sum for multiple series by multiple split columns', () => { - const result = runFn( + it('calculates cumulative sum for multiple series by multiple split columns', async () => { + const result = await runFn( { type: 'datatable', columns: [ @@ -313,8 +313,8 @@ describe('interpreter/functions#overall_metric', () => { expect(result.rows.map((row) => row.output)).toEqual([1 + 4, 2, 3, 1 + 4, 5, 6, 7 + 8, 7 + 8]); }); - it('splits separate series by the string representation of the cell values', () => { - const result = runFn( + it('splits separate series by the string representation of the cell values', async () => { + const result = await runFn( { type: 'datatable', columns: [ @@ -334,8 +334,8 @@ describe('interpreter/functions#overall_metric', () => { expect(result.rows.map((row) => row.output)).toEqual([1 + 2, 1 + 2, 10 + 11, 10 + 11]); }); - it('casts values to number before calculating cumulative sum', () => { - const result = runFn( + it('casts values to number before calculating cumulative sum', async () => { + const result = await runFn( { type: 'datatable', columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }], @@ -346,8 +346,8 @@ describe('interpreter/functions#overall_metric', () => { expect(result.rows.map((row) => row.output)).toEqual([7, 7, 7, 7]); }); - it('casts values to number before calculating metric for NaN like values', () => { - const result = runFn( + it('casts values to number before calculating metric for NaN like values', async () => { + const result = await runFn( { type: 'datatable', columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }], @@ -358,8 +358,8 @@ describe('interpreter/functions#overall_metric', () => { expect(result.rows.map((row) => row.output)).toEqual([NaN, NaN, NaN, NaN]); }); - it('skips undefined and null values', () => { - const result = runFn( + it('skips undefined and null values', async () => { + const result = await runFn( { type: 'datatable', columns: [{ id: 'val', name: 'val', meta: { type: 'number' } }], @@ -380,8 +380,8 @@ describe('interpreter/functions#overall_metric', () => { expect(result.rows.map((row) => row.output)).toEqual([4, 4, 4, 4, 4, 4, 4, 4, 4]); }); - it('copies over meta information from the source column', () => { - const result = runFn( + it('copies over meta information from the source column', async () => { + const result = await runFn( { type: 'datatable', columns: [ @@ -422,8 +422,8 @@ describe('interpreter/functions#overall_metric', () => { }); }); - it('sets output name on output column if specified', () => { - const result = runFn( + it('sets output name on output column if specified', async () => { + const result = await runFn( { type: 'datatable', columns: [ @@ -451,7 +451,7 @@ describe('interpreter/functions#overall_metric', () => { }); }); - it('returns source table if input column does not exist', () => { + it('returns source table if input column does not exist', async () => { const input: Datatable = { type: 'datatable', columns: [ @@ -466,12 +466,12 @@ describe('interpreter/functions#overall_metric', () => { rows: [{ val: 5 }], }; expect( - runFn(input, { inputColumnId: 'nonexisting', outputColumnId: 'output', metric: 'sum' }) + await runFn(input, { inputColumnId: 'nonexisting', outputColumnId: 'output', metric: 'sum' }) ).toBe(input); }); - it('throws an error if output column exists already', () => { - expect(() => + it('throws an error if output column exists already', async () => { + await expect( runFn( { type: 'datatable', @@ -488,6 +488,6 @@ describe('interpreter/functions#overall_metric', () => { }, { inputColumnId: 'val', outputColumnId: 'val', metric: 'max' } ) - ).toThrow(); + ).rejects.toBeDefined(); }); }); diff --git a/src/plugins/expressions/public/index.ts b/src/plugins/expressions/public/index.ts index 6e8d220a3ca0c..3746d4d61a7bc 100644 --- a/src/plugins/expressions/public/index.ts +++ b/src/plugins/expressions/public/index.ts @@ -22,20 +22,23 @@ export function plugin(initializerContext: PluginInitializerContext) { } // Static exports. -export { ExpressionExecutor, IExpressionLoaderParams, ExpressionRenderError } from './types'; -export { +export type { + ExpressionExecutor, + IExpressionLoaderParams, + ExpressionRenderError, + ExpressionRendererEvent, +} from './types'; +export type { ExpressionLoader } from './loader'; +export type { ExpressionRenderHandler } from './render'; +export type { ExpressionRendererComponent, - ReactExpressionRenderer, ReactExpressionRendererProps, ReactExpressionRendererType, } from './react_expression_renderer'; -export { ExpressionRenderHandler, ExpressionRendererEvent } from './render'; -export { +export type { AnyExpressionFunctionDefinition, AnyExpressionTypeDefinition, ArgumentType, - buildExpression, - buildExpressionFunction, Datatable, DatatableColumn, DatatableColumnType, @@ -79,17 +82,12 @@ export { FontStyle, FontValue, FontWeight, - format, - formatExpression, FunctionsRegistry, IInterpreterRenderHandlers, InterpreterErrorType, IRegistry, - isExpressionAstBuilder, KnownTypeToString, Overflow, - parse, - parseExpression, PointSeries, PointSeriesColumn, PointSeriesColumnName, @@ -109,6 +107,13 @@ export { ExpressionsServiceSetup, ExpressionsServiceStart, TablesAdapter, - ExpressionsInspectorAdapter, +} from '../common'; + +export { + buildExpression, + buildExpressionFunction, + formatExpression, + isExpressionAstBuilder, + parseExpression, createDefaultInspectorAdapters, } from '../common'; diff --git a/src/plugins/expressions/public/loader.test.ts b/src/plugins/expressions/public/loader.test.ts index f22963cedf612..61a6ee3bda912 100644 --- a/src/plugins/expressions/public/loader.test.ts +++ b/src/plugins/expressions/public/loader.test.ts @@ -88,8 +88,8 @@ jest.mock('./services', () => { }); describe('execute helper function', () => { - it('returns ExpressionLoader instance', () => { - const response = loader(element, '', {}); + it('returns ExpressionLoader instance', async () => { + const response = await loader(element, '', {}); expect(response).toBeInstanceOf(ExpressionLoader); }); }); diff --git a/src/plugins/expressions/public/loader.ts b/src/plugins/expressions/public/loader.ts index b0a54e3dec35d..64384ebbfc852 100644 --- a/src/plugins/expressions/public/loader.ts +++ b/src/plugins/expressions/public/loader.ts @@ -194,10 +194,10 @@ export class ExpressionLoader { export type IExpressionLoader = ( element: HTMLElement, - expression: string | ExpressionAstExpression, - params: IExpressionLoaderParams -) => ExpressionLoader; + expression?: string | ExpressionAstExpression, + params?: IExpressionLoaderParams +) => Promise; -export const loader: IExpressionLoader = (element, expression, params) => { +export const loader: IExpressionLoader = async (element, expression?, params?) => { return new ExpressionLoader(element, expression, params); }; diff --git a/src/plugins/expressions/public/mocks.tsx b/src/plugins/expressions/public/mocks.tsx index f2f6a6807f339..353ae2908b54c 100644 --- a/src/plugins/expressions/public/mocks.tsx +++ b/src/plugins/expressions/public/mocks.tsx @@ -30,8 +30,6 @@ const createSetupContract = (): Setup => { const createStartContract = (): Start => { return { execute: jest.fn(), - ExpressionLoader: jest.fn(), - ExpressionRenderHandler: jest.fn(), getFunction: jest.fn(), getFunctions: jest.fn(), getRenderer: jest.fn(), @@ -39,8 +37,8 @@ const createStartContract = (): Start => { getType: jest.fn(), getTypes: jest.fn(), loader: jest.fn(), - ReactExpressionRenderer: jest.fn((props) => <>), render: jest.fn(), + ReactExpressionRenderer: jest.fn((props) => <>), run: jest.fn(), }; }; diff --git a/src/plugins/expressions/public/plugin.ts b/src/plugins/expressions/public/plugin.ts index 37d519ac45133..ccbeab791fe2c 100644 --- a/src/plugins/expressions/public/plugin.ts +++ b/src/plugins/expressions/public/plugin.ts @@ -8,16 +8,17 @@ import { pick } from 'lodash'; import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from 'src/core/public'; -import { ExpressionsServiceSetup, ExpressionsServiceStart } from '../common'; +import { SerializableRecord } from '@kbn/utility-types'; +import type { ExpressionsServiceSetup, ExpressionsServiceStart } from '../common'; import { ExpressionsService, setRenderersRegistry, setNotifications, setExpressionsService, } from './services'; -import { ReactExpressionRenderer } from './react_expression_renderer'; -import { ExpressionLoader, IExpressionLoader, loader } from './loader'; -import { render, ExpressionRenderHandler } from './render'; +import { ReactExpressionRenderer } from './react_expression_renderer_wrapper'; +import type { IExpressionLoader } from './loader'; +import type { IExpressionRenderer } from './render'; /** * Expressions public setup contract, extends {@link ExpressionsServiceSetup} @@ -28,11 +29,9 @@ export type ExpressionsSetup = ExpressionsServiceSetup; * Expressions public start contrect, extends {@link ExpressionServiceStart} */ export interface ExpressionsStart extends ExpressionsServiceStart { - ExpressionLoader: typeof ExpressionLoader; - ExpressionRenderHandler: typeof ExpressionRenderHandler; loader: IExpressionLoader; + render: IExpressionRenderer; ReactExpressionRenderer: typeof ReactExpressionRenderer; - render: typeof render; } export class ExpressionsPublicPlugin implements Plugin { @@ -66,13 +65,24 @@ export class ExpressionsPublicPlugin implements Plugin { + const { ExpressionLoader } = await import('./loader'); + return new ExpressionLoader(element, expression, params); + }; + + const render: IExpressionRenderer = async (element, data, options) => { + const { ExpressionRenderHandler } = await import('./render'); + const handler = new ExpressionRenderHandler(element, options); + handler.render(data as SerializableRecord); + return handler; + }; + const start = { ...expressions.start(), - ExpressionLoader, - ExpressionRenderHandler, loader, - ReactExpressionRenderer, render, + ReactExpressionRenderer, }; return Object.freeze(start); diff --git a/src/plugins/expressions/public/react_expression_renderer.test.tsx b/src/plugins/expressions/public/react_expression_renderer.test.tsx index f1932ce7dd6ba..cf19b333fed45 100644 --- a/src/plugins/expressions/public/react_expression_renderer.test.tsx +++ b/src/plugins/expressions/public/react_expression_renderer.test.tsx @@ -10,13 +10,12 @@ import React from 'react'; import { act } from 'react-dom/test-utils'; import { Subject } from 'rxjs'; import { share } from 'rxjs/operators'; -import { ReactExpressionRenderer } from './react_expression_renderer'; +import { default as ReactExpressionRenderer } from './react_expression_renderer'; import { ExpressionLoader } from './loader'; import { mount } from 'enzyme'; import { EuiProgress } from '@elastic/eui'; import { IInterpreterRenderHandlers } from '../common'; -import { RenderErrorHandlerFnType } from './types'; -import { ExpressionRendererEvent } from './render'; +import { RenderErrorHandlerFnType, ExpressionRendererEvent } from './types'; jest.mock('./loader', () => { return { diff --git a/src/plugins/expressions/public/react_expression_renderer.tsx b/src/plugins/expressions/public/react_expression_renderer.tsx index 428419c4ff022..77b4402b22c06 100644 --- a/src/plugins/expressions/public/react_expression_renderer.tsx +++ b/src/plugins/expressions/public/react_expression_renderer.tsx @@ -13,10 +13,9 @@ import { filter } from 'rxjs/operators'; import useShallowCompareEffect from 'react-use/lib/useShallowCompareEffect'; import { EuiLoadingChart, EuiProgress } from '@elastic/eui'; import theme from '@elastic/eui/dist/eui_theme_light.json'; -import { IExpressionLoaderParams, ExpressionRenderError } from './types'; +import { IExpressionLoaderParams, ExpressionRenderError, ExpressionRendererEvent } from './types'; import { ExpressionAstExpression, IInterpreterRenderHandlers } from '../common'; import { ExpressionLoader } from './loader'; -import { ExpressionRendererEvent } from './render'; // Accept all options of the runner as props except for the // dom element which is provided by the component itself @@ -58,7 +57,8 @@ const defaultState: State = { error: null, }; -export const ReactExpressionRenderer = ({ +// eslint-disable-next-line import/no-default-export +export default function ReactExpressionRenderer({ className, dataAttrs, padding, @@ -69,7 +69,7 @@ export const ReactExpressionRenderer = ({ reload$, debounce, ...expressionLoaderOptions -}: ReactExpressionRendererProps) => { +}: ReactExpressionRendererProps) { const mountpoint: React.MutableRefObject = useRef(null); const [state, setState] = useState({ ...defaultState }); const hasCustomRenderErrorHandler = !!renderError; @@ -237,4 +237,4 @@ export const ReactExpressionRenderer = ({ />
); -}; +} diff --git a/src/plugins/expressions/public/react_expression_renderer_wrapper.tsx b/src/plugins/expressions/public/react_expression_renderer_wrapper.tsx new file mode 100644 index 0000000000000..45295da0a9ae8 --- /dev/null +++ b/src/plugins/expressions/public/react_expression_renderer_wrapper.tsx @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { lazy, Suspense } from 'react'; +import { EuiLoadingSpinner } from '@elastic/eui'; +import type { ReactExpressionRendererProps } from './react_expression_renderer'; + +const ReactExpressionRendererComponent = lazy(() => import('./react_expression_renderer')); + +export const ReactExpressionRenderer = (props: ReactExpressionRendererProps) => ( + }> + + +); diff --git a/src/plugins/expressions/public/render.test.ts b/src/plugins/expressions/public/render.test.ts index 8d4298785572a..f522ea5fe5971 100644 --- a/src/plugins/expressions/public/render.test.ts +++ b/src/plugins/expressions/public/render.test.ts @@ -53,8 +53,8 @@ const getHandledError = () => { }; describe('render helper function', () => { - it('returns ExpressionRenderHandler instance', () => { - const response = render(element, {}); + it('returns ExpressionRenderHandler instance', async () => { + const response = await render(element, {}); expect(response).toBeInstanceOf(ExpressionRenderHandler); }); }); diff --git a/src/plugins/expressions/public/render.ts b/src/plugins/expressions/public/render.ts index 8635a4033bde5..25bffdca089ee 100644 --- a/src/plugins/expressions/public/render.ts +++ b/src/plugins/expressions/public/render.ts @@ -11,14 +11,14 @@ import { Observable } from 'rxjs'; import { filter } from 'rxjs/operators'; import { isNumber } from 'lodash'; import { SerializableRecord } from '@kbn/utility-types'; -import { ExpressionRenderError, RenderErrorHandlerFnType, IExpressionLoaderParams } from './types'; -import { renderErrorHandler as defaultRenderErrorHandler } from './render_error_handler'; import { - IInterpreterRenderHandlers, - IInterpreterRenderEvent, - IInterpreterRenderUpdateParams, - RenderMode, -} from '../common'; + ExpressionRenderError, + RenderErrorHandlerFnType, + IExpressionLoaderParams, + ExpressionRendererEvent, +} from './types'; +import { renderErrorHandler as defaultRenderErrorHandler } from './render_error_handler'; +import { IInterpreterRenderHandlers, IInterpreterRenderUpdateParams, RenderMode } from '../common'; import { getRenderersRegistry } from './services'; @@ -32,9 +32,6 @@ export interface ExpressionRenderHandlerParams { hasCompatibleActions?: (event: ExpressionRendererEvent) => Promise; } -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export type ExpressionRendererEvent = IInterpreterRenderEvent; - type UpdateValue = IInterpreterRenderUpdateParams; export class ExpressionRenderHandler { @@ -154,12 +151,14 @@ export class ExpressionRenderHandler { }; } -export function render( +export type IExpressionRenderer = ( element: HTMLElement, data: unknown, options?: ExpressionRenderHandlerParams -): ExpressionRenderHandler { +) => Promise; + +export const render: IExpressionRenderer = async (element, data, options) => { const handler = new ExpressionRenderHandler(element, options); handler.render(data as SerializableRecord); return handler; -} +}; diff --git a/src/plugins/expressions/public/types/index.ts b/src/plugins/expressions/public/types/index.ts index ea47403332c74..35bda400756d0 100644 --- a/src/plugins/expressions/public/types/index.ts +++ b/src/plugins/expressions/public/types/index.ts @@ -14,6 +14,7 @@ import { ExpressionValue, ExpressionsService, RenderMode, + IInterpreterRenderEvent, } from '../../common'; import { ExpressionRenderHandlerParams } from '../render'; @@ -75,3 +76,6 @@ export type RenderErrorHandlerFnType = ( error: ExpressionRenderError, handlers: IInterpreterRenderHandlers ) => void; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type ExpressionRendererEvent = IInterpreterRenderEvent; diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts index 5868489934dc5..928cbec9c3747 100644 --- a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts @@ -30,7 +30,7 @@ import { } from '../../../../plugins/embeddable/public'; import { IExpressionLoaderParams, - ExpressionsStart, + ExpressionLoader, ExpressionRenderError, ExpressionAstExpression, } from '../../../../plugins/expressions/public'; @@ -81,8 +81,6 @@ export type VisualizeSavedObjectAttributes = SavedObjectAttributes & { export type VisualizeByValueInput = { attributes: VisualizeSavedObjectAttributes } & VisualizeInput; export type VisualizeByReferenceInput = SavedObjectEmbeddableInput & VisualizeInput; -type ExpressionLoader = InstanceType; - export class VisualizeEmbeddable extends Embeddable implements ReferenceOrValueEmbeddable @@ -302,7 +300,7 @@ export class VisualizeEmbeddable super.render(this.domNode); const expressions = getExpressions(); - this.handler = new expressions.ExpressionLoader(this.domNode, undefined, { + this.handler = await expressions.loader(this.domNode, undefined, { onRenderError: (element: HTMLElement, error: ExpressionRenderError) => { this.onContainerError(error); }, diff --git a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/kibana.json b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/kibana.json index 67316b1d0d7eb..e78f294cde7c9 100644 --- a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/kibana.json +++ b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/kibana.json @@ -9,5 +9,5 @@ "requiredPlugins": ["data", "savedObjects", "kibanaUtils", "expressions"], "server": true, "ui": true, - "requiredBundles": ["inspector"] + "requiredBundles": [] } diff --git a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/app/components/main.tsx b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/app/components/main.tsx index b03451bdebad2..6d132c3acb730 100644 --- a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/app/components/main.tsx +++ b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/app/components/main.tsx @@ -13,10 +13,8 @@ import { first, pluck } from 'rxjs/operators'; import { IInterpreterRenderHandlers, ExpressionValue, - TablesAdapter, } from '../../../../../../../src/plugins/expressions/public'; -import { RequestAdapter } from '../../../../../../../src/plugins/inspector/public'; -import { Adapters, ExpressionRenderHandler } from '../../types'; +import { ExpressionRenderHandler } from '../../types'; import { getExpressions } from '../../services'; declare global { @@ -50,13 +48,9 @@ class Main extends React.Component<{}, State> { initialContext: ExpressionValue = {} ) => { this.setState({ expression }); - const adapters: Adapters = { - requests: new RequestAdapter(), - tables: new TablesAdapter(), - }; + return getExpressions() .execute(expression, context || { type: 'null' }, { - inspectorAdapters: adapters, searchContext: initialContext as any, }) .getData() @@ -70,7 +64,7 @@ class Main extends React.Component<{}, State> { lastRenderHandler.destroy(); } - lastRenderHandler = getExpressions().render(this.chartRef.current!, context, { + lastRenderHandler = await getExpressions().render(this.chartRef.current!, context, { onRenderError: (el: HTMLElement, error: unknown, handler: IInterpreterRenderHandlers) => { this.setState({ expression: 'Render error!\n\n' + JSON.stringify(error), diff --git a/x-pack/plugins/lens/common/expressions/merge_tables/merge_tables.test.ts b/x-pack/plugins/lens/common/expressions/merge_tables/merge_tables.test.ts index c883f6b7cb479..cd4fc5ed945d4 100644 --- a/x-pack/plugins/lens/common/expressions/merge_tables/merge_tables.test.ts +++ b/x-pack/plugins/lens/common/expressions/merge_tables/merge_tables.test.ts @@ -58,6 +58,7 @@ describe('lens_merge_tables', () => { const adapters: DefaultInspectorAdapters = { tables: new TablesAdapter(), requests: {} as never, + expression: {} as never, }; mergeTables.fn(null, { layerIds: ['first', 'second'], tables: [sampleTable1, sampleTable2] }, { inspectorAdapters: adapters, From 3f12a90766a98427ff703334a3f1a8cb0d9f87e8 Mon Sep 17 00:00:00 2001 From: Peter Pisljar Date: Wed, 20 Oct 2021 13:56:58 +0200 Subject: [PATCH 123/204] remove any from filter persistable state (#115128) --- src/plugins/data/common/query/persistable_state.ts | 7 +------ .../public/query/filter_manager/filter_manager.ts | 12 +++--------- src/plugins/data/server/query/query_service.ts | 9 +-------- .../kibana_utils/common/persistable_state/types.ts | 6 +++--- .../header/__snapshots__/index.test.tsx.snap | 1 - 5 files changed, 8 insertions(+), 27 deletions(-) diff --git a/src/plugins/data/common/query/persistable_state.ts b/src/plugins/data/common/query/persistable_state.ts index 367234e9ff4f0..934d481685db4 100644 --- a/src/plugins/data/common/query/persistable_state.ts +++ b/src/plugins/data/common/query/persistable_state.ts @@ -8,7 +8,6 @@ import uuid from 'uuid'; import { Filter } from '@kbn/es-query'; -import type { SerializableRecord } from '@kbn/utility-types'; import { SavedObjectReference } from '../../../../core/types'; export const extract = (filters: Filter[]) => { @@ -51,14 +50,10 @@ export const inject = (filters: Filter[], references: SavedObjectReference[]) => }); }; -export const telemetry = (filters: SerializableRecord, collector: unknown) => { +export const telemetry = (filters: Filter[], collector: unknown) => { return {}; }; -export const migrateToLatest = (filters: Filter[], version: string) => { - return filters; -}; - export const getAllMigrations = () => { return {}; }; diff --git a/src/plugins/data/public/query/filter_manager/filter_manager.ts b/src/plugins/data/public/query/filter_manager/filter_manager.ts index 34af80a483e6b..f076a2c591fb1 100644 --- a/src/plugins/data/public/query/filter_manager/filter_manager.ts +++ b/src/plugins/data/public/query/filter_manager/filter_manager.ts @@ -26,13 +26,12 @@ import { import { PersistableStateService } from '../../../../kibana_utils/common/persistable_state'; import { getAllMigrations, - migrateToLatest, inject, extract, telemetry, } from '../../../common/query/persistable_state'; -export class FilterManager implements PersistableStateService { +export class FilterManager implements PersistableStateService { private filters: Filter[] = []; private updated$: Subject = new Subject(); private fetch$: Subject = new Subject(); @@ -228,16 +227,11 @@ export class FilterManager implements PersistableStateService { }); } - // Filter needs to implement SerializableRecord - public extract = extract as any; + public extract = extract; - // Filter needs to implement SerializableRecord - public inject = inject as any; + public inject = inject; public telemetry = telemetry; - // Filter needs to implement SerializableRecord - public migrateToLatest = migrateToLatest as any; - public getAllMigrations = getAllMigrations; } diff --git a/src/plugins/data/server/query/query_service.ts b/src/plugins/data/server/query/query_service.ts index 9e67f0ab825df..1bf5ff901e90f 100644 --- a/src/plugins/data/server/query/query_service.ts +++ b/src/plugins/data/server/query/query_service.ts @@ -8,13 +8,7 @@ import { CoreSetup, Plugin } from 'kibana/server'; import { querySavedObjectType } from '../saved_objects'; -import { - extract, - inject, - telemetry, - migrateToLatest, - getAllMigrations, -} from '../../common/query/persistable_state'; +import { extract, inject, telemetry, getAllMigrations } from '../../common/query/persistable_state'; export class QueryService implements Plugin { public setup(core: CoreSetup) { @@ -25,7 +19,6 @@ export class QueryService implements Plugin { extract, inject, telemetry, - migrateToLatest, getAllMigrations, }, }; diff --git a/src/plugins/kibana_utils/common/persistable_state/types.ts b/src/plugins/kibana_utils/common/persistable_state/types.ts index 6fea0a3a4eab6..80b791a75758f 100644 --- a/src/plugins/kibana_utils/common/persistable_state/types.ts +++ b/src/plugins/kibana_utils/common/persistable_state/types.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import type { SerializableRecord } from '@kbn/utility-types'; +import type { SerializableRecord, Serializable } from '@kbn/utility-types'; import { SavedObjectReference } from '../../../../core/types'; /** @@ -26,7 +26,7 @@ import { SavedObjectReference } from '../../../../core/types'; * }; * ``` */ -export interface VersionedState { +export interface VersionedState { version: string; state: S; } @@ -116,7 +116,7 @@ export type PersistableStateDefinition

{ +export interface PersistableStateService

{ /** * Function which reports telemetry information. This function is essentially * a "reducer" - it receives the existing "stats" object and returns an diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/header/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/header/__snapshots__/index.test.tsx.snap index 17a9c7efbf301..3e764b34d74c5 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/header/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/header/__snapshots__/index.test.tsx.snap @@ -20,7 +20,6 @@ exports[`Header rendering renders correctly against snapshot 1`] = ` "filters": Array [], "getAllMigrations": [Function], "inject": [Function], - "migrateToLatest": [Function], "telemetry": [Function], "uiSettings": Object { "get": [MockFunction], From 73566ad285f703e216d35f6775d62367e1010900 Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Wed, 20 Oct 2021 14:03:45 +0200 Subject: [PATCH 124/204] [charts] Replace usage of linear xScaleType in barchart [skip-ci] (#114778) --- .../alerting/chart_preview/index.tsx | 2 +- .../field_data_row/action_menu/lens_utils.ts | 2 +- .../boolean_content.tsx | 6 +- .../boolean_content_preview.tsx | 1 + .../field_data_row/column_chart.tsx | 12 ++- .../field_data_row/use_column_chart.test.tsx | 85 +++++++++++-------- .../field_data_row/use_column_chart.tsx | 8 +- .../criterion_preview_chart.tsx | 2 +- .../single_metric_sparkline.tsx | 8 +- .../indexpattern_datasource/field_item.tsx | 6 +- .../components/data_grid/column_chart.tsx | 8 +- .../alerts_histogram.tsx | 11 +-- .../charts/duration_line_series_list.tsx | 6 +- .../common/charts/ping_histogram.tsx | 5 +- .../components/waterfall_chart_fixed_axis.tsx | 2 +- 15 files changed, 91 insertions(+), 73 deletions(-) diff --git a/x-pack/plugins/apm/public/components/alerting/chart_preview/index.tsx b/x-pack/plugins/apm/public/components/alerting/chart_preview/index.tsx index ee6a58b0dbb76..fb1a99db0bf5b 100644 --- a/x-pack/plugins/apm/public/components/alerting/chart_preview/index.tsx +++ b/x-pack/plugins/apm/public/components/alerting/chart_preview/index.tsx @@ -103,7 +103,7 @@ export function ChartPreview({ data={data} id="chart_preview_bar_series" xAccessor="x" - xScaleType={ScaleType.Linear} + xScaleType={ScaleType.Time} yAccessors={['y']} yScaleType={ScaleType.Linear} /> diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/field_data_row/action_menu/lens_utils.ts b/x-pack/plugins/data_visualizer/public/application/common/components/field_data_row/action_menu/lens_utils.ts index 615ba84afb5b7..db04712271587 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/field_data_row/action_menu/lens_utils.ts +++ b/x-pack/plugins/data_visualizer/public/application/common/components/field_data_row/action_menu/lens_utils.ts @@ -153,7 +153,7 @@ export function getKeywordSettings(item: FieldVisConfig) { accessors: ['col2'], layerId: 'layer1', layerType: 'data', - seriesType: 'bar', + seriesType: 'bar_horizontal', xAccessor: 'col1', }; diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/boolean_content.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/boolean_content.tsx index 754d0e470fe40..b56e6ae815645 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/boolean_content.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/boolean_content.tsx @@ -7,7 +7,7 @@ import React, { FC, ReactNode, useMemo } from 'react'; import { EuiBasicTable, EuiSpacer, RIGHT_ALIGNMENT, HorizontalAlignment } from '@elastic/eui'; -import { Axis, BarSeries, Chart, Settings } from '@elastic/charts'; +import { Axis, BarSeries, Chart, Settings, ScaleType } from '@elastic/charts'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; @@ -137,9 +137,9 @@ export const BooleanContent: FC = ({ config }) => { splitSeriesAccessors={['x']} stackAccessors={['x']} xAccessor="x" - xScaleType="ordinal" + xScaleType={ScaleType.Ordinal} yAccessors={['count']} - yScaleType="linear" + yScaleType={ScaleType.Linear} /> diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_row/boolean_content_preview.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_row/boolean_content_preview.tsx index ceb2e6f241682..a44cd2a2e83c9 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_row/boolean_content_preview.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_row/boolean_content_preview.tsx @@ -37,6 +37,7 @@ export const BooleanContentPreview: FC = ({ config }) => { chartData={chartData} columnType={columnType} hideLabel={true} + maxChartColumns={10} /> ); }; diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_row/column_chart.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_row/column_chart.tsx index 453754d4d6bd4..8b0ba08498600 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_row/column_chart.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_row/column_chart.tsx @@ -22,7 +22,7 @@ interface Props { columnType: EuiDataGridColumn; dataTestSubj: string; hideLabel?: boolean; - maxChartColumns?: number; + maxChartColumns: number; } const zeroSize = { bottom: 0, left: 0, right: 0, top: 0 }; @@ -42,8 +42,12 @@ export const ColumnChart: FC = ({ {!isUnsupportedChartData(chartData) && data.length > 0 && ( i)} + theme={{ + chartMargins: zeroSize, + chartPaddings: zeroSize, + crosshair: { band: { visible: false } }, + }} /> = ({ /> { describe('getLegendText()', () => { it('should return the chart legend text for unsupported chart types', () => { - expect(getLegendText(validUnsupportedChartData)).toBe('Chart not supported.'); + expect(getLegendText(validUnsupportedChartData, 20)).toBe('Chart not supported.'); }); it('should return the chart legend text for empty datasets', () => { - expect(getLegendText(validNumericChartData)).toBe('0 documents contain field.'); + expect(getLegendText(validNumericChartData, 20)).toBe('0 documents contain field.'); }); it('should return the chart legend text for boolean chart types', () => { const { getByText } = render( <> - {getLegendText({ - cardinality: 2, - data: [ - { key: 'true', key_as_string: 'true', doc_count: 10 }, - { key: 'false', key_as_string: 'false', doc_count: 20 }, - ], - id: 'the-id', - type: 'boolean', - })} + {getLegendText( + { + cardinality: 2, + data: [ + { key: 'true', key_as_string: 'true', doc_count: 10 }, + { key: 'false', key_as_string: 'false', doc_count: 20 }, + ], + id: 'the-id', + type: 'boolean', + }, + 20 + )} ); expect(getByText('t')).toBeInTheDocument(); expect(getByText('f')).toBeInTheDocument(); }); it('should return the chart legend text for ordinal chart data with less than max categories', () => { - expect(getLegendText({ ...validOrdinalChartData, data: [{ key: 'cat', doc_count: 10 }] })).toBe( - '10 categories' - ); + expect( + getLegendText({ ...validOrdinalChartData, data: [{ key: 'cat', doc_count: 10 }] }, 20) + ).toBe('10 categories'); }); it('should return the chart legend text for ordinal chart data with more than max categories', () => { expect( - getLegendText({ - ...validOrdinalChartData, - cardinality: 30, - data: [{ key: 'cat', doc_count: 10 }], - }) + getLegendText( + { + ...validOrdinalChartData, + cardinality: 30, + data: [{ key: 'cat', doc_count: 10 }], + }, + 20 + ) ).toBe('top 20 of 30 categories'); }); it('should return the chart legend text for numeric datasets', () => { expect( - getLegendText({ - ...validNumericChartData, - data: [{ key: 1, doc_count: 10 }], - stats: [1, 100], - }) + getLegendText( + { + ...validNumericChartData, + data: [{ key: 1, doc_count: 10 }], + stats: [1, 100], + }, + 20 + ) ).toBe('1 - 100'); expect( - getLegendText({ - ...validNumericChartData, - data: [{ key: 1, doc_count: 10 }], - stats: [100, 100], - }) + getLegendText( + { + ...validNumericChartData, + data: [{ key: 1, doc_count: 10 }], + stats: [100, 100], + }, + 20 + ) ).toBe('100'); expect( - getLegendText({ - ...validNumericChartData, - data: [{ key: 1, doc_count: 10 }], - stats: [1.2345, 6.3456], - }) + getLegendText( + { + ...validNumericChartData, + data: [{ key: 1, doc_count: 10 }], + stats: [1.2345, 6.3456], + }, + 20 + ) ).toBe('1.23 - 6.35'); }); }); @@ -167,7 +182,7 @@ describe('getLegendText()', () => { describe('useColumnChart()', () => { it('should return the column chart hook data', () => { const { result } = renderHook(() => - useColumnChart(validNumericChartData, { id: 'the-id', schema: 'numeric' }) + useColumnChart(validNumericChartData, { id: 'the-id', schema: 'numeric' }, 20) ); expect(result.current.data).toStrictEqual([]); diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_row/use_column_chart.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_row/use_column_chart.tsx index 2c0817228655e..60e1595c64ece 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_row/use_column_chart.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_row/use_column_chart.tsx @@ -32,7 +32,6 @@ export const hoveredRow$ = new BehaviorSubject(null); export const BAR_COLOR = euiPaletteColorBlind()[0]; const BAR_COLOR_BLUR = euiPaletteColorBlind({ rotations: 2 })[10]; -const MAX_CHART_COLUMNS = 20; type XScaleType = 'ordinal' | 'time' | 'linear' | undefined; export const getXScaleType = (kbnFieldType: KBN_FIELD_TYPES | undefined): XScaleType => { @@ -76,10 +75,7 @@ export const getFieldType = (schema: EuiDataGridColumn['schema']): KBN_FIELD_TYP }; type LegendText = string | JSX.Element; -export const getLegendText = ( - chartData: ChartData, - maxChartColumns = MAX_CHART_COLUMNS -): LegendText => { +export const getLegendText = (chartData: ChartData, maxChartColumns: number): LegendText => { if (chartData.type === 'unsupported') { return i18n.translate('xpack.dataVisualizer.dataGridChart.histogramNotAvailable', { defaultMessage: 'Chart not supported.', @@ -146,7 +142,7 @@ interface ColumnChart { export const useColumnChart = ( chartData: ChartData, columnType: EuiDataGridColumn, - maxChartColumns?: number + maxChartColumns: number ): ColumnChart => { const fieldType = getFieldType(columnType.schema); diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion_preview_chart.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion_preview_chart.tsx index 0ad6378a22960..c5f5ace2f5470 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion_preview_chart.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion_preview_chart.tsx @@ -224,7 +224,7 @@ const CriterionPreviewChart: React.FC = ({ - + diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx index 9c22ec9d4bb05..854075a94dcaa 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx @@ -27,7 +27,7 @@ import { } from '@elastic/eui'; import { Axis, - BarSeries, + HistogramBarSeries, Chart, niceTimeFormatter, Position, @@ -636,7 +636,7 @@ function FieldItemPopoverContents(props: State & FieldItemProps) { showOverlappingTicks={true} /> - formatter.convert(d)} /> - = ({ hideLabel, maxChartColumns, }) => { - const { data, legendText, xScaleType } = useColumnChart(chartData, columnType, maxChartColumns); + const { data, legendText } = useColumnChart(chartData, columnType, maxChartColumns); return (

@@ -59,8 +59,8 @@ export const ColumnChart: FC = ({ d.datum.color} diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/alerts_histogram.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/alerts_histogram.tsx index d09d33ce5aded..2d3e6dcdca4c5 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/alerts_histogram.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/alerts_histogram.tsx @@ -12,6 +12,7 @@ import { Position, Settings, ChartSizeArray, + ScaleType, } from '@elastic/charts'; import { EuiFlexGroup, EuiFlexItem, EuiProgress } from '@elastic/eui'; import React, { useMemo } from 'react'; @@ -42,7 +43,7 @@ export const AlertsHistogram = React.memo( data, from, legendItems, - legendPosition = 'right', + legendPosition = Position.Right, loading, showLegend, to, @@ -81,14 +82,14 @@ export const AlertsHistogram = React.memo( theme={theme} /> - + - + ( key={`loc-line-${name}`} name={name} xAccessor={0} - xScaleType="time" + xScaleType={ScaleType.Time} yAccessors={[1]} - yScaleType="linear" + yScaleType={ScaleType.Linear} fit={Fit.Linear} tickFormat={(d) => monitorType === 'browser' diff --git a/x-pack/plugins/uptime/public/components/common/charts/ping_histogram.tsx b/x-pack/plugins/uptime/public/components/common/charts/ping_histogram.tsx index aa981071b7ee2..5c4be0e6719f4 100644 --- a/x-pack/plugins/uptime/public/components/common/charts/ping_histogram.tsx +++ b/x-pack/plugins/uptime/public/components/common/charts/ping_histogram.tsx @@ -15,6 +15,7 @@ import { BrushEndListener, XYChartElementEvent, ElementClickListener, + ScaleType, } from '@elastic/charts'; import { EuiTitle, EuiFlexGroup, EuiFlexItem, EuiButton } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -182,9 +183,9 @@ export const PingHistogramComponent: React.FC = ({ splitSeriesAccessors={['type']} timeZone="local" xAccessor="x" - xScaleType="time" + xScaleType={ScaleType.Time} yAccessors={['y']} - yScaleType="linear" + yScaleType={ScaleType.Linear} /> diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_chart_fixed_axis.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_chart_fixed_axis.tsx index 3824b9ae19d0f..c73fce07f9779 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_chart_fixed_axis.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_chart_fixed_axis.tsx @@ -52,7 +52,7 @@ export const WaterfallChartFixedAxis = ({ tickFormat, domain, barStyleAccessor } Date: Wed, 20 Oct 2021 14:19:42 +0200 Subject: [PATCH 125/204] [ftr] update webdriver dependency to 4.0 (#115649) * [ftr] update webdriver dependency to 4.0 * [ftr] cast options on assign Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- package.json | 4 +- test/functional/services/remote/webdriver.ts | 124 +++++++++---------- yarn.lock | 59 ++++----- 3 files changed, 87 insertions(+), 100 deletions(-) diff --git a/package.json b/package.json index f4706b5afe7cd..7c915f2f12228 100644 --- a/package.json +++ b/package.json @@ -598,7 +598,7 @@ "@types/reduce-reducers": "^1.0.0", "@types/redux-actions": "^2.6.1", "@types/seedrandom": ">=2.0.0 <4.0.0", - "@types/selenium-webdriver": "^4.0.9", + "@types/selenium-webdriver": "^4.0.15", "@types/semver": "^7", "@types/set-value": "^2.0.0", "@types/sinon": "^7.0.13", @@ -780,7 +780,7 @@ "rxjs-marbles": "^5.0.6", "sass-loader": "^10.2.0", "sass-resources-loader": "^2.0.1", - "selenium-webdriver": "^4.0.0-alpha.7", + "selenium-webdriver": "^4.0.0", "serve-static": "1.14.1", "shelljs": "^0.8.4", "simple-git": "1.116.0", diff --git a/test/functional/services/remote/webdriver.ts b/test/functional/services/remote/webdriver.ts index 7d629ee817252..82a9f9b7d3f27 100644 --- a/test/functional/services/remote/webdriver.ts +++ b/test/functional/services/remote/webdriver.ts @@ -71,6 +71,60 @@ export interface BrowserConfig { acceptInsecureCerts: boolean; } +function initChromiumOptions(browserType: Browsers, acceptInsecureCerts: boolean) { + const options = browserType === Browsers.Chrome ? new chrome.Options() : new edge.Options(); + + options.addArguments( + // Disables the sandbox for all process types that are normally sandboxed. + 'no-sandbox', + // Launches URL in new browser window. + 'new-window', + // By default, file:// URIs cannot read other file:// URIs. This is an override for developers who need the old behavior for testing. + 'allow-file-access-from-files', + // Use fake device for Media Stream to replace actual camera and microphone. + 'use-fake-device-for-media-stream', + // Bypass the media stream infobar by selecting the default device for media streams (e.g. WebRTC). Works with --use-fake-device-for-media-stream. + 'use-fake-ui-for-media-stream' + ); + + if (process.platform === 'linux') { + // The /dev/shm partition is too small in certain VM environments, causing + // Chrome to fail or crash. Use this flag to work-around this issue + // (a temporary directory will always be used to create anonymous shared memory files). + options.addArguments('disable-dev-shm-usage'); + } + + if (headlessBrowser === '1') { + // Use --disable-gpu to avoid an error from a missing Mesa library, as per + // See: https://chromium.googlesource.com/chromium/src/+/lkgr/headless/README.md + options.headless(); + options.addArguments('disable-gpu'); + } + + if (certValidation === '0') { + options.addArguments('ignore-certificate-errors'); + } + + if (remoteDebug === '1') { + // Visit chrome://inspect in chrome to remotely view/debug + options.headless(); + options.addArguments('disable-gpu', 'remote-debugging-port=9222'); + } + + if (browserBinaryPath) { + options.setChromeBinaryPath(browserBinaryPath); + } + + const prefs = new logging.Preferences(); + prefs.setLevel(logging.Type.BROWSER, logging.Level.ALL); + options.setUserPreferences(chromiumUserPrefs); + options.setLoggingPrefs(prefs); + options.set('unexpectedAlertBehaviour', 'accept'); + options.setAcceptInsecureCerts(acceptInsecureCerts); + + return options; +} + let attemptCounter = 0; let edgePaths: { driverPath: string | undefined; browserPath: string | undefined }; async function attemptToCreateCommand( @@ -86,55 +140,10 @@ async function attemptToCreateCommand( const buildDriverInstance = async () => { switch (browserType) { case 'chrome': { - const chromeOptions = new chrome.Options(); - chromeOptions.addArguments( - // Disables the sandbox for all process types that are normally sandboxed. - 'no-sandbox', - // Launches URL in new browser window. - 'new-window', - // By default, file:// URIs cannot read other file:// URIs. This is an override for developers who need the old behavior for testing. - 'allow-file-access-from-files', - // Use fake device for Media Stream to replace actual camera and microphone. - 'use-fake-device-for-media-stream', - // Bypass the media stream infobar by selecting the default device for media streams (e.g. WebRTC). Works with --use-fake-device-for-media-stream. - 'use-fake-ui-for-media-stream' - ); - - if (process.platform === 'linux') { - // The /dev/shm partition is too small in certain VM environments, causing - // Chrome to fail or crash. Use this flag to work-around this issue - // (a temporary directory will always be used to create anonymous shared memory files). - chromeOptions.addArguments('disable-dev-shm-usage'); - } - - if (headlessBrowser === '1') { - // Use --disable-gpu to avoid an error from a missing Mesa library, as per - // See: https://chromium.googlesource.com/chromium/src/+/lkgr/headless/README.md - chromeOptions.headless(); - chromeOptions.addArguments('disable-gpu'); - } - - if (certValidation === '0') { - chromeOptions.addArguments('ignore-certificate-errors'); - } - - if (remoteDebug === '1') { - // Visit chrome://inspect in chrome to remotely view/debug - chromeOptions.headless(); - chromeOptions.addArguments('disable-gpu', 'remote-debugging-port=9222'); - } - - if (browserBinaryPath) { - chromeOptions.setChromeBinaryPath(browserBinaryPath); - } - - const prefs = new logging.Preferences(); - prefs.setLevel(logging.Type.BROWSER, logging.Level.ALL); - chromeOptions.setUserPreferences(chromiumUserPrefs); - chromeOptions.setLoggingPrefs(prefs); - chromeOptions.set('unexpectedAlertBehaviour', 'accept'); - chromeOptions.setAcceptInsecureCerts(config.acceptInsecureCerts); - + const chromeOptions = initChromiumOptions( + browserType, + config.acceptInsecureCerts + ) as chrome.Options; let session; if (remoteSessionUrl) { session = await new Builder() @@ -169,19 +178,10 @@ async function attemptToCreateCommand( case 'msedge': { if (edgePaths && edgePaths.browserPath && edgePaths.driverPath) { - const edgeOptions = new edge.Options(); - if (headlessBrowser === '1') { - // @ts-ignore internal modules are not typed - edgeOptions.headless(); - } - // @ts-ignore internal modules are not typed - edgeOptions.setEdgeChromium(true); - // @ts-ignore internal modules are not typed - edgeOptions.setBinaryPath(edgePaths.browserPath); - const options = edgeOptions.get('ms:edgeOptions'); - // overriding options to include preferences - Object.assign(options, { prefs: chromiumUserPrefs }); - edgeOptions.set('ms:edgeOptions', options); + const edgeOptions = initChromiumOptions( + browserType, + config.acceptInsecureCerts + ) as edge.Options; const session = await new Builder() .forBrowser('MicrosoftEdge') .setEdgeOptions(edgeOptions) diff --git a/yarn.lock b/yarn.lock index 375e748564283..8d174464d0a92 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6985,10 +6985,10 @@ resolved "https://registry.yarnpkg.com/@types/seedrandom/-/seedrandom-2.4.28.tgz#9ce8fa048c1e8c85cb71d7fe4d704e000226036f" integrity sha512-SMA+fUwULwK7sd/ZJicUztiPs8F1yCPwF3O23Z9uQ32ME5Ha0NmDK9+QTsYE4O2tHXChzXomSWWeIhCnoN1LqA== -"@types/selenium-webdriver@^4.0.9": - version "4.0.9" - resolved "https://registry.yarnpkg.com/@types/selenium-webdriver/-/selenium-webdriver-4.0.9.tgz#12621e55b2ef8f6c98bd17fe23fa720c6cba16bd" - integrity sha512-HopIwBE7GUXsscmt/J0DhnFXLSmO04AfxT6b8HAprknwka7pqEWquWDMXxCjd+NUHK9MkCe1SDKKsMiNmCItbQ== +"@types/selenium-webdriver@^4.0.15": + version "4.0.15" + resolved "https://registry.yarnpkg.com/@types/selenium-webdriver/-/selenium-webdriver-4.0.15.tgz#03012b84155cf6bbae2f64aa9dccf2a84c78c7c8" + integrity sha512-5760PIZkzhPejy3hsKAdCKe5LJygGdxLKOLxmZL9GEUcFlO5OgzM6G2EbdbvOnaw4xvUSa9Uip6Ipwkih12BPA== "@types/semver@^7": version "7.3.4" @@ -18808,10 +18808,10 @@ jsts@^1.6.2: array-includes "^3.1.2" object.assign "^4.1.2" -jszip@^3.2.2: - version "3.3.0" - resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.3.0.tgz#29d72c21a54990fa885b11fc843db320640d5271" - integrity sha512-EJ9k766htB1ZWnsV5ZMDkKLgA+201r/ouFF8R2OigVjVdcm2rurcBrrdXaeqBJbqnUVMko512PYmlncBKE1Huw== +jszip@^3.6.0: + version "3.7.1" + resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.7.1.tgz#bd63401221c15625a1228c556ca8a68da6fda3d9" + integrity sha512-ghL0tz1XG9ZEmRMcEN2vt7xabrDdqHHeykgARpmZ0BiIctWxM47Vt63ZO2dnp4QYt/xJVLLy5Zv1l/xRdh2byg== dependencies: lie "~3.3.0" pako "~1.0.2" @@ -21737,7 +21737,7 @@ os-shim@^0.1.2: resolved "https://registry.yarnpkg.com/os-shim/-/os-shim-0.1.3.tgz#6b62c3791cf7909ea35ed46e17658bb417cb3917" integrity sha1-a2LDeRz3kJ6jXtRuF2WLtBfLORc= -os-tmpdir@^1.0.0, os-tmpdir@~1.0.1, os-tmpdir@~1.0.2: +os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= @@ -25397,13 +25397,6 @@ rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.2, rimraf@^2.6.3: dependencies: glob "^7.1.3" -rimraf@^2.7.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" - integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== - dependencies: - glob "^7.1.3" - rimraf@~2.4.0: version "2.4.5" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.4.5.tgz#ee710ce5d93a8fdb856fb5ea8ff0e2d75934b2da" @@ -25709,14 +25702,15 @@ select-hose@^2.0.0: resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo= -selenium-webdriver@^4.0.0-alpha.7: - version "4.0.0-alpha.7" - resolved "https://registry.yarnpkg.com/selenium-webdriver/-/selenium-webdriver-4.0.0-alpha.7.tgz#e3879d8457fd7ad8e4424094b7dc0540d99e6797" - integrity sha512-D4qnTsyTr91jT8f7MfN+OwY0IlU5+5FmlO5xlgRUV6hDEV8JyYx2NerdTEqDDkNq7RZDYc4VoPALk8l578RBHw== +selenium-webdriver@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/selenium-webdriver/-/selenium-webdriver-4.0.0.tgz#7dc8969facee3be634459e173f557b7e34308e73" + integrity sha512-tOlu6FnTjPq2FKpd153pl8o2cB7H40Rvl/ogiD2sapMv4IDjQqpIxbd+swDJe9UDLdszeh5CDis6lgy4e9UG1w== dependencies: - jszip "^3.2.2" - rimraf "^2.7.1" - tmp "0.0.30" + jszip "^3.6.0" + rimraf "^3.0.2" + tmp "^0.2.1" + ws ">=7.4.6" selfsigned@^1.10.7: version "1.10.8" @@ -27871,13 +27865,6 @@ title-case@^2.1.1: no-case "^2.2.0" upper-case "^1.0.3" -tmp@0.0.30: - version "0.0.30" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.30.tgz#72419d4a8be7d6ce75148fd8b324e593a711c2ed" - integrity sha1-ckGdSovn1s51FI/YsyTlk6cRwu0= - dependencies: - os-tmpdir "~1.0.1" - tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -27885,7 +27872,7 @@ tmp@^0.0.33: dependencies: os-tmpdir "~1.0.2" -tmp@~0.2.1: +tmp@^0.2.1, tmp@~0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== @@ -30289,6 +30276,11 @@ write-pkg@^4.0.0: type-fest "^0.4.1" write-json-file "^3.2.0" +ws@>=7.4.6, ws@^7.4.6: + version "7.5.5" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.5.tgz#8b4bc4af518cfabd0473ae4f99144287b33eb881" + integrity sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w== + ws@^6.1.2, ws@^6.2.1: version "6.2.2" resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e" @@ -30301,11 +30293,6 @@ ws@^7.2.3: resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== -ws@^7.4.6: - version "7.5.5" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.5.tgz#8b4bc4af518cfabd0473ae4f99144287b33eb881" - integrity sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w== - x-is-function@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/x-is-function/-/x-is-function-1.0.4.tgz#5d294dc3d268cbdd062580e0c5df77a391d1fa1e" From 453d4bca3678669ecb6833bd4800d9cbfbd29354 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 20 Oct 2021 15:26:22 +0300 Subject: [PATCH 126/204] [XY] Fixes the formatting on multiple axis (#115552) * [XY] fixes the formatting on multiple axis * Move mock data to its own file --- .../xy/public/config/get_config.test.ts | 44 ++++ .../vis_types/xy/public/config/get_config.ts | 14 +- src/plugins/vis_types/xy/public/mocks.ts | 223 ++++++++++++++++++ 3 files changed, 277 insertions(+), 4 deletions(-) create mode 100644 src/plugins/vis_types/xy/public/config/get_config.test.ts create mode 100644 src/plugins/vis_types/xy/public/mocks.ts diff --git a/src/plugins/vis_types/xy/public/config/get_config.test.ts b/src/plugins/vis_types/xy/public/config/get_config.test.ts new file mode 100644 index 0000000000000..d046ac17c2b27 --- /dev/null +++ b/src/plugins/vis_types/xy/public/config/get_config.test.ts @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { getConfig } from './get_config'; +import { visData, visParamsWithTwoYAxes } from '../mocks'; + +// ToDo: add more tests for all the config properties +describe('getConfig', () => { + it('identifies it as a timeChart if the x axis has a date field', () => { + const config = getConfig(visData, visParamsWithTwoYAxes); + expect(config.isTimeChart).toBe(true); + }); + + it('not adds the current time marker if the param is set to false', () => { + const config = getConfig(visData, visParamsWithTwoYAxes); + expect(config.showCurrentTime).toBe(false); + }); + + it('adds the current time marker if the param is set to false', () => { + const newVisParams = { + ...visParamsWithTwoYAxes, + addTimeMarker: true, + }; + const config = getConfig(visData, newVisParams); + expect(config.showCurrentTime).toBe(true); + }); + + it('enables the histogram mode for a date_histogram', () => { + const config = getConfig(visData, visParamsWithTwoYAxes); + expect(config.enableHistogramMode).toBe(true); + }); + + it('assigns the correct formatter per y axis', () => { + const config = getConfig(visData, visParamsWithTwoYAxes); + expect(config.yAxes.length).toBe(2); + expect(config.yAxes[0].ticks?.formatter).toStrictEqual(config.aspects.y[1].formatter); + expect(config.yAxes[1].ticks?.formatter).toStrictEqual(config.aspects.y[0].formatter); + }); +}); diff --git a/src/plugins/vis_types/xy/public/config/get_config.ts b/src/plugins/vis_types/xy/public/config/get_config.ts index 13c9a6c275f8e..d2a3b9ad2a103 100644 --- a/src/plugins/vis_types/xy/public/config/get_config.ts +++ b/src/plugins/vis_types/xy/public/config/get_config.ts @@ -50,10 +50,16 @@ export function getConfig(table: Datatable, params: VisParams): VisConfig { params.dimensions.x?.aggType === BUCKET_TYPES.DATE_HISTOGRAM ); const tooltip = getTooltip(aspects, params); - const yAxes = params.valueAxes.map((a) => - // uses first y aspect in array for formatting axis - getAxis(a, params.grid, aspects.y[0], params.seriesParams) - ); + const yAxes = params.valueAxes.map((a) => { + // find the correct aspect for each value axis + const aspectsIdx = params.seriesParams.findIndex((s) => s.valueAxis === a.id); + return getAxis( + a, + params.grid, + aspects.y[aspectsIdx > -1 ? aspectsIdx : 0], + params.seriesParams + ); + }); const enableHistogramMode = (params.dimensions.x?.aggType === BUCKET_TYPES.DATE_HISTOGRAM || params.dimensions.x?.aggType === BUCKET_TYPES.HISTOGRAM) && diff --git a/src/plugins/vis_types/xy/public/mocks.ts b/src/plugins/vis_types/xy/public/mocks.ts new file mode 100644 index 0000000000000..bb74035485723 --- /dev/null +++ b/src/plugins/vis_types/xy/public/mocks.ts @@ -0,0 +1,223 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import type { Datatable } from '../../../expressions/public'; +import type { VisParams } from './types'; + +export const visData = { + type: 'datatable', + columns: [ + { + id: 'col-0-2', + name: 'timestamp per 12 hours', + meta: { + type: 'date', + field: 'timestamp', + index: 'kibana_sample_data_logs', + }, + }, + { + id: 'col-1-3', + name: 'Average memory', + meta: { + type: 'number', + field: 'memory', + index: 'kibana_sample_data_logs', + }, + }, + { + id: 'col-2-1', + name: 'Average bytes', + meta: { + type: 'number', + field: 'bytes', + index: 'kibana_sample_data_logs', + }, + }, + ], + rows: [ + { + 'col-0-2': 1632603600000, + 'col-1-3': 27400, + 'col-2-1': 6079.305555555556, + }, + { + 'col-0-2': 1632646800000, + 'col-1-3': 148270, + 'col-2-1': 6164.056818181818, + }, + { + 'col-0-2': 1632690000000, + 'col-1-3': 235280, + 'col-2-1': 6125.469387755102, + }, + { + 'col-0-2': 1632733200000, + 'col-1-3': 206750, + 'col-2-1': 5362.68306010929, + }, + ], +} as Datatable; + +export const visParamsWithTwoYAxes = { + type: 'histogram', + addLegend: true, + addTooltip: true, + legendPosition: 'right', + addTimeMarker: false, + maxLegendLines: 1, + truncateLegend: true, + categoryAxes: [ + { + type: 'category', + id: 'CategoryAxis-1', + show: true, + position: 'bottom', + title: {}, + scale: { + type: 'linear', + }, + labels: { + filter: true, + show: true, + truncate: 100, + }, + }, + ], + labels: { + type: 'label', + show: false, + }, + thresholdLine: { + type: 'threshold_line', + show: false, + value: 10, + width: 1, + style: 'full', + color: '#E7664C', + }, + valueAxes: [ + { + type: 'value', + name: 'LeftAxis-1', + id: 'ValueAxis-1', + show: true, + position: 'left', + axisType: 'value', + title: { + text: 'my custom title', + }, + scale: { + type: 'linear', + mode: 'normal', + scaleType: 'linear', + }, + labels: { + type: 'label', + filter: false, + rotate: 0, + show: true, + truncate: 100, + }, + }, + { + type: 'value', + name: 'RightAxis-1', + id: 'ValueAxis-2', + show: true, + position: 'right', + title: { + text: 'my custom title', + }, + scale: { + type: 'linear', + mode: 'normal', + }, + labels: { + filter: false, + rotate: 0, + show: true, + truncate: 100, + }, + }, + ], + grid: { + categoryLines: false, + }, + seriesParams: [ + { + type: 'histogram', + data: { + label: 'Average memory', + id: '3', + }, + drawLinesBetweenPoints: true, + interpolate: 'linear', + lineWidth: 2, + mode: 'stacked', + show: true, + showCircles: true, + circlesRadius: 3, + seriesParamType: 'histogram', + valueAxis: 'ValueAxis-2', + }, + { + type: 'line', + data: { + label: 'Average bytes', + id: '1', + }, + drawLinesBetweenPoints: true, + interpolate: 'linear', + lineWidth: 2, + mode: 'stacked', + show: true, + showCircles: true, + circlesRadius: 3, + valueAxis: 'ValueAxis-1', + }, + ], + dimensions: { + x: { + type: 'xy_dimension', + label: 'timestamp per 12 hours', + aggType: 'date_histogram', + accessor: 0, + format: { + id: 'date', + params: { + pattern: 'YYYY-MM-DD HH:mm', + }, + }, + params: { + date: true, + }, + }, + y: [ + { + label: 'Average memory', + aggType: 'avg', + params: {}, + accessor: 1, + format: { + id: 'number', + params: {}, + }, + }, + { + label: 'Average bytes', + aggType: 'avg', + params: {}, + accessor: 2, + format: { + id: 'bytes', + params: {}, + }, + }, + ], + }, +} as unknown as VisParams; From d9f80133ad5b7aed5468c7ab634ebb2c5411c4f1 Mon Sep 17 00:00:00 2001 From: Byron Hulcher Date: Wed, 20 Oct 2021 09:23:07 -0400 Subject: [PATCH 127/204] [App Search] Improve visual design of Promoted Documents panel (#115683) --- .../curation/documents/hidden_documents.tsx | 29 ++++---- .../curation/documents/organic_documents.tsx | 47 +++++++------ .../documents/promoted_documents.scss | 7 ++ .../curation/documents/promoted_documents.tsx | 66 ++++++++++-------- .../curation/results/add_result_flyout.tsx | 67 ++++++++++--------- .../curation/results/curation_result.tsx | 21 +++--- .../components/data_panel/data_panel.tsx | 10 ++- 7 files changed, 133 insertions(+), 114 deletions(-) create mode 100644 x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/documents/promoted_documents.scss diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/documents/hidden_documents.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/documents/hidden_documents.tsx index 519808940fa1b..5e6123efa988e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/documents/hidden_documents.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/documents/hidden_documents.tsx @@ -52,19 +52,22 @@ export const HiddenDocuments: React.FC = () => { isLoading={hiddenDocumentsLoading} > {hasDocuments ? ( - documents.map((document, index) => ( - removeHiddenId(document.id), - }, - ]} - /> - )) + + {documents.map((document, index) => ( + + removeHiddenId(document.id), + }, + ]} + /> + + ))} + ) : ( { } > {hasDocuments ? ( - documents.map((document: Result, index) => ( - addHiddenId(document.id.raw), - }, - { - ...PROMOTE_DOCUMENT_ACTION, - onClick: () => addPromotedId(document.id.raw), - }, - ] - } - /> - )) + + {documents.map((document: Result, index) => ( + + addHiddenId(document.id.raw), + }, + { + ...PROMOTE_DOCUMENT_ACTION, + onClick: () => addPromotedId(document.id.raw), + }, + ] + } + /> + + ))} + ) : organicDocumentsLoading ? ( ) : ( diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/documents/promoted_documents.scss b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/documents/promoted_documents.scss new file mode 100644 index 0000000000000..59346b66af607 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/documents/promoted_documents.scss @@ -0,0 +1,7 @@ +.promotedDocuments { + &--results { + border-radius: $euiSizeM; + border: $euiBorderWidthThick solid $euiColorPrimary; + background-color: tintOrShade($euiColorPrimary, 90%, 70%); // Copied from @elastit/eui/src/global_styling/variables/_panels.scss + } +} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/documents/promoted_documents.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/documents/promoted_documents.tsx index c953fb210c03d..ab5bf2876ef0b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/documents/promoted_documents.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/documents/promoted_documents.tsx @@ -31,6 +31,8 @@ import { PROMOTED_DOCUMENTS_TITLE } from '../constants'; import { CurationLogic } from '../curation_logic'; import { AddResultButton, CurationResult, convertToResultFormat } from '../results'; +import './promoted_documents.scss'; + export const PromotedDocuments: React.FC = () => { const { curation, isAutomated, promotedIds, promotedDocumentsLoading } = useValues(CurationLogic); const documents = curation.promoted; @@ -89,36 +91,42 @@ export const PromotedDocuments: React.FC = () => { > {hasDocuments ? ( - - {documents.map((document, index) => ( - - {(provided) => ( - + + {documents.map((document, index) => ( + + removePromotedId(document.id), - }, - ] - } - dragHandleProps={provided.dragHandleProps} - /> - )} - - ))} + draggableId={document.id} + customDragHandle + spacing="none" + isDragDisabled={isAutomated} + > + {(provided) => ( + removePromotedId(document.id), + }, + ] + } + dragHandleProps={provided.dragHandleProps} + /> + )} + + + ))} + ) : ( diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/results/add_result_flyout.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/results/add_result_flyout.tsx index 9d341cc2f5520..4c5ba8f1447fd 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/results/add_result_flyout.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/results/add_result_flyout.tsx @@ -19,6 +19,8 @@ import { EuiSpacer, EuiFieldSearch, EuiEmptyPrompt, + EuiFlexGroup, + EuiFlexItem, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -76,38 +78,41 @@ export const AddResultFlyout: React.FC = () => { {searchResults.length > 0 ? ( - searchResults.map((result) => { - const id = result.id.raw; - const isPromoted = promotedIds.includes(id); - const isHidden = hiddenIds.includes(id); + + {searchResults.map((result, index) => { + const id = result.id.raw; + const isPromoted = promotedIds.includes(id); + const isHidden = hiddenIds.includes(id); - return ( - removeHiddenId(id), - } - : { - ...HIDE_DOCUMENT_ACTION, - onClick: () => addHiddenId(id), - }, - isPromoted - ? { - ...DEMOTE_DOCUMENT_ACTION, - onClick: () => removePromotedId(id), - } - : { - ...PROMOTE_DOCUMENT_ACTION, - onClick: () => addPromotedId(id), - }, - ]} - /> - ); - }) + return ( + + removeHiddenId(id), + } + : { + ...HIDE_DOCUMENT_ACTION, + onClick: () => addHiddenId(id), + }, + isPromoted + ? { + ...DEMOTE_DOCUMENT_ACTION, + onClick: () => removePromotedId(id), + } + : { + ...PROMOTE_DOCUMENT_ACTION, + onClick: () => addPromotedId(id), + }, + ]} + /> + + ); + })} + ) : ( = ({ actions, dragHandleProps, resu } = useValues(EngineLogic); return ( - <> - - - + ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/data_panel/data_panel.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/data_panel/data_panel.tsx index 8dc43d08e5e09..d199d2a6d3edd 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/data_panel/data_panel.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/data_panel/data_panel.tsx @@ -20,12 +20,13 @@ import { EuiTitle, EuiTitleProps, } from '@elastic/eui'; +import { _EuiPanelDivlike } from '@elastic/eui/src/components/panel/panel'; import { LoadingOverlay } from '../../../shared/loading'; import './data_panel.scss'; -interface Props { +type Props = Omit<_EuiPanelDivlike, 'title'> & { title: React.ReactElement; // e.g., h2 tag titleSize?: EuiTitleProps['size']; subtitle?: React.ReactNode; @@ -33,10 +34,9 @@ interface Props { action?: React.ReactNode; responsive?: boolean; filled?: boolean; - hasBorder?: boolean; isLoading?: boolean; className?: string; -} +}; export const DataPanel: React.FC = ({ title, @@ -46,7 +46,6 @@ export const DataPanel: React.FC = ({ action, responsive = false, filled, - hasBorder, isLoading, className, children, @@ -58,12 +57,11 @@ export const DataPanel: React.FC = ({ return ( From 85719272b91ab512f81d56413cd58af045052c5d Mon Sep 17 00:00:00 2001 From: Devon Thomson Date: Wed, 20 Oct 2021 10:02:02 -0400 Subject: [PATCH 128/204] [Reporting / Dashboard] Use Unsaved State in Report URL (#114331) * All dashboard state keys taken from unsaved changes for share --- ...nel_map.ts => convert_dashboard_panels.ts} | 11 +- .../lib/convert_dashboard_state.ts | 2 +- .../load_dashboard_history_location_state.ts | 2 +- .../lib/sync_dashboard_url_state.ts | 2 +- .../application/top_nav/dashboard_top_nav.tsx | 12 +- .../top_nav/show_share_modal.test.tsx | 106 +++++++++++++++++- .../application/top_nav/show_share_modal.tsx | 45 +++++--- src/plugins/dashboard/public/services/core.ts | 1 + 8 files changed, 158 insertions(+), 23 deletions(-) rename src/plugins/dashboard/public/application/lib/{convert_saved_panels_to_panel_map.ts => convert_dashboard_panels.ts} (66%) diff --git a/src/plugins/dashboard/public/application/lib/convert_saved_panels_to_panel_map.ts b/src/plugins/dashboard/public/application/lib/convert_dashboard_panels.ts similarity index 66% rename from src/plugins/dashboard/public/application/lib/convert_saved_panels_to_panel_map.ts rename to src/plugins/dashboard/public/application/lib/convert_dashboard_panels.ts index b91940f45081b..4ff1278d9467e 100644 --- a/src/plugins/dashboard/public/application/lib/convert_saved_panels_to_panel_map.ts +++ b/src/plugins/dashboard/public/application/lib/convert_dashboard_panels.ts @@ -6,7 +6,10 @@ * Side Public License, v 1. */ -import { convertSavedDashboardPanelToPanelState } from '../../../common/embeddable/embeddable_saved_object_converters'; +import { + convertSavedDashboardPanelToPanelState, + convertPanelStateToSavedDashboardPanel, +} from '../../../common/embeddable/embeddable_saved_object_converters'; import type { SavedDashboardPanel, DashboardPanelMap } from '../../types'; export const convertSavedPanelsToPanelMap = (panels?: SavedDashboardPanel[]): DashboardPanelMap => { @@ -16,3 +19,9 @@ export const convertSavedPanelsToPanelMap = (panels?: SavedDashboardPanel[]): Da }); return panelsMap; }; + +export const convertPanelMapToSavedPanels = (panels: DashboardPanelMap, version: string) => { + return Object.values(panels).map((panel) => + convertPanelStateToSavedDashboardPanel(panel, version) + ); +}; diff --git a/src/plugins/dashboard/public/application/lib/convert_dashboard_state.ts b/src/plugins/dashboard/public/application/lib/convert_dashboard_state.ts index 123ef381f25f6..0bd49cccbe5ef 100644 --- a/src/plugins/dashboard/public/application/lib/convert_dashboard_state.ts +++ b/src/plugins/dashboard/public/application/lib/convert_dashboard_state.ts @@ -19,7 +19,7 @@ import { DashboardContainerInput, DashboardBuildContext, } from '../../types'; -import { convertSavedPanelsToPanelMap } from './convert_saved_panels_to_panel_map'; +import { convertSavedPanelsToPanelMap } from './convert_dashboard_panels'; interface SavedObjectToDashboardStateProps { version: string; diff --git a/src/plugins/dashboard/public/application/lib/load_dashboard_history_location_state.ts b/src/plugins/dashboard/public/application/lib/load_dashboard_history_location_state.ts index d20e14cea74b5..ce06ef443d69f 100644 --- a/src/plugins/dashboard/public/application/lib/load_dashboard_history_location_state.ts +++ b/src/plugins/dashboard/public/application/lib/load_dashboard_history_location_state.ts @@ -8,7 +8,7 @@ import { ForwardedDashboardState } from '../../locator'; import { DashboardState } from '../../types'; -import { convertSavedPanelsToPanelMap } from './convert_saved_panels_to_panel_map'; +import { convertSavedPanelsToPanelMap } from './convert_dashboard_panels'; export const loadDashboardHistoryLocationState = ( state?: ForwardedDashboardState diff --git a/src/plugins/dashboard/public/application/lib/sync_dashboard_url_state.ts b/src/plugins/dashboard/public/application/lib/sync_dashboard_url_state.ts index 00b8f8217e25f..d314dc631631d 100644 --- a/src/plugins/dashboard/public/application/lib/sync_dashboard_url_state.ts +++ b/src/plugins/dashboard/public/application/lib/sync_dashboard_url_state.ts @@ -21,7 +21,7 @@ import type { DashboardState, RawDashboardState, } from '../../types'; -import { convertSavedPanelsToPanelMap } from './convert_saved_panels_to_panel_map'; +import { convertSavedPanelsToPanelMap } from './convert_dashboard_panels'; type SyncDashboardUrlStateProps = DashboardBuildContext & { savedDashboard: DashboardSavedObject }; diff --git a/src/plugins/dashboard/public/application/top_nav/dashboard_top_nav.tsx b/src/plugins/dashboard/public/application/top_nav/dashboard_top_nav.tsx index 712c070e17b9f..8391edd30d8ee 100644 --- a/src/plugins/dashboard/public/application/top_nav/dashboard_top_nav.tsx +++ b/src/plugins/dashboard/public/application/top_nav/dashboard_top_nav.tsx @@ -407,16 +407,24 @@ export function DashboardTopNav({ const timeRange = timefilter.getTime(); ShowShareModal({ share, + timeRange, kibanaVersion, anchorElement, dashboardCapabilities, + dashboardSessionStorage, currentDashboardState: currentState, savedDashboard: dashboardAppState.savedDashboard, isDirty: Boolean(dashboardAppState.hasUnsavedChanges), - timeRange, }); }, - [dashboardAppState, dashboardCapabilities, share, kibanaVersion, timefilter] + [ + share, + timefilter, + kibanaVersion, + dashboardAppState, + dashboardCapabilities, + dashboardSessionStorage, + ] ); const dashboardTopNavActions = useMemo(() => { diff --git a/src/plugins/dashboard/public/application/top_nav/show_share_modal.test.tsx b/src/plugins/dashboard/public/application/top_nav/show_share_modal.test.tsx index c684370c92062..184d25081d4fa 100644 --- a/src/plugins/dashboard/public/application/top_nav/show_share_modal.test.tsx +++ b/src/plugins/dashboard/public/application/top_nav/show_share_modal.test.tsx @@ -6,8 +6,13 @@ * Side Public License, v 1. */ -import { Capabilities } from 'src/core/public'; -import { showPublicUrlSwitch } from './show_share_modal'; +import { DashboardState } from '../../types'; +import { DashboardAppLocatorParams } from '../..'; +import { Capabilities } from '../../services/core'; +import { SharePluginStart } from '../../services/share'; +import { stateToRawDashboardState } from '../lib/convert_dashboard_state'; +import { getSavedDashboardMock, makeDefaultServices } from '../test_helpers'; +import { showPublicUrlSwitch, ShowShareModal, ShowShareModalProps } from './show_share_modal'; describe('showPublicUrlSwitch', () => { test('returns false if "dashboard" app is not available', () => { @@ -49,3 +54,100 @@ describe('showPublicUrlSwitch', () => { expect(result).toBe(true); }); }); + +describe('ShowShareModal', () => { + const unsavedStateKeys = ['query', 'filters', 'options', 'savedQuery', 'panels'] as Array< + keyof DashboardAppLocatorParams + >; + + const getPropsAndShare = ( + unsavedState?: Partial + ): { share: SharePluginStart; showModalProps: ShowShareModalProps } => { + const services = makeDefaultServices(); + const share = {} as unknown as SharePluginStart; + share.toggleShareContextMenu = jest.fn(); + services.dashboardSessionStorage.getState = jest.fn().mockReturnValue(unsavedState); + return { + showModalProps: { + share, + isDirty: true, + kibanaVersion: 'testKibanaVersion', + savedDashboard: getSavedDashboardMock(), + anchorElement: document.createElement('div'), + dashboardCapabilities: services.dashboardCapabilities, + currentDashboardState: { panels: {} } as unknown as DashboardState, + dashboardSessionStorage: services.dashboardSessionStorage, + timeRange: { + from: '2021-10-07T00:00:00.000Z', + to: '2021-10-10T00:00:00.000Z', + }, + }, + share, + }; + }; + + it('locatorParams is missing all unsaved state when none is given', () => { + const { share, showModalProps } = getPropsAndShare(); + const toggleShareMenuSpy = jest.spyOn(share, 'toggleShareContextMenu'); + ShowShareModal(showModalProps); + expect(share.toggleShareContextMenu).toHaveBeenCalledTimes(1); + const shareLocatorParams = ( + toggleShareMenuSpy.mock.calls[0][0].sharingData as { + locatorParams: { params: DashboardAppLocatorParams }; + } + ).locatorParams.params; + unsavedStateKeys.forEach((key) => { + expect(shareLocatorParams[key]).toBeUndefined(); + }); + }); + + it('locatorParams unsaved state is properly propagated to locator', () => { + const unsavedDashboardState: DashboardState = { + panels: { + panel_1: { + type: 'panel_type', + gridData: { w: 0, h: 0, x: 0, y: 0, i: '0' }, + panelRefName: 'superPanel', + explicitInput: { + id: 'superPanel', + }, + }, + }, + options: { + hidePanelTitles: true, + useMargins: true, + syncColors: true, + }, + filters: [ + { + meta: { + alias: null, + disabled: false, + negate: false, + }, + query: { query: 'hi' }, + }, + ], + query: { query: 'bye', language: 'kuery' }, + savedQuery: 'amazingSavedQuery', + } as unknown as DashboardState; + const { share, showModalProps } = getPropsAndShare(unsavedDashboardState); + const toggleShareMenuSpy = jest.spyOn(share, 'toggleShareContextMenu'); + ShowShareModal(showModalProps); + expect(share.toggleShareContextMenu).toHaveBeenCalledTimes(1); + const shareLocatorParams = ( + toggleShareMenuSpy.mock.calls[0][0].sharingData as { + locatorParams: { params: DashboardAppLocatorParams }; + } + ).locatorParams.params; + const rawDashboardState = stateToRawDashboardState({ + state: unsavedDashboardState, + version: 'testKibanaVersion', + }); + unsavedStateKeys.forEach((key) => { + expect(shareLocatorParams[key]).toStrictEqual( + (rawDashboardState as Partial)[key] + ); + }); + }); +}); diff --git a/src/plugins/dashboard/public/application/top_nav/show_share_modal.tsx b/src/plugins/dashboard/public/application/top_nav/show_share_modal.tsx index 879afd4b9d305..3bfac12a6b500 100644 --- a/src/plugins/dashboard/public/application/top_nav/show_share_modal.tsx +++ b/src/plugins/dashboard/public/application/top_nav/show_share_modal.tsx @@ -21,18 +21,21 @@ import { SharePluginStart } from '../../services/share'; import { DashboardAppCapabilities, DashboardState } from '../../types'; import { dashboardUrlParams } from '../dashboard_router'; import { stateToRawDashboardState } from '../lib/convert_dashboard_state'; +import { convertPanelMapToSavedPanels } from '../lib/convert_dashboard_panels'; +import { DashboardSessionStorage } from '../lib'; const showFilterBarId = 'showFilterBar'; -interface ShowShareModalProps { +export interface ShowShareModalProps { isDirty: boolean; + timeRange: TimeRange; kibanaVersion: string; share: SharePluginStart; anchorElement: HTMLElement; savedDashboard: DashboardSavedObject; currentDashboardState: DashboardState; dashboardCapabilities: DashboardAppCapabilities; - timeRange: TimeRange; + dashboardSessionStorage: DashboardSessionStorage; } export const showPublicUrlSwitch = (anonymousUserCapabilities: Capabilities) => { @@ -46,12 +49,13 @@ export const showPublicUrlSwitch = (anonymousUserCapabilities: Capabilities) => export function ShowShareModal({ share, isDirty, + timeRange, kibanaVersion, anchorElement, savedDashboard, dashboardCapabilities, currentDashboardState, - timeRange, + dashboardSessionStorage, }: ShowShareModalProps) { const EmbedUrlParamExtension = ({ setParamValue, @@ -110,23 +114,31 @@ export function ShowShareModal({ ); }; - const rawDashboardState = stateToRawDashboardState({ - state: currentDashboardState, - version: kibanaVersion, - }); + let unsavedStateForLocator: Pick< + DashboardAppLocatorParams, + 'options' | 'query' | 'savedQuery' | 'filters' | 'panels' + > = {}; + const unsavedDashboardState = dashboardSessionStorage.getState(savedDashboard.id); + if (unsavedDashboardState) { + unsavedStateForLocator = { + query: unsavedDashboardState.query, + filters: unsavedDashboardState.filters, + options: unsavedDashboardState.options, + savedQuery: unsavedDashboardState.savedQuery, + panels: unsavedDashboardState.panels + ? convertPanelMapToSavedPanels(unsavedDashboardState.panels, kibanaVersion) + : undefined, + }; + } const locatorParams: DashboardAppLocatorParams = { dashboardId: savedDashboard.id, - filters: rawDashboardState.filters, preserveSavedFilters: true, - query: rawDashboardState.query, - savedQuery: rawDashboardState.savedQuery, + refreshInterval: undefined, // We don't share refresh interval externally + viewMode: ViewMode.VIEW, // For share locators we always load the dashboard in view mode useHash: false, - panels: rawDashboardState.panels, timeRange, - viewMode: ViewMode.VIEW, // For share locators we always load the dashboard in view mode - refreshInterval: undefined, // We don't share refresh interval externally - options: rawDashboardState.options, + ...unsavedStateForLocator, }; share.toggleShareContextMenu({ @@ -136,7 +148,10 @@ export function ShowShareModal({ allowShortUrl: dashboardCapabilities.createShortUrl, shareableUrl: setStateToKbnUrl( '_a', - rawDashboardState, + stateToRawDashboardState({ + state: currentDashboardState, + version: kibanaVersion, + }), { useHash: false, storeInHashQuery: true }, unhashUrl(window.location.href) ), diff --git a/src/plugins/dashboard/public/services/core.ts b/src/plugins/dashboard/public/services/core.ts index 75461729841e9..e805c6cd706a9 100644 --- a/src/plugins/dashboard/public/services/core.ts +++ b/src/plugins/dashboard/public/services/core.ts @@ -9,6 +9,7 @@ export { AppMountParameters, CoreSetup, + Capabilities, PluginInitializerContext, ScopedHistory, NotificationsStart, From 7e593a05a2d58311bbee83e916720719bf9bd06a Mon Sep 17 00:00:00 2001 From: Bryan Clement Date: Wed, 20 Oct 2021 07:09:08 -0700 Subject: [PATCH 129/204] [Osquery] Cypress automation for osquery manager integration (#108759) --- x-pack/plugins/osquery/cypress/.gitignore | 2 + x-pack/plugins/osquery/cypress/README.md | 12 +- .../integration/osquery_manager.spec.ts | 14 +- .../osquery/cypress/screens/integrations.ts | 1 + .../osquery/cypress/screens/live_query.ts | 11 + .../osquery/cypress/tasks/integrations.ts | 22 +- .../osquery/cypress/tasks/live_query.ts | 25 +++ .../osquery/cypress/tasks/navigation.ts | 10 +- .../public/live_queries/form/index.tsx | 1 + x-pack/test/osquery_cypress/agent.ts | 126 +++++++++++ .../test/osquery_cypress/artifact_manager.ts | 125 +++++++++++ x-pack/test/osquery_cypress/fleet_server.ts | 65 ++++++ x-pack/test/osquery_cypress/fleet_server.yml | 17 ++ .../test/osquery_cypress/resource_manager.ts | 24 ++ x-pack/test/osquery_cypress/runner.ts | 205 +++++++++++++----- x-pack/test/osquery_cypress/users.ts | 101 +++++++++ 16 files changed, 679 insertions(+), 82 deletions(-) create mode 100644 x-pack/plugins/osquery/cypress/.gitignore create mode 100644 x-pack/plugins/osquery/cypress/screens/live_query.ts create mode 100644 x-pack/plugins/osquery/cypress/tasks/live_query.ts create mode 100644 x-pack/test/osquery_cypress/agent.ts create mode 100644 x-pack/test/osquery_cypress/artifact_manager.ts create mode 100644 x-pack/test/osquery_cypress/fleet_server.ts create mode 100644 x-pack/test/osquery_cypress/fleet_server.yml create mode 100644 x-pack/test/osquery_cypress/resource_manager.ts create mode 100644 x-pack/test/osquery_cypress/users.ts diff --git a/x-pack/plugins/osquery/cypress/.gitignore b/x-pack/plugins/osquery/cypress/.gitignore new file mode 100644 index 0000000000000..adaba54810395 --- /dev/null +++ b/x-pack/plugins/osquery/cypress/.gitignore @@ -0,0 +1,2 @@ +videos +screenshots diff --git a/x-pack/plugins/osquery/cypress/README.md b/x-pack/plugins/osquery/cypress/README.md index 0df311ebc0a05..72d558b6e5980 100644 --- a/x-pack/plugins/osquery/cypress/README.md +++ b/x-pack/plugins/osquery/cypress/README.md @@ -20,7 +20,7 @@ A headless browser is a browser simulation program that does not have a user int #### FTR (CI) -This is the configuration used by CI. It uses the FTR to spawn both a Kibana instance (http://localhost:5620) and an Elasticsearch instance (http://localhost:9220) with a preloaded minimum set of data (see preceding "Test data" section), and then executes cypress against this stack. You can find this configuration in `x-pack/test/security_solution_cypress` +This is the configuration used by CI. It uses the FTR to spawn both a Kibana instance (http://localhost:5620) and an Elasticsearch instance (http://localhost:9220) with a preloaded minimum set of data (see preceding "Test data" section), and then executes cypress against this stack. You can find this configuration in `x-pack/test/osquery_cypress` ### Test Execution: Examples @@ -36,7 +36,7 @@ yarn kbn bootstrap node scripts/build_kibana_platform_plugins # launch the cypress test runner -cd x-pack/plugins/security_solution +cd x-pack/plugins/osquery yarn cypress:run-as-ci ``` #### FTR + Interactive @@ -51,7 +51,7 @@ yarn kbn bootstrap node scripts/build_kibana_platform_plugins # launch the cypress test runner -cd x-pack/plugins/security_solution +cd x-pack/plugins/osquery yarn cypress:open-as-ci ``` @@ -98,16 +98,16 @@ We use es_archiver to manage the data that our Cypress tests need. 1. Set up a clean instance of kibana and elasticsearch (if this is not possible, try to clean/minimize the data that you are going to archive). 2. With the kibana and elasticsearch instance up and running, create the data that you need for your test. -3. When you are sure that you have all the data you need run the following command from: `x-pack/plugins/security_solution` +3. When you are sure that you have all the data you need run the following command from: `x-pack/plugins/osquery` ```sh -node ../../../scripts/es_archiver save --dir ../../test/security_solution_cypress/es_archives --config ../../../test/functional/config.js --es-url http://:@: +node ../../../scripts/es_archiver save --dir ../../test/osquery_cypress/es_archives --config ../../../test/functional/config.js --es-url http://:@: ``` Example: ```sh -node ../../../scripts/es_archiver save custom_rules ".kibana",".siem-signal*" --dir ../../test/security_solution_cypress/es_archives --config ../../../test/functional/config.js --es-url http://elastic:changeme@localhost:9220 +node ../../../scripts/es_archiver save custom_rules ".kibana",".siem-signal*" --dir ../../test/osquery_cypress/es_archives --config ../../../test/functional/config.js --es-url http://elastic:changeme@localhost:9220 ``` Note that the command will create the folder if it does not exist. diff --git a/x-pack/plugins/osquery/cypress/integration/osquery_manager.spec.ts b/x-pack/plugins/osquery/cypress/integration/osquery_manager.spec.ts index 0babfd2f10a8e..367de59b3e1fc 100644 --- a/x-pack/plugins/osquery/cypress/integration/osquery_manager.spec.ts +++ b/x-pack/plugins/osquery/cypress/integration/osquery_manager.spec.ts @@ -8,13 +8,19 @@ import { HEADER } from '../screens/osquery'; import { OSQUERY_NAVIGATION_LINK } from '../screens/navigation'; -import { INTEGRATIONS, OSQUERY, openNavigationFlyout, navigateTo } from '../tasks/navigation'; +import { OSQUERY, NEW_LIVE_QUERY, openNavigationFlyout, navigateTo } from '../tasks/navigation'; import { addIntegration } from '../tasks/integrations'; +import { checkResults, inputQuery, selectAllAgents, submitQuery } from '../tasks/live_query'; describe('Osquery Manager', () => { - before(() => { - navigateTo(INTEGRATIONS); - addIntegration('Osquery Manager'); + before(() => addIntegration(Cypress.env('OSQUERY_POLICY'))); + + it('Runs live queries', () => { + navigateTo(NEW_LIVE_QUERY); + selectAllAgents(); + inputQuery(); + submitQuery(); + checkResults(); }); it('Displays Osquery on the navigation flyout once installed ', () => { diff --git a/x-pack/plugins/osquery/cypress/screens/integrations.ts b/x-pack/plugins/osquery/cypress/screens/integrations.ts index 0b29e857f46ee..6a2951c85bf74 100644 --- a/x-pack/plugins/osquery/cypress/screens/integrations.ts +++ b/x-pack/plugins/osquery/cypress/screens/integrations.ts @@ -8,3 +8,4 @@ export const ADD_POLICY_BTN = '[data-test-subj="addIntegrationPolicyButton"]'; export const CREATE_PACKAGE_POLICY_SAVE_BTN = '[data-test-subj="createPackagePolicySaveButton"]'; export const INTEGRATIONS_CARD = '.euiCard__titleAnchor'; +export const SAVE_PACKAGE_CONFIRM = '[data-test-subj=confirmModalConfirmButton]'; diff --git a/x-pack/plugins/osquery/cypress/screens/live_query.ts b/x-pack/plugins/osquery/cypress/screens/live_query.ts new file mode 100644 index 0000000000000..1a521fe1cd651 --- /dev/null +++ b/x-pack/plugins/osquery/cypress/screens/live_query.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const AGENT_FIELD = '[data-test-subj="comboBoxInput"]'; +export const ALL_AGENTS_OPTION = '[title="All agents"]'; +export const LIVE_QUERY_EDITOR = '#osquery_editor'; +export const SUBMIT_BUTTON = '#submit-button'; diff --git a/x-pack/plugins/osquery/cypress/tasks/integrations.ts b/x-pack/plugins/osquery/cypress/tasks/integrations.ts index f85ef56550af5..4de42ffa95bb5 100644 --- a/x-pack/plugins/osquery/cypress/tasks/integrations.ts +++ b/x-pack/plugins/osquery/cypress/tasks/integrations.ts @@ -5,16 +5,18 @@ * 2.0. */ -import { - ADD_POLICY_BTN, - CREATE_PACKAGE_POLICY_SAVE_BTN, - INTEGRATIONS_CARD, -} from '../screens/integrations'; +import { CREATE_PACKAGE_POLICY_SAVE_BTN, SAVE_PACKAGE_CONFIRM } from '../screens/integrations'; -export const addIntegration = (integration: string) => { - cy.get(INTEGRATIONS_CARD).contains(integration).click(); - cy.get(ADD_POLICY_BTN).click(); +import { navigateTo, OSQUERY_INTEGRATION_PAGE } from './navigation'; + +// TODO: allow adding integration version strings to this +export const addIntegration = (policyId: string) => { + navigateTo(OSQUERY_INTEGRATION_PAGE, { qs: { policyId } }); cy.get(CREATE_PACKAGE_POLICY_SAVE_BTN).click(); - cy.get(CREATE_PACKAGE_POLICY_SAVE_BTN).should('not.exist'); - cy.reload(); + cy.get(SAVE_PACKAGE_CONFIRM).click(); + // XXX: there is a race condition between the test going to the ui powered by the agent, and the agent having the integration ready to go + // so we wait. + // TODO: actually make this wait til the agent has been updated with the proper integration + cy.wait(5000); + return cy.reload(); }; diff --git a/x-pack/plugins/osquery/cypress/tasks/live_query.ts b/x-pack/plugins/osquery/cypress/tasks/live_query.ts new file mode 100644 index 0000000000000..c2b97c885cad6 --- /dev/null +++ b/x-pack/plugins/osquery/cypress/tasks/live_query.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + AGENT_FIELD, + ALL_AGENTS_OPTION, + LIVE_QUERY_EDITOR, + SUBMIT_BUTTON, +} from '../screens/live_query'; + +export const selectAllAgents = () => { + cy.get(AGENT_FIELD).first().click(); + return cy.get(ALL_AGENTS_OPTION).contains('All agents').click(); +}; + +export const inputQuery = () => cy.get(LIVE_QUERY_EDITOR).type('select * from processes;'); + +export const submitQuery = () => cy.get(SUBMIT_BUTTON).contains('Submit').click(); + +export const checkResults = () => + cy.get('[data-test-subj="dataGridRowCell"]').should('have.lengthOf.above', 0); diff --git a/x-pack/plugins/osquery/cypress/tasks/navigation.ts b/x-pack/plugins/osquery/cypress/tasks/navigation.ts index 63d6b205b433b..7528f318a2fa4 100644 --- a/x-pack/plugins/osquery/cypress/tasks/navigation.ts +++ b/x-pack/plugins/osquery/cypress/tasks/navigation.ts @@ -7,11 +7,13 @@ import { TOGGLE_NAVIGATION_BTN } from '../screens/navigation'; -export const INTEGRATIONS = 'app/integrations#/'; export const OSQUERY = 'app/osquery/live_queries'; - -export const navigateTo = (page: string) => { - cy.visit(page); +export const NEW_LIVE_QUERY = 'app/osquery/live_queries/new'; +export const OSQUERY_INTEGRATION_PAGE = '/app/fleet/integrations/osquery_manager/add-integration'; +export const navigateTo = (page: string, opts?: Partial) => { + cy.visit(page, opts); + // There's a security warning toast that seemingly makes ui elements in the bottom right unavailable, so we close it + return cy.get('[data-test-subj="toastCloseButton"]').click(); }; export const openNavigationFlyout = () => { diff --git a/x-pack/plugins/osquery/public/live_queries/form/index.tsx b/x-pack/plugins/osquery/public/live_queries/form/index.tsx index 86323b7ab4315..6d13c76d9d592 100644 --- a/x-pack/plugins/osquery/public/live_queries/form/index.tsx +++ b/x-pack/plugins/osquery/public/live_queries/form/index.tsx @@ -328,6 +328,7 @@ const LiveQueryFormComponent: React.FC = ({ )} diff --git a/x-pack/test/osquery_cypress/agent.ts b/x-pack/test/osquery_cypress/agent.ts new file mode 100644 index 0000000000000..802a7caa66d5f --- /dev/null +++ b/x-pack/test/osquery_cypress/agent.ts @@ -0,0 +1,126 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ToolingLog } from '@kbn/dev-utils'; +import axios, { AxiosRequestConfig } from 'axios'; +import { copyFile } from 'fs/promises'; +import { ChildProcess, execFileSync, spawn } from 'child_process'; +import { resolve } from 'path'; +import { unlinkSync } from 'fs'; +import { Manager } from './resource_manager'; + +interface AgentManagerParams { + user: string; + password: string; + kibanaUrl: string; + esHost: string; +} + +export class AgentManager extends Manager { + private directoryPath: string; + private params: AgentManagerParams; + private log: ToolingLog; + private agentProcess?: ChildProcess; + private requestOptions: AxiosRequestConfig; + constructor(directoryPath: string, params: AgentManagerParams, log: ToolingLog) { + super(); + // TODO: check if the file exists + this.directoryPath = directoryPath; + this.log = log; + this.params = params; + this.requestOptions = { + headers: { + 'kbn-xsrf': 'kibana', + }, + auth: { + username: this.params.user, + password: this.params.password, + }, + }; + } + + public getBinaryPath() { + return resolve(this.directoryPath, 'elastic-agent'); + } + + public async setup() { + this.log.info('Running agent preconfig'); + await axios.post(`${this.params.kibanaUrl}/api/fleet/agents/setup`, {}, this.requestOptions); + + this.log.info('Updating the default agent output'); + const { + data: { + items: [defaultOutput], + }, + } = await axios.get(this.params.kibanaUrl + '/api/fleet/outputs', this.requestOptions); + + await axios.put( + `${this.params.kibanaUrl}/api/fleet/outputs/${defaultOutput.id}`, + { hosts: [this.params.esHost] }, + this.requestOptions + ); + + this.log.info('Getting agent enrollment key'); + const { data: apiKeys } = await axios.get( + this.params.kibanaUrl + '/api/fleet/enrollment-api-keys', + this.requestOptions + ); + const policy = apiKeys.list[1]; + + this.log.info('Enrolling the agent'); + const args = [ + 'enroll', + '--insecure', + '-f', + // TODO: parse the host/port out of the logs for the fleet server + '--url=http://localhost:8220', + `--enrollment-token=${policy.api_key}`, + ]; + const agentBinPath = this.getBinaryPath(); + execFileSync(agentBinPath, args, { stdio: 'inherit' }); + + // Copy the config file + const configPath = resolve(__dirname, this.directoryPath, 'elastic-agent.yml'); + this.log.info(`Copying agent config from ${configPath}`); + await copyFile(configPath, resolve('.', 'elastic-agent.yml')); + + this.log.info('Running the agent'); + this.agentProcess = spawn(agentBinPath, ['run', '-v'], { stdio: 'inherit' }); + + // Wait til we see the agent is online + let done = false; + let retries = 0; + while (!done) { + await new Promise((r) => setTimeout(r, 5000)); + const { data: agents } = await axios.get( + `${this.params.kibanaUrl}/api/fleet/agents`, + this.requestOptions + ); + done = agents.list[0]?.status === 'online'; + if (++retries > 12) { + this.log.error('Giving up on enrolling the agent after a minute'); + throw new Error('Agent timed out while coming online'); + } + } + return { policyId: policy.policy_id as string }; + } + + protected _cleanup() { + this.log.info('Cleaning up the agent process'); + if (this.agentProcess) { + if (!this.agentProcess.kill(9)) { + this.log.warning('Unable to kill agent process'); + } + + this.agentProcess.on('close', () => { + this.log.info('Agent process closed'); + }); + delete this.agentProcess; + } + unlinkSync(resolve('.', 'elastic-agent.yml')); + } +} diff --git a/x-pack/test/osquery_cypress/artifact_manager.ts b/x-pack/test/osquery_cypress/artifact_manager.ts new file mode 100644 index 0000000000000..89d6f34987007 --- /dev/null +++ b/x-pack/test/osquery_cypress/artifact_manager.ts @@ -0,0 +1,125 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import axios, { AxiosResponse } from 'axios'; +import { get } from 'lodash'; +import { execSync } from 'child_process'; +import { writeFileSync, unlinkSync, rmdirSync } from 'fs'; +import { resolve } from 'path'; +import { ToolingLog } from '@kbn/dev-utils'; +import { Manager } from './resource_manager'; + +const archMap: { [key: string]: string } = { + x64: 'x86_64', +}; + +type ArtifactName = 'elastic-agent' | 'fleet-server'; + +async function getArtifact( + artifact: string, + urlExtractor: (data: AxiosResponse, filename: string) => string, + log: ToolingLog, + version: string +) { + log.info(`Fetching ${version} of ${artifact}`); + const agents = await axios( + `https://artifacts-api.elastic.co/v1/versions/${version}/builds/latest` + ); + const arch = archMap[process.arch] ?? process.arch; + const dirName = `${artifact}-${version}-${process.platform}-${arch}`; + const filename = dirName + '.tar.gz'; + const url = urlExtractor(agents.data, filename); + if (!url) { + log.error(`Could not find url for ${artifact}: ${url}`); + throw new Error(`Unable to fetch ${artifact}`); + } + log.info(`Fetching ${filename} from ${url}`); + const agent = await axios(url as string, { responseType: 'arraybuffer' }); + writeFileSync(filename, agent.data); + execSync(`tar xvf ${filename}`); + return resolve(filename); +} + +// There has to be a better way to represent partial function application +type ArtifactFetcher = ( + log: Parameters[2], + version: Parameters[3] +) => ReturnType; +type ArtifactFetchers = { + [artifactName in ArtifactName]: ArtifactFetcher; +}; + +const fetchers: ArtifactFetchers = { + 'elastic-agent': getArtifact.bind(null, 'elastic-agent', (data, filename) => + get(data, ['build', 'projects', 'beats', 'packages', filename, 'url']) + ), + 'fleet-server': getArtifact.bind(null, 'fleet-server', (data, filename) => + get(data, ['build', 'projects', 'fleet-server', 'packages', filename, 'url']) + ), +}; + +export type FetchArtifactsParams = { + [artifactName in ArtifactName]?: string; +}; + +type ArtifactPaths = FetchArtifactsParams; +export class ArtifactManager extends Manager { + private artifacts: ArtifactPaths; + private versions: FetchArtifactsParams; + private log: ToolingLog; + + constructor(versions: FetchArtifactsParams, log: ToolingLog) { + super(); + this.versions = versions; + this.log = log; + this.artifacts = {}; + } + + public fetchArtifacts = async () => { + this.log.info('Fetching artifacts'); + await Promise.all( + Object.keys(this.versions).map(async (name: string) => { + const artifactName = name as ArtifactName; + const version = this.versions[artifactName]; + if (!version) { + this.log.warning(`No version is specified for ${artifactName}, skipping`); + return; + } + const fetcher = fetchers[artifactName]; + if (!fetcher) { + this.log.warning(`No fetcher is defined for ${artifactName}, skipping`); + } + + this.artifacts[artifactName] = await fetcher(this.log, version); + }) + ); + }; + + public getArtifactDirectory(artifactName: string) { + const file = this.artifacts[artifactName as ArtifactName]; + // this will break if the tarball name diverges from the directory that gets untarred + if (!file) { + throw new Error(`Unknown artifact ${artifactName}, unable to retreive directory`); + } + return file.replace('.tar.gz', ''); + } + + protected _cleanup() { + this.log.info('Cleaning up artifacts'); + if (this.artifacts) { + for (const artifactName of Object.keys(this.artifacts)) { + const file = this.artifacts[artifactName as ArtifactName]; + if (!file) { + this.log.warning(`Unknown artifact ${artifactName} encountered during cleanup, skipping`); + continue; + } + unlinkSync(file); + rmdirSync(this.getArtifactDirectory(artifactName), { recursive: true }); + } + } + } +} diff --git a/x-pack/test/osquery_cypress/fleet_server.ts b/x-pack/test/osquery_cypress/fleet_server.ts new file mode 100644 index 0000000000000..3c520233bc9b0 --- /dev/null +++ b/x-pack/test/osquery_cypress/fleet_server.ts @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ChildProcess, spawn } from 'child_process'; +import { copyFile } from 'fs/promises'; +import { unlinkSync } from 'fs'; +import { resolve } from 'path'; +import { ToolingLog } from '@kbn/dev-utils'; +import { Manager } from './resource_manager'; +export interface ElasticsearchConfig { + esHost: string; + user: string; + password: string; +} + +export class FleetManager extends Manager { + private directoryPath: string; + private fleetProcess?: ChildProcess; + private esConfig: ElasticsearchConfig; + private log: ToolingLog; + constructor(directoryPath: string, esConfig: ElasticsearchConfig, log: ToolingLog) { + super(); + // TODO: check if the file exists + this.esConfig = esConfig; + this.directoryPath = directoryPath; + this.log = log; + } + public async setup(): Promise { + this.log.info('Setting fleet up'); + await copyFile(resolve(__dirname, 'fleet_server.yml'), resolve('.', 'fleet-server.yml')); + return new Promise((res, rej) => { + const env = { + ELASTICSEARCH_HOSTS: this.esConfig.esHost, + ELASTICSEARCH_USERNAME: this.esConfig.user, + ELASTICSEARCH_PASSWORD: this.esConfig.password, + }; + const file = resolve(this.directoryPath, 'fleet-server'); + // TODO: handle logging properly + this.fleetProcess = spawn(file, [], { stdio: 'inherit', env }); + this.fleetProcess.on('error', rej); + // TODO: actually wait for the fleet server to start listening + setTimeout(res, 15000); + }); + } + + protected _cleanup() { + this.log.info('Removing old fleet config'); + if (this.fleetProcess) { + this.log.info('Closing fleet process'); + if (!this.fleetProcess.kill(9)) { + this.log.warning('Unable to kill fleet server process'); + } + + this.fleetProcess.on('close', () => { + this.log.info('Fleet server process closed'); + }); + delete this.fleetProcess; + } + unlinkSync(resolve('.', 'fleet-server.yml')); + } +} diff --git a/x-pack/test/osquery_cypress/fleet_server.yml b/x-pack/test/osquery_cypress/fleet_server.yml new file mode 100644 index 0000000000000..70ac62b018a25 --- /dev/null +++ b/x-pack/test/osquery_cypress/fleet_server.yml @@ -0,0 +1,17 @@ +# mostly a stub config +output: + elasticsearch: + hosts: '${ELASTICSEARCH_HOSTS:localhost:9220}' + username: '${ELASTICSEARCH_USERNAME:elastic}' + password: '${ELASTICSEARCH_PASSWORD:changeme}' + +fleet: + agent: + id: 1e4954ce-af37-4731-9f4a-407b08e69e42 + logging: + level: '${LOG_LEVEL:DEBUG}' + +logging: + to_stderr: true + +http.enabled: true diff --git a/x-pack/test/osquery_cypress/resource_manager.ts b/x-pack/test/osquery_cypress/resource_manager.ts new file mode 100644 index 0000000000000..e892021155417 --- /dev/null +++ b/x-pack/test/osquery_cypress/resource_manager.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +const CLEANUP_EVENTS = ['SIGINT', 'exit', 'uncaughtException', 'unhandledRejection']; +export class Manager { + private cleaned = false; + constructor() { + const cleanup = () => this.cleanup(); + CLEANUP_EVENTS.forEach((ev) => process.on(ev, cleanup)); + } + // This must be a synchronous method because it is used in the unhandledException and exit event handlers + public cleanup() { + // Since this can be called multiple places we proxy it with some protection + if (this._cleanup && !this.cleaned) { + this.cleaned = true; + this._cleanup(); + } + } + protected _cleanup?(): void; +} diff --git a/x-pack/test/osquery_cypress/runner.ts b/x-pack/test/osquery_cypress/runner.ts index 32c84af5faf76..bd0a97ad5feac 100644 --- a/x-pack/test/osquery_cypress/runner.ts +++ b/x-pack/test/osquery_cypress/runner.ts @@ -12,70 +12,159 @@ import { withProcRunner } from '@kbn/dev-utils'; import { FtrProviderContext } from './ftr_provider_context'; -export async function OsqueryCypressCliTestRunner({ getService }: FtrProviderContext) { +import { ArtifactManager, FetchArtifactsParams } from './artifact_manager'; +import { setupUsers } from './users'; +import { AgentManager } from './agent'; +import { FleetManager } from './fleet_server'; + +interface SetupParams { + artifacts: FetchArtifactsParams; +} + +async function withFleetAgent( + { getService }: FtrProviderContext, + params: SetupParams, + runner: (runnerEnv: Record) => Promise +) { const log = getService('log'); const config = getService('config'); - await withProcRunner(log, async (procs) => { - await procs.run('cypress', { - cmd: 'yarn', - args: ['cypress:run'], - cwd: resolve(__dirname, '../../plugins/osquery'), - env: { - FORCE_COLOR: '1', - // eslint-disable-next-line @typescript-eslint/naming-convention - CYPRESS_baseUrl: Url.format(config.get('servers.kibana')), - // eslint-disable-next-line @typescript-eslint/naming-convention - CYPRESS_protocol: config.get('servers.kibana.protocol'), - // eslint-disable-next-line @typescript-eslint/naming-convention - CYPRESS_hostname: config.get('servers.kibana.hostname'), - // eslint-disable-next-line @typescript-eslint/naming-convention - CYPRESS_configport: config.get('servers.kibana.port'), - CYPRESS_ELASTICSEARCH_URL: Url.format(config.get('servers.elasticsearch')), - CYPRESS_ELASTICSEARCH_USERNAME: config.get('servers.elasticsearch.username'), - CYPRESS_ELASTICSEARCH_PASSWORD: config.get('servers.elasticsearch.password'), - CYPRESS_KIBANA_URL: Url.format({ - protocol: config.get('servers.kibana.protocol'), - hostname: config.get('servers.kibana.hostname'), - port: config.get('servers.kibana.port'), - }), - ...process.env, - }, - wait: true, - }); + const artifactManager = new ArtifactManager(params.artifacts, log); + await artifactManager.fetchArtifacts(); + + const esHost = Url.format(config.get('servers.elasticsearch')); + const esConfig = { + user: config.get('servers.elasticsearch.username'), + password: config.get('servers.elasticsearch.password'), + esHost, + }; + const fleetManager = new FleetManager( + artifactManager.getArtifactDirectory('fleet-server'), + esConfig, + log + ); + + const agentManager = new AgentManager( + artifactManager.getArtifactDirectory('elastic-agent'), + { + ...esConfig, + kibanaUrl: Url.format({ + protocol: config.get('servers.kibana.protocol'), + hostname: config.get('servers.kibana.hostname'), + port: config.get('servers.kibana.port'), + }), + }, + log + ); + + // Since the managers will create uncaughtException event handlers we need to exit manually + process.on('uncaughtException', (err) => { + // eslint-disable-next-line no-console + console.error('Encountered error; exiting after cleanup.', err); + process.exit(1); }); + + await fleetManager.setup(); + const { policyId } = await agentManager.setup(); + await setupUsers(esConfig); + try { + await runner({ + CYPRESS_OSQUERY_POLICY: policyId, + }); + } finally { + fleetManager.cleanup(); + agentManager.cleanup(); + artifactManager.cleanup(); + } } -export async function OsqueryCypressVisualTestRunner({ getService }: FtrProviderContext) { - const log = getService('log'); - const config = getService('config'); +export async function OsqueryCypressCliTestRunner(context: FtrProviderContext) { + const log = context.getService('log'); + const config = context.getService('config'); + await withFleetAgent( + context, + { + artifacts: { + 'elastic-agent': '7.15.0-SNAPSHOT', + 'fleet-server': '7.15.0-SNAPSHOT', + }, + }, + (runnerEnv) => + withProcRunner(log, async (procs) => { + await procs.run('cypress', { + cmd: 'yarn', + args: ['cypress:run'], + cwd: resolve(__dirname, '../../plugins/osquery'), + env: { + FORCE_COLOR: '1', + // eslint-disable-next-line @typescript-eslint/naming-convention + CYPRESS_baseUrl: Url.format(config.get('servers.kibana')), + // eslint-disable-next-line @typescript-eslint/naming-convention + CYPRESS_protocol: config.get('servers.kibana.protocol'), + // eslint-disable-next-line @typescript-eslint/naming-convention + CYPRESS_hostname: config.get('servers.kibana.hostname'), + // eslint-disable-next-line @typescript-eslint/naming-convention + CYPRESS_configport: config.get('servers.kibana.port'), + CYPRESS_ELASTICSEARCH_URL: Url.format(config.get('servers.elasticsearch')), + CYPRESS_ELASTICSEARCH_USERNAME: config.get('servers.elasticsearch.username'), + CYPRESS_ELASTICSEARCH_PASSWORD: config.get('servers.elasticsearch.password'), + CYPRESS_KIBANA_URL: Url.format({ + protocol: config.get('servers.kibana.protocol'), + hostname: config.get('servers.kibana.hostname'), + port: config.get('servers.kibana.port'), + }), + ...runnerEnv, + ...process.env, + }, + wait: true, + }); + }) + ); +} + +export async function OsqueryCypressVisualTestRunner(context: FtrProviderContext) { + const log = context.getService('log'); + const config = context.getService('config'); - await withProcRunner(log, async (procs) => { - await procs.run('cypress', { - cmd: 'yarn', - args: ['cypress:open'], - cwd: resolve(__dirname, '../../plugins/osquery'), - env: { - FORCE_COLOR: '1', - // eslint-disable-next-line @typescript-eslint/naming-convention - CYPRESS_baseUrl: Url.format(config.get('servers.kibana')), - // eslint-disable-next-line @typescript-eslint/naming-convention - CYPRESS_protocol: config.get('servers.kibana.protocol'), - // eslint-disable-next-line @typescript-eslint/naming-convention - CYPRESS_hostname: config.get('servers.kibana.hostname'), - // eslint-disable-next-line @typescript-eslint/naming-convention - CYPRESS_configport: config.get('servers.kibana.port'), - CYPRESS_ELASTICSEARCH_URL: Url.format(config.get('servers.elasticsearch')), - CYPRESS_ELASTICSEARCH_USERNAME: config.get('servers.elasticsearch.username'), - CYPRESS_ELASTICSEARCH_PASSWORD: config.get('servers.elasticsearch.password'), - CYPRESS_KIBANA_URL: Url.format({ - protocol: config.get('servers.kibana.protocol'), - hostname: config.get('servers.kibana.hostname'), - port: config.get('servers.kibana.port'), - }), - ...process.env, + await withFleetAgent( + context, + { + artifacts: { + 'elastic-agent': '7.15.0-SNAPSHOT', + 'fleet-server': '7.15.0-SNAPSHOT', }, - wait: true, - }); - }); + }, + (runnerEnv) => + withProcRunner( + log, + async (procs) => + await procs.run('cypress', { + cmd: 'yarn', + args: ['cypress:open'], + cwd: resolve(__dirname, '../../plugins/osquery'), + env: { + FORCE_COLOR: '1', + // eslint-disable-next-line @typescript-eslint/naming-convention + CYPRESS_baseUrl: Url.format(config.get('servers.kibana')), + // eslint-disable-next-line @typescript-eslint/naming-convention + CYPRESS_protocol: config.get('servers.kibana.protocol'), + // eslint-disable-next-line @typescript-eslint/naming-convention + CYPRESS_hostname: config.get('servers.kibana.hostname'), + // eslint-disable-next-line @typescript-eslint/naming-convention + CYPRESS_configport: config.get('servers.kibana.port'), + CYPRESS_ELASTICSEARCH_URL: Url.format(config.get('servers.elasticsearch')), + CYPRESS_ELASTICSEARCH_USERNAME: config.get('servers.elasticsearch.username'), + CYPRESS_ELASTICSEARCH_PASSWORD: config.get('servers.elasticsearch.password'), + CYPRESS_KIBANA_URL: Url.format({ + protocol: config.get('servers.kibana.protocol'), + hostname: config.get('servers.kibana.hostname'), + port: config.get('servers.kibana.port'), + }), + ...runnerEnv, + ...process.env, + }, + wait: true, + }) + ) + ); } diff --git a/x-pack/test/osquery_cypress/users.ts b/x-pack/test/osquery_cypress/users.ts new file mode 100644 index 0000000000000..bfe8abfd8452f --- /dev/null +++ b/x-pack/test/osquery_cypress/users.ts @@ -0,0 +1,101 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import axios from 'axios'; +import { ElasticsearchConfig } from './fleet_server'; + +interface Roles { + [roleName: string]: { + indices: [ + { + names: string[]; + privileges: string[]; + allow_restricted_indices: boolean; + } + ]; + applications: [ + { + application: string; + privileges: string[]; + resources: string[]; + } + ]; + transient_metadata: { + enabled: boolean; + }; + }; +} + +interface Users { + [username: string]: { roles: string[] }; +} + +export const ROLES: Roles = { + read: { + indices: [ + { + names: ['logs-*'], + privileges: ['read'], + allow_restricted_indices: false, + }, + ], + applications: [ + { + application: 'kibana-.kibana', + privileges: ['feature_osquery.read'], + resources: ['*'], + }, + ], + transient_metadata: { + enabled: true, + }, + }, + all: { + indices: [ + { + names: ['logs-*'], + privileges: ['read'], + allow_restricted_indices: false, + }, + ], + applications: [ + { + application: 'kibana-.kibana', + privileges: ['feature_osquery.all'], + resources: ['*'], + }, + ], + transient_metadata: { + enabled: true, + }, + }, +}; + +export const USERS: Users = { + osqueryRead: { + roles: ['osquery_read'], + }, + osqueryAll: { + roles: ['osquery_all'], + }, +}; + +export const setupUsers = async (config: ElasticsearchConfig) => { + const { esHost, user: username, password } = config; + const params = { + auth: { username, password }, + }; + await Promise.all( + Object.keys(ROLES).map((role) => + axios.put(`${esHost}/_security/role/osquery_${role}`, ROLES[role], params) + ) + ); + await Promise.all( + Object.keys(USERS).map((newUsername) => + axios.put(`${esHost}/_security/user/${newUsername}`, { password, ...USERS[username] }, params) + ) + ); +}; From c6fcde9a8bb28a361a6c8baf847874fe1140ecb6 Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Wed, 20 Oct 2021 16:46:37 +0200 Subject: [PATCH 130/204] [ES client] Rename deprecated params (#115528) * filterPath --> filter_path * ignoreUnavailable --> ignore_unavailable * ignoreUnavailable --> ignore_unavailable * trackScores --> track_scores * trackTotalHits --> track_total_hits * rollback unnecessary changes --- .../server/fetcher/lib/es_api.test.js | 4 +-- .../get_saved_object_counts.test.ts | 12 +++---- .../get_saved_object_counts.ts | 4 +-- .../collectors/custom_element_collector.ts | 4 +-- .../server/collectors/workpad_collector.ts | 4 +-- .../log_entries/kibana_log_entries_adapter.ts | 8 ++--- .../adapters/metrics/lib/check_valid_node.ts | 4 +-- .../elasticsearch_source_status_adapter.ts | 4 +-- .../queries/log_entry_datasets.ts | 8 ++--- .../server/lib/infra_ml/queries/common.ts | 8 ++--- .../queries/metrics_host_anomalies.test.ts | 8 ++--- .../queries/metrics_k8s_anomalies.test.ts | 8 ++--- .../server/lib/log_analysis/queries/common.ts | 8 ++--- .../plugins/infra/server/lib/metrics/index.ts | 4 +-- .../infra/server/lib/sources/has_data.ts | 4 +-- .../lib/get_cloud_metadata.ts | 4 +-- .../metadata/lib/get_cloud_metric_metadata.ts | 4 +-- .../metadata/lib/get_metric_metadata.ts | 4 +-- .../routes/metadata/lib/get_node_info.ts | 4 +-- .../routes/metadata/lib/get_pod_node_name.ts | 4 +-- .../lib/get_dataset_for_field.ts | 4 +-- .../lib/query_total_groupings.ts | 4 +-- .../server/utils/calculate_metric_interval.ts | 4 +-- .../lib/elasticsearch/get_last_recovery.ts | 2 +- .../actions/all/query.all_actions.dsl.ts | 4 +-- .../details/query.action_details.dsl.ts | 4 +-- .../results/query.action_results.dsl.ts | 4 +-- .../factory/agents/query.all_agents.dsl.ts | 4 +-- .../factory/results/query.all_results.dsl.ts | 4 +-- .../server/usage/get_reporting_usage.ts | 2 +- .../rollup/server/collectors/helpers.ts | 4 +-- .../factory/cti/event_enrichment/query.ts | 4 +-- .../cti/event_enrichment/response.test.ts | 4 +-- .../factory/hosts/all/__mocks__/index.ts | 8 ++--- .../factory/hosts/all/query.all_hosts.dsl.ts | 4 +-- .../hosts/all/query.all_hosts_entities.dsl.ts | 4 +-- .../hosts/authentications/__mocks__/index.ts | 8 ++--- .../hosts/authentications/dsl/query.dsl.ts | 4 +-- .../authentications/dsl/query_entities.dsl.ts | 4 +-- .../factory/hosts/details/__mocks__/index.ts | 8 ++--- .../hosts/details/query.host_details.dsl.ts | 4 +-- .../query.hosts_kpi_authentications.dsl.ts | 4 +-- ....hosts_kpi_authentications_entities.dsl.ts | 4 +-- .../kpi/hosts/query.hosts_kpi_hosts.dsl.ts | 4 +-- .../query.hosts_kpi_hosts_entities.dsl.ts | 4 +-- .../query.hosts_kpi_unique_ips.dsl.ts | 4 +-- ...query.hosts_kpi_unique_ips_entities.dsl.ts | 4 +-- .../hosts/last_first_seen/__mocks__/index.ts | 12 +++---- .../query.first_or_last_seen_host.dsl.ts | 4 +-- .../factory/hosts/overview/__mocks__/index.ts | 8 ++--- .../hosts/overview/query.overview_host.dsl.ts | 4 +-- .../hosts/risk_score/query.hosts_risk.dsl.ts | 4 +-- .../uncommon_processes/__mocks__/index.ts | 8 ++--- .../hosts/uncommon_processes/dsl/query.dsl.ts | 4 +-- .../matrix_histogram/__mocks__/index.ts | 24 +++++++------- .../alerts/__mocks__/index.ts | 4 +-- .../alerts/query.alerts_histogram.dsl.ts | 4 +-- .../anomalies/__mocks__/index.ts | 4 +-- .../query.anomalies_histogram.dsl.ts | 4 +-- .../authentications/__mocks__/index.ts | 4 +-- .../query.authentications_histogram.dsl.ts | 4 +-- ....authentications_histogram_entities.dsl.ts | 4 +-- .../matrix_histogram/dns/__mocks__/index.ts | 4 +-- .../dns/query.dns_histogram.dsl.ts | 4 +-- .../events/__mocks__/index.ts | 32 +++++++++---------- .../events/query.events_histogram.dsl.ts | 4 +-- .../network/details/__mocks__/index.ts | 8 ++--- .../details/query.details_network.dsl.ts | 4 +-- .../factory/network/dns/__mocks__/index.ts | 8 ++--- .../network/dns/query.dns_network.dsl.ts | 4 +-- .../factory/network/http/__mocks__/index.ts | 8 ++--- .../network/http/query.http_network.dsl.ts | 4 +-- .../dns/query.network_kip_dns_entities.dsl.ts | 4 +-- .../kpi/dns/query.network_kpi_dns.dsl.ts | 4 +-- .../query.network_kpi_network_events.dsl.ts | 4 +-- ...network_kpi_network_events_entities.dsl.ts | 4 +-- .../query.network_kpi_tls_handshakes.dsl.ts | 4 +-- ...network_kpi_tls_handshakes_entities.dsl.ts | 4 +-- .../query.network_kpi_unique_flows.dsl.ts | 4 +-- ...uery.network_kpi_unique_private_ips.dsl.ts | 4 +-- ...ork_kpi_unique_private_ips_entities.dsl.ts | 4 +-- .../network/overview/__mocks__/index.ts | 8 ++--- .../overview/query.overview_network.dsl.ts | 4 +-- .../factory/network/tls/__mocks__/index.ts | 8 ++--- .../network/tls/query.tls_network.dsl.ts | 4 +-- .../network/top_countries/__mocks__/index.ts | 8 ++--- .../query.top_countries_network.dsl.ts | 4 +-- ...uery.top_countries_network_entities.dsl.ts | 4 +-- .../network/top_n_flow/__mocks__/index.ts | 8 ++--- .../query.top_n_flow_network.dsl.ts | 4 +-- .../query.top_n_flow_network_entities.dsl.ts | 4 +-- .../factory/network/users/__mocks__/index.ts | 8 ++--- .../network/users/query.users_network.dsl.ts | 4 +-- .../ueba/host_rules/query.host_rules.dsl.ts | 4 +-- .../host_tactics/query.host_tactics.dsl.ts | 4 +-- .../ueba/risk_score/query.risk_score.dsl.ts | 4 +-- .../ueba/user_rules/query.user_rules.dsl.ts | 4 +-- .../detections/detection_rule_helpers.ts | 4 +-- .../server/usage/detections/types.ts | 4 +-- .../events/all/query.events_all.dsl.ts | 4 +-- .../details/query.events_details.dsl.test.ts | 4 +-- .../details/query.events_details.dsl.ts | 4 +-- .../factory/events/kpi/query.kpi.dsl.ts | 4 +-- .../query.events_last_event_time.dsl.ts | 12 +++---- 104 files changed, 280 insertions(+), 280 deletions(-) diff --git a/src/plugins/data_views/server/fetcher/lib/es_api.test.js b/src/plugins/data_views/server/fetcher/lib/es_api.test.js index 2893fde671bb2..085add965b921 100644 --- a/src/plugins/data_views/server/fetcher/lib/es_api.test.js +++ b/src/plugins/data_views/server/fetcher/lib/es_api.test.js @@ -61,7 +61,7 @@ describe('server/index_patterns/service/lib/es_api', () => { expect(resp).toBe(football); }); - it('sets ignoreUnavailable and allowNoIndices params', async () => { + it('sets ignore_unavailable and allow_no_indices params', async () => { const getAlias = sinon.stub(); const callCluster = { indices: { @@ -149,7 +149,7 @@ describe('server/index_patterns/service/lib/es_api', () => { expect(resp).toBe(football); }); - it('sets ignoreUnavailable, allowNoIndices, and fields params', async () => { + it('sets ignore_unavailable, allow_no_indices, and fields params', async () => { const fieldCaps = sinon.stub(); const callCluster = { indices: { diff --git a/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/get_saved_object_counts.test.ts b/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/get_saved_object_counts.test.ts index 94c618354fc37..4b3421efed96e 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/get_saved_object_counts.test.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/get_saved_object_counts.test.ts @@ -26,8 +26,8 @@ describe('getSavedObjectsCounts', () => { expect(results).toStrictEqual([]); expect(esClient.search).toHaveBeenCalledWith({ index: '.kibana', - ignoreUnavailable: true, - filterPath: 'aggregations.types.buckets', + ignore_unavailable: true, + filter_path: 'aggregations.types.buckets', body: { size: 0, query: { match_all: {} }, @@ -41,8 +41,8 @@ describe('getSavedObjectsCounts', () => { await getSavedObjectsCounts(esClient, '.kibana'); expect(esClient.search).toHaveBeenCalledWith({ index: '.kibana', - ignoreUnavailable: true, - filterPath: 'aggregations.types.buckets', + ignore_unavailable: true, + filter_path: 'aggregations.types.buckets', body: { size: 0, query: { match_all: {} }, @@ -56,8 +56,8 @@ describe('getSavedObjectsCounts', () => { await getSavedObjectsCounts(esClient, '.kibana', ['type_one', 'type_two']); expect(esClient.search).toHaveBeenCalledWith({ index: '.kibana', - ignoreUnavailable: true, - filterPath: 'aggregations.types.buckets', + ignore_unavailable: true, + filter_path: 'aggregations.types.buckets', body: { size: 0, query: { terms: { type: ['type_one', 'type_two'] } }, diff --git a/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/get_saved_object_counts.ts b/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/get_saved_object_counts.ts index eeaeed67e753f..f82a803b763c5 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/get_saved_object_counts.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/saved_objects_counts/get_saved_object_counts.ts @@ -17,8 +17,8 @@ export async function getSavedObjectsCounts( const savedObjectCountSearchParams = { index: kibanaIndex, - ignoreUnavailable: true, - filterPath: 'aggregations.types.buckets', + ignore_unavailable: true, + filter_path: 'aggregations.types.buckets', body: { size: 0, query, diff --git a/x-pack/plugins/canvas/server/collectors/custom_element_collector.ts b/x-pack/plugins/canvas/server/collectors/custom_element_collector.ts index 18cfe1a3df56c..b0e219a4d886d 100644 --- a/x-pack/plugins/canvas/server/collectors/custom_element_collector.ts +++ b/x-pack/plugins/canvas/server/collectors/custom_element_collector.ts @@ -147,8 +147,8 @@ const customElementCollector: TelemetryCollector = async function customElementC const customElementParams = { size: 10000, index: kibanaIndex, - ignoreUnavailable: true, - filterPath: [`hits.hits._source.${CUSTOM_ELEMENT_TYPE}.content`], + ignore_unavailable: true, + filter_path: [`hits.hits._source.${CUSTOM_ELEMENT_TYPE}.content`], body: { query: { bool: { filter: { term: { type: CUSTOM_ELEMENT_TYPE } } } } }, }; diff --git a/x-pack/plugins/canvas/server/collectors/workpad_collector.ts b/x-pack/plugins/canvas/server/collectors/workpad_collector.ts index d42b8480ed7c7..62b0ce200e320 100644 --- a/x-pack/plugins/canvas/server/collectors/workpad_collector.ts +++ b/x-pack/plugins/canvas/server/collectors/workpad_collector.ts @@ -381,8 +381,8 @@ const workpadCollector: TelemetryCollector = async function (kibanaIndex, esClie const searchParams = { size: 10000, // elasticsearch index.max_result_window default value index: kibanaIndex, - ignoreUnavailable: true, - filterPath: ['hits.hits._source.canvas-workpad', '-hits.hits._source.canvas-workpad.assets'], + ignore_unavailable: true, + filter_path: ['hits.hits._source.canvas-workpad', '-hits.hits._source.canvas-workpad.assets'], body: { query: { bool: { filter: { term: { type: CANVAS_TYPE } } } } }, }; diff --git a/x-pack/plugins/infra/server/lib/adapters/log_entries/kibana_log_entries_adapter.ts b/x-pack/plugins/infra/server/lib/adapters/log_entries/kibana_log_entries_adapter.ts index ab98de7901a3d..524658559eadf 100644 --- a/x-pack/plugins/infra/server/lib/adapters/log_entries/kibana_log_entries_adapter.ts +++ b/x-pack/plugins/infra/server/lib/adapters/log_entries/kibana_log_entries_adapter.ts @@ -69,9 +69,9 @@ export class InfraKibanaLogEntriesAdapter implements LogEntriesAdapter { }; const esQuery = { - allowNoIndices: true, + allow_no_indices: true, index: resolvedLogSourceConfiguration.indices, - ignoreUnavailable: true, + ignore_unavailable: true, body: { size: size + 1, // Extra one to test if it has more before or after track_total_hits: false, @@ -139,9 +139,9 @@ export class InfraKibanaLogEntriesAdapter implements LogEntriesAdapter { ); const query = { - allowNoIndices: true, + allow_no_indices: true, index: resolvedLogSourceConfiguration.indices, - ignoreUnavailable: true, + ignore_unavailable: true, body: { aggregations: { count_by_date: { diff --git a/x-pack/plugins/infra/server/lib/adapters/metrics/lib/check_valid_node.ts b/x-pack/plugins/infra/server/lib/adapters/metrics/lib/check_valid_node.ts index f53016561dc5b..f2b7691646e48 100644 --- a/x-pack/plugins/infra/server/lib/adapters/metrics/lib/check_valid_node.ts +++ b/x-pack/plugins/infra/server/lib/adapters/metrics/lib/check_valid_node.ts @@ -14,8 +14,8 @@ export const checkValidNode = async ( id: string ): Promise => { const params = { - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, index: indexPattern, terminateAfter: 1, body: { diff --git a/x-pack/plugins/infra/server/lib/adapters/source_status/elasticsearch_source_status_adapter.ts b/x-pack/plugins/infra/server/lib/adapters/source_status/elasticsearch_source_status_adapter.ts index 0aa305a580ffa..9c15d4746cdc5 100644 --- a/x-pack/plugins/infra/server/lib/adapters/source_status/elasticsearch_source_status_adapter.ts +++ b/x-pack/plugins/infra/server/lib/adapters/source_status/elasticsearch_source_status_adapter.ts @@ -18,13 +18,13 @@ export class InfraElasticsearchSourceStatusAdapter implements InfraSourceStatusA this.framework .callWithRequest(requestContext, 'indices.getAlias', { name: aliasName, - filterPath: '*.settings.index.uuid', // to keep the response size as small as possible + filter_path: '*.settings.index.uuid', // to keep the response size as small as possible }) .catch(withDefaultIfNotFound({})), this.framework .callWithRequest(requestContext, 'indices.get', { index: aliasName, - filterPath: '*.settings.index.uuid', // to keep the response size as small as possible + filter_path: '*.settings.index.uuid', // to keep the response size as small as possible }) .catch(withDefaultIfNotFound({})), ]); diff --git a/x-pack/plugins/infra/server/lib/domains/log_entries_domain/queries/log_entry_datasets.ts b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/queries/log_entry_datasets.ts index 3431f3bfb0c8c..9eae8daa3e74f 100644 --- a/x-pack/plugins/infra/server/lib/domains/log_entries_domain/queries/log_entry_datasets.ts +++ b/x-pack/plugins/infra/server/lib/domains/log_entries_domain/queries/log_entry_datasets.ts @@ -65,10 +65,10 @@ export const createLogEntryDatasetsQuery = ( }); const defaultRequestParameters = { - allowNoIndices: true, - ignoreUnavailable: true, - trackScores: false, - trackTotalHits: false, + allow_no_indices: true, + ignore_unavailable: true, + track_scores: false, + track_total_hits: false, }; const compositeDatasetKeyRT = rt.type({ diff --git a/x-pack/plugins/infra/server/lib/infra_ml/queries/common.ts b/x-pack/plugins/infra/server/lib/infra_ml/queries/common.ts index f4088c56303e2..594dc3371f3a2 100644 --- a/x-pack/plugins/infra/server/lib/infra_ml/queries/common.ts +++ b/x-pack/plugins/infra/server/lib/infra_ml/queries/common.ts @@ -6,10 +6,10 @@ */ export const defaultRequestParameters = { - allowNoIndices: true, - ignoreUnavailable: true, - trackScores: false, - trackTotalHits: false, + allow_no_indices: true, + ignore_unavailable: true, + track_scores: false, + track_total_hits: false, }; export const createJobIdFilters = (jobId: string) => [ diff --git a/x-pack/plugins/infra/server/lib/infra_ml/queries/metrics_host_anomalies.test.ts b/x-pack/plugins/infra/server/lib/infra_ml/queries/metrics_host_anomalies.test.ts index 7c281963e5717..5e7cb78790de0 100644 --- a/x-pack/plugins/infra/server/lib/infra_ml/queries/metrics_host_anomalies.test.ts +++ b/x-pack/plugins/infra/server/lib/infra_ml/queries/metrics_host_anomalies.test.ts @@ -27,10 +27,10 @@ describe('createMetricsHostAnomaliesQuery', () => { pagination, }) ).toMatchObject({ - allowNoIndices: true, - ignoreUnavailable: true, - trackScores: false, - trackTotalHits: false, + allow_no_indices: true, + ignore_unavailable: true, + track_scores: false, + track_total_hits: false, body: { query: { bool: { diff --git a/x-pack/plugins/infra/server/lib/infra_ml/queries/metrics_k8s_anomalies.test.ts b/x-pack/plugins/infra/server/lib/infra_ml/queries/metrics_k8s_anomalies.test.ts index 61efb896a6c89..959b20a482544 100644 --- a/x-pack/plugins/infra/server/lib/infra_ml/queries/metrics_k8s_anomalies.test.ts +++ b/x-pack/plugins/infra/server/lib/infra_ml/queries/metrics_k8s_anomalies.test.ts @@ -27,10 +27,10 @@ describe('createMetricsK8sAnomaliesQuery', () => { pagination, }) ).toMatchObject({ - allowNoIndices: true, - ignoreUnavailable: true, - trackScores: false, - trackTotalHits: false, + allow_no_indices: true, + ignore_unavailable: true, + track_scores: false, + track_total_hits: false, body: { query: { bool: { diff --git a/x-pack/plugins/infra/server/lib/log_analysis/queries/common.ts b/x-pack/plugins/infra/server/lib/log_analysis/queries/common.ts index 4c3ba5612c443..b05e4ce00fe9c 100644 --- a/x-pack/plugins/infra/server/lib/log_analysis/queries/common.ts +++ b/x-pack/plugins/infra/server/lib/log_analysis/queries/common.ts @@ -6,10 +6,10 @@ */ export const defaultRequestParameters = { - allowNoIndices: true, - ignoreUnavailable: true, - trackScores: false, - trackTotalHits: false, + allow_no_indices: true, + ignore_unavailable: true, + track_scores: false, + track_total_hits: false, }; export const createJobIdFilters = (jobId: string) => [ diff --git a/x-pack/plugins/infra/server/lib/metrics/index.ts b/x-pack/plugins/infra/server/lib/metrics/index.ts index 131f6944b0484..d291dbf88b49a 100644 --- a/x-pack/plugins/infra/server/lib/metrics/index.ts +++ b/x-pack/plugins/infra/server/lib/metrics/index.ts @@ -47,8 +47,8 @@ export const query = async ( ]; const params = { - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, index: options.indexPattern, body: { size: 0, diff --git a/x-pack/plugins/infra/server/lib/sources/has_data.ts b/x-pack/plugins/infra/server/lib/sources/has_data.ts index b036124344ea9..d56512918f11a 100644 --- a/x-pack/plugins/infra/server/lib/sources/has_data.ts +++ b/x-pack/plugins/infra/server/lib/sources/has_data.ts @@ -10,9 +10,9 @@ import { ESSearchClient } from '../metrics/types'; export const hasData = async (index: string, client: ESSearchClient) => { const params = { index, - allowNoIndices: true, + allow_no_indices: true, terminate_after: 1, - ignoreUnavailable: true, + ignore_unavailable: true, body: { size: 0, }, diff --git a/x-pack/plugins/infra/server/routes/inventory_metadata/lib/get_cloud_metadata.ts b/x-pack/plugins/infra/server/routes/inventory_metadata/lib/get_cloud_metadata.ts index 57f99aee6d187..c721ca75ea978 100644 --- a/x-pack/plugins/infra/server/routes/inventory_metadata/lib/get_cloud_metadata.ts +++ b/x-pack/plugins/infra/server/routes/inventory_metadata/lib/get_cloud_metadata.ts @@ -40,8 +40,8 @@ export const getCloudMetadata = async ( } const metricQuery = { - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, index: sourceConfiguration.metricAlias, body: { query: { diff --git a/x-pack/plugins/infra/server/routes/metadata/lib/get_cloud_metric_metadata.ts b/x-pack/plugins/infra/server/routes/metadata/lib/get_cloud_metric_metadata.ts index be2a39b84ef1c..d9da7bce2246f 100644 --- a/x-pack/plugins/infra/server/routes/metadata/lib/get_cloud_metric_metadata.ts +++ b/x-pack/plugins/infra/server/routes/metadata/lib/get_cloud_metric_metadata.ts @@ -26,8 +26,8 @@ export const getCloudMetricsMetadata = async ( timeRange: { from: number; to: number } ): Promise => { const metricQuery = { - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, index: sourceConfiguration.metricAlias, body: { query: { diff --git a/x-pack/plugins/infra/server/routes/metadata/lib/get_metric_metadata.ts b/x-pack/plugins/infra/server/routes/metadata/lib/get_metric_metadata.ts index 26bcf267ad6cc..bfa0884bfe199 100644 --- a/x-pack/plugins/infra/server/routes/metadata/lib/get_metric_metadata.ts +++ b/x-pack/plugins/infra/server/routes/metadata/lib/get_metric_metadata.ts @@ -32,8 +32,8 @@ export const getMetricMetadata = async ( ): Promise => { const fields = findInventoryFields(nodeType, sourceConfiguration.fields); const metricQuery = { - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, index: sourceConfiguration.metricAlias, body: { query: { diff --git a/x-pack/plugins/infra/server/routes/metadata/lib/get_node_info.ts b/x-pack/plugins/infra/server/routes/metadata/lib/get_node_info.ts index fccbead2832c9..06035ed40adf1 100644 --- a/x-pack/plugins/infra/server/routes/metadata/lib/get_node_info.ts +++ b/x-pack/plugins/infra/server/routes/metadata/lib/get_node_info.ts @@ -53,8 +53,8 @@ export const getNodeInfo = async ( const fields = findInventoryFields(nodeType, sourceConfiguration.fields); const timestampField = sourceConfiguration.fields.timestamp; const params = { - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, terminateAfter: 1, index: sourceConfiguration.metricAlias, body: { diff --git a/x-pack/plugins/infra/server/routes/metadata/lib/get_pod_node_name.ts b/x-pack/plugins/infra/server/routes/metadata/lib/get_pod_node_name.ts index 8e4c707d9b467..9bf809ba3b3f4 100644 --- a/x-pack/plugins/infra/server/routes/metadata/lib/get_pod_node_name.ts +++ b/x-pack/plugins/infra/server/routes/metadata/lib/get_pod_node_name.ts @@ -22,8 +22,8 @@ export const getPodNodeName = async ( const fields = findInventoryFields(nodeType, sourceConfiguration.fields); const timestampField = sourceConfiguration.fields.timestamp; const params = { - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, terminateAfter: 1, index: sourceConfiguration.metricAlias, body: { diff --git a/x-pack/plugins/infra/server/routes/metrics_explorer/lib/get_dataset_for_field.ts b/x-pack/plugins/infra/server/routes/metrics_explorer/lib/get_dataset_for_field.ts index b88472fbb4b14..be25bbbf022ee 100644 --- a/x-pack/plugins/infra/server/routes/metrics_explorer/lib/get_dataset_for_field.ts +++ b/x-pack/plugins/infra/server/routes/metrics_explorer/lib/get_dataset_for_field.ts @@ -22,8 +22,8 @@ export const getDatasetForField = async ( timerange: { field: string; to: number; from: number } ) => { const params = { - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, terminateAfter: 1, index: indexPattern, body: { diff --git a/x-pack/plugins/infra/server/routes/metrics_explorer/lib/query_total_groupings.ts b/x-pack/plugins/infra/server/routes/metrics_explorer/lib/query_total_groupings.ts index b871fa21c111d..a2bf778d5016d 100644 --- a/x-pack/plugins/infra/server/routes/metrics_explorer/lib/query_total_groupings.ts +++ b/x-pack/plugins/infra/server/routes/metrics_explorer/lib/query_total_groupings.ts @@ -41,8 +41,8 @@ export const queryTotalGroupings = async ( } const params = { - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, index: options.indexPattern, body: { size: 0, diff --git a/x-pack/plugins/infra/server/utils/calculate_metric_interval.ts b/x-pack/plugins/infra/server/utils/calculate_metric_interval.ts index 13a108b76b6a4..3357b1a842183 100644 --- a/x-pack/plugins/infra/server/utils/calculate_metric_interval.ts +++ b/x-pack/plugins/infra/server/utils/calculate_metric_interval.ts @@ -35,9 +35,9 @@ export const calculateMetricInterval = async ( from = options.timerange.to - inventoryModel.metrics.defaultTimeRangeInSeconds * 1000; } const query = { - allowNoIndices: true, + allow_no_indices: true, index: options.indexPattern, - ignoreUnavailable: true, + ignore_unavailable: true, body: { query: { bool: { diff --git a/x-pack/plugins/monitoring/server/lib/elasticsearch/get_last_recovery.ts b/x-pack/plugins/monitoring/server/lib/elasticsearch/get_last_recovery.ts index 43527f875cf72..974ffad7745d9 100644 --- a/x-pack/plugins/monitoring/server/lib/elasticsearch/get_last_recovery.ts +++ b/x-pack/plugins/monitoring/server/lib/elasticsearch/get_last_recovery.ts @@ -107,7 +107,7 @@ export async function getLastRecovery(req: LegacyRequest, esIndexPattern: string const mbParams = { index: esIndexPattern, size, - ignoreUnavailable: true, + ignore_unavailable: true, body: { _source: ['elasticsearch.index.recovery', '@timestamp'], sort: { timestamp: { order: 'desc', unmapped_type: 'long' } }, diff --git a/x-pack/plugins/osquery/server/search_strategy/osquery/factory/actions/all/query.all_actions.dsl.ts b/x-pack/plugins/osquery/server/search_strategy/osquery/factory/actions/all/query.all_actions.dsl.ts index 55bec687d3e2c..8dc8fad02a7c1 100644 --- a/x-pack/plugins/osquery/server/search_strategy/osquery/factory/actions/all/query.all_actions.dsl.ts +++ b/x-pack/plugins/osquery/server/search_strategy/osquery/factory/actions/all/query.all_actions.dsl.ts @@ -20,9 +20,9 @@ export const buildActionsQuery = ({ // const filter = [...createQueryFilterClauses(filterQuery)]; const dslQuery = { - allowNoIndices: true, + allow_no_indices: true, index: '.fleet-actions', - ignoreUnavailable: true, + ignore_unavailable: true, body: { // query: { bool: { filter } }, query: { diff --git a/x-pack/plugins/osquery/server/search_strategy/osquery/factory/actions/details/query.action_details.dsl.ts b/x-pack/plugins/osquery/server/search_strategy/osquery/factory/actions/details/query.action_details.dsl.ts index bc9d63e619338..41f2875b1d4c8 100644 --- a/x-pack/plugins/osquery/server/search_strategy/osquery/factory/actions/details/query.action_details.dsl.ts +++ b/x-pack/plugins/osquery/server/search_strategy/osquery/factory/actions/details/query.action_details.dsl.ts @@ -23,9 +23,9 @@ export const buildActionDetailsQuery = ({ ]; const dslQuery = { - allowNoIndices: true, + allow_no_indices: true, index: '.fleet-actions', - ignoreUnavailable: true, + ignore_unavailable: true, body: { query: { bool: { filter } }, size: 1, diff --git a/x-pack/plugins/osquery/server/search_strategy/osquery/factory/actions/results/query.action_results.dsl.ts b/x-pack/plugins/osquery/server/search_strategy/osquery/factory/actions/results/query.action_results.dsl.ts index d74067bff0251..109e260911933 100644 --- a/x-pack/plugins/osquery/server/search_strategy/osquery/factory/actions/results/query.action_results.dsl.ts +++ b/x-pack/plugins/osquery/server/search_strategy/osquery/factory/actions/results/query.action_results.dsl.ts @@ -25,9 +25,9 @@ export const buildActionResultsQuery = ({ ]; const dslQuery = { - allowNoIndices: true, + allow_no_indices: true, index: '.fleet-actions-results*', - ignoreUnavailable: true, + ignore_unavailable: true, body: { aggs: { aggs: { diff --git a/x-pack/plugins/osquery/server/search_strategy/osquery/factory/agents/query.all_agents.dsl.ts b/x-pack/plugins/osquery/server/search_strategy/osquery/factory/agents/query.all_agents.dsl.ts index 52101462270c7..314de574f0a70 100644 --- a/x-pack/plugins/osquery/server/search_strategy/osquery/factory/agents/query.all_agents.dsl.ts +++ b/x-pack/plugins/osquery/server/search_strategy/osquery/factory/agents/query.all_agents.dsl.ts @@ -21,9 +21,9 @@ export const buildAgentsQuery = ({ ]; const dslQuery = { - allowNoIndices: true, + allow_no_indices: true, index: '.fleet-agents', - ignoreUnavailable: true, + ignore_unavailable: true, body: { query: { bool: { diff --git a/x-pack/plugins/osquery/server/search_strategy/osquery/factory/results/query.all_results.dsl.ts b/x-pack/plugins/osquery/server/search_strategy/osquery/factory/results/query.all_results.dsl.ts index 406ff26991f0e..59326d6c50155 100644 --- a/x-pack/plugins/osquery/server/search_strategy/osquery/factory/results/query.all_results.dsl.ts +++ b/x-pack/plugins/osquery/server/search_strategy/osquery/factory/results/query.all_results.dsl.ts @@ -36,9 +36,9 @@ export const buildResultsQuery = ({ ]; const dslQuery = { - allowNoIndices: true, + allow_no_indices: true, index: `logs-${OSQUERY_INTEGRATION_NAME}.result*`, - ignoreUnavailable: true, + ignore_unavailable: true, body: { aggs: { count_by_agent_id: { diff --git a/x-pack/plugins/reporting/server/usage/get_reporting_usage.ts b/x-pack/plugins/reporting/server/usage/get_reporting_usage.ts index 69213d8f8cacc..b2c6aece924f2 100644 --- a/x-pack/plugins/reporting/server/usage/get_reporting_usage.ts +++ b/x-pack/plugins/reporting/server/usage/get_reporting_usage.ts @@ -148,7 +148,7 @@ export async function getReportingUsage( const reportingIndex = REPORTING_SYSTEM_INDEX; const params = { index: `${reportingIndex}-*`, - filterPath: 'aggregations.*.buckets', + filter_path: 'aggregations.*.buckets', body: { size: 0, aggs: { diff --git a/x-pack/plugins/rollup/server/collectors/helpers.ts b/x-pack/plugins/rollup/server/collectors/helpers.ts index b6e5bc190d972..b007bbbff7e4a 100644 --- a/x-pack/plugins/rollup/server/collectors/helpers.ts +++ b/x-pack/plugins/rollup/server/collectors/helpers.ts @@ -32,8 +32,8 @@ export async function fetchRollupIndexPatterns(kibanaIndex: string, esClient: El const searchParams = { size: ES_MAX_RESULT_WINDOW_DEFAULT_VALUE, index: kibanaIndex, - ignoreUnavailable: true, - filterPath: ['hits.hits._id'], + ignore_unavailable: true, + filter_path: ['hits.hits._id'], body: { query: { bool: { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/event_enrichment/query.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/event_enrichment/query.ts index 786f9a20f77e5..ea015ea145b3f 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/event_enrichment/query.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/event_enrichment/query.ts @@ -28,8 +28,8 @@ export const buildEventEnrichmentQuery: SecuritySolutionFactory { const parsedResponse = await parseEventEnrichmentResponse(options, response); const expectedInspect = expect.objectContaining({ - allowNoIndices: true, + allow_no_indices: true, body: { _source: false, fields: ['*'], @@ -57,7 +57,7 @@ describe('parseEventEnrichmentResponse', () => { }, }, }, - ignoreUnavailable: true, + ignore_unavailable: true, index: ['filebeat-*'], }); const parsedInspect = JSON.parse(parsedResponse.inspect.dsl[0]); diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/all/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/all/__mocks__/index.ts index 0369f182a4c75..d6e456e706673 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/all/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/all/__mocks__/index.ts @@ -611,7 +611,7 @@ export const formattedSearchStrategyResponse = { dsl: [ JSON.stringify( { - allowNoIndices: true, + allow_no_indices: true, index: [ 'apm-*-transaction*', 'traces-apm*', @@ -622,7 +622,7 @@ export const formattedSearchStrategyResponse = { 'packetbeat-*', 'winlogbeat-*', ], - ignoreUnavailable: true, + ignore_unavailable: true, track_total_hits: false, body: { docvalue_fields: mockOptions.docValueFields, @@ -783,7 +783,7 @@ export const mockBuckets: HostAggEsItem = { }; export const expectedDsl = { - allowNoIndices: true, + allow_no_indices: true, track_total_hits: false, body: { aggregations: { @@ -821,7 +821,7 @@ export const expectedDsl = { docvalue_fields: mockOptions.docValueFields, size: 0, }, - ignoreUnavailable: true, + ignore_unavailable: true, index: [ 'apm-*-transaction*', 'traces-apm*', diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/all/query.all_hosts.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/all/query.all_hosts.dsl.ts index 5d8540f886077..bc405e89bf7a6 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/all/query.all_hosts.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/all/query.all_hosts.dsl.ts @@ -40,9 +40,9 @@ export const buildHostsQuery = ({ const agg = { host_count: { cardinality: { field: 'host.name' } } }; const dslQuery = { - allowNoIndices: true, + allow_no_indices: true, index: defaultIndex, - ignoreUnavailable: true, + ignore_unavailable: true, track_total_hits: false, body: { ...(!isEmpty(docValueFields) ? { docvalue_fields: docValueFields } : {}), diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/all/query.all_hosts_entities.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/all/query.all_hosts_entities.dsl.ts index 1c338998e3b65..94124bc9567b7 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/all/query.all_hosts_entities.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/all/query.all_hosts_entities.dsl.ts @@ -40,9 +40,9 @@ export const buildHostsQueryEntities = ({ const agg = { host_count: { cardinality: { field: 'host.name' } } }; const dslQuery = { - allowNoIndices: true, + allow_no_indices: true, index: defaultIndex, - ignoreUnavailable: true, + ignore_unavailable: true, track_total_hits: false, body: { ...(!isEmpty(docValueFields) ? { docvalue_fields: docValueFields } : {}), diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/authentications/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/authentications/__mocks__/index.ts index 1dd3dc8ee4cff..bdc4755c6a02b 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/authentications/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/authentications/__mocks__/index.ts @@ -2149,7 +2149,7 @@ export const formattedSearchStrategyResponse = { dsl: [ JSON.stringify( { - allowNoIndices: true, + allow_no_indices: true, index: [ 'apm-*-transaction*', 'traces-apm*', @@ -2160,7 +2160,7 @@ export const formattedSearchStrategyResponse = { 'packetbeat-*', 'winlogbeat-*', ], - ignoreUnavailable: true, + ignore_unavailable: true, body: { docvalue_fields: mockOptions.docValueFields, aggregations: { @@ -2371,7 +2371,7 @@ export const formattedSearchStrategyResponse = { }; export const expectedDsl = { - allowNoIndices: true, + allow_no_indices: true, index: [ 'apm-*-transaction*', 'traces-apm*', @@ -2382,7 +2382,7 @@ export const expectedDsl = { 'packetbeat-*', 'winlogbeat-*', ], - ignoreUnavailable: true, + ignore_unavailable: true, body: { docvalue_fields: mockOptions.docValueFields, aggregations: { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/authentications/dsl/query.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/authentications/dsl/query.dsl.ts index 325d45e04b2b0..1057ace837b43 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/authentications/dsl/query.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/authentications/dsl/query.dsl.ts @@ -61,9 +61,9 @@ export const buildQuery = ({ }; const dslQuery = { - allowNoIndices: true, + allow_no_indices: true, index: defaultIndex, - ignoreUnavailable: true, + ignore_unavailable: true, body: { ...(!isEmpty(docValueFields) ? { docvalue_fields: docValueFields } : {}), aggregations: { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/authentications/dsl/query_entities.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/authentications/dsl/query_entities.dsl.ts index d320130115588..a17bb2ecf9c8f 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/authentications/dsl/query_entities.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/authentications/dsl/query_entities.dsl.ts @@ -41,9 +41,9 @@ export const buildQueryEntities = ({ }; const dslQuery = { - allowNoIndices: true, + allow_no_indices: true, index: defaultIndex, - ignoreUnavailable: true, + ignore_unavailable: true, body: { ...(!isEmpty(docValueFields) ? { docvalue_fields: docValueFields } : {}), aggregations: { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/details/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/details/__mocks__/index.ts index e0473c9501b0c..ec8c7d7ca6866 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/details/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/details/__mocks__/index.ts @@ -1301,7 +1301,7 @@ export const formattedSearchStrategyResponse = { dsl: [ JSON.stringify( { - allowNoIndices: true, + allow_no_indices: true, index: [ 'apm-*-transaction*', 'traces-apm*', @@ -1312,7 +1312,7 @@ export const formattedSearchStrategyResponse = { 'packetbeat-*', 'winlogbeat-*', ], - ignoreUnavailable: true, + ignore_unavailable: true, track_total_hits: false, body: { aggregations: { @@ -1415,7 +1415,7 @@ export const formattedSearchStrategyResponse = { }; export const expectedDsl = { - allowNoIndices: true, + allow_no_indices: true, index: [ 'apm-*-transaction*', 'traces-apm*', @@ -1426,7 +1426,7 @@ export const expectedDsl = { 'packetbeat-*', 'winlogbeat-*', ], - ignoreUnavailable: true, + ignore_unavailable: true, track_total_hits: false, body: { aggregations: { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/details/query.host_details.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/details/query.host_details.dsl.ts index 45afed2526aa3..003149a9501bb 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/details/query.host_details.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/details/query.host_details.dsl.ts @@ -35,9 +35,9 @@ export const buildHostDetailsQuery = ({ ]; const dslQuery = { - allowNoIndices: true, + allow_no_indices: true, index: defaultIndex, - ignoreUnavailable: true, + ignore_unavailable: true, track_total_hits: false, body: { aggregations: { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/authentications/query.hosts_kpi_authentications.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/authentications/query.hosts_kpi_authentications.dsl.ts index 01473a4368dbc..738a9db683728 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/authentications/query.hosts_kpi_authentications.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/authentications/query.hosts_kpi_authentications.dsl.ts @@ -39,8 +39,8 @@ export const buildHostsKpiAuthenticationsQuery = ({ const dslQuery = { index: defaultIndex, - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, track_total_hits: false, body: { aggs: { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/authentications/query.hosts_kpi_authentications_entities.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/authentications/query.hosts_kpi_authentications_entities.dsl.ts index cff09f2354d31..fd1104345babc 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/authentications/query.hosts_kpi_authentications_entities.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/authentications/query.hosts_kpi_authentications_entities.dsl.ts @@ -28,8 +28,8 @@ export const buildHostsKpiAuthenticationsQueryEntities = ({ const dslQuery = { index: defaultIndex, - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, track_total_hits: false, body: { aggs: { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/hosts/query.hosts_kpi_hosts.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/hosts/query.hosts_kpi_hosts.dsl.ts index 5ea2d1aa40780..ed1d0e8edb107 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/hosts/query.hosts_kpi_hosts.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/hosts/query.hosts_kpi_hosts.dsl.ts @@ -28,8 +28,8 @@ export const buildHostsKpiHostsQuery = ({ const dslQuery = { index: defaultIndex, - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, track_total_hits: false, body: { aggregations: { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/hosts/query.hosts_kpi_hosts_entities.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/hosts/query.hosts_kpi_hosts_entities.dsl.ts index 972ead9a6538e..785f6aa5b6980 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/hosts/query.hosts_kpi_hosts_entities.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/hosts/query.hosts_kpi_hosts_entities.dsl.ts @@ -28,8 +28,8 @@ export const buildHostsKpiHostsQueryEntities = ({ const dslQuery = { index: defaultIndex, - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, track_total_hits: false, body: { aggregations: { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/unique_ips/query.hosts_kpi_unique_ips.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/unique_ips/query.hosts_kpi_unique_ips.dsl.ts index 0471644d11bbe..c875e23b523e5 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/unique_ips/query.hosts_kpi_unique_ips.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/unique_ips/query.hosts_kpi_unique_ips.dsl.ts @@ -28,8 +28,8 @@ export const buildHostsKpiUniqueIpsQuery = ({ const dslQuery = { index: defaultIndex, - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, track_total_hits: false, body: { aggregations: { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/unique_ips/query.hosts_kpi_unique_ips_entities.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/unique_ips/query.hosts_kpi_unique_ips_entities.dsl.ts index 2a55c34238d70..e323de78a0459 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/unique_ips/query.hosts_kpi_unique_ips_entities.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/unique_ips/query.hosts_kpi_unique_ips_entities.dsl.ts @@ -28,8 +28,8 @@ export const buildHostsKpiUniqueIpsQueryEntities = ({ const dslQuery = { index: defaultIndex, - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, track_total_hits: false, body: { aggregations: { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/last_first_seen/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/last_first_seen/__mocks__/index.ts index 443e7e96a3c7f..54403f9c392e7 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/last_first_seen/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/last_first_seen/__mocks__/index.ts @@ -124,7 +124,7 @@ export const formattedSearchStrategyFirstResponse = { dsl: [ JSON.stringify( { - allowNoIndices: true, + allow_no_indices: true, index: [ 'apm-*-transaction*', 'traces-apm*', @@ -135,7 +135,7 @@ export const formattedSearchStrategyFirstResponse = { 'packetbeat-*', 'winlogbeat-*', ], - ignoreUnavailable: true, + ignore_unavailable: true, track_total_hits: false, body: { query: { bool: { filter: [{ term: { 'host.name': 'siem-kibana' } }] } }, @@ -190,7 +190,7 @@ export const formattedSearchStrategyLastResponse = { dsl: [ JSON.stringify( { - allowNoIndices: true, + allow_no_indices: true, index: [ 'apm-*-transaction*', 'traces-apm*', @@ -201,7 +201,7 @@ export const formattedSearchStrategyLastResponse = { 'packetbeat-*', 'winlogbeat-*', ], - ignoreUnavailable: true, + ignore_unavailable: true, track_total_hits: false, body: { query: { bool: { filter: [{ term: { 'host.name': 'siem-kibana' } }] } }, @@ -225,7 +225,7 @@ export const formattedSearchStrategyLastResponse = { }; export const expectedDsl = { - allowNoIndices: true, + allow_no_indices: true, index: [ 'apm-*-transaction*', 'traces-apm*', @@ -236,7 +236,7 @@ export const expectedDsl = { 'packetbeat-*', 'winlogbeat-*', ], - ignoreUnavailable: true, + ignore_unavailable: true, track_total_hits: false, body: { _source: ['@timestamp'], diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/last_first_seen/query.first_or_last_seen_host.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/last_first_seen/query.first_or_last_seen_host.dsl.ts index 21876b9aad11b..8f9f7462b9dcb 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/last_first_seen/query.first_or_last_seen_host.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/last_first_seen/query.first_or_last_seen_host.dsl.ts @@ -17,9 +17,9 @@ export const buildFirstOrLastSeenHostQuery = ({ const filter = [{ term: { 'host.name': hostName } }]; const dslQuery = { - allowNoIndices: true, + allow_no_indices: true, index: defaultIndex, - ignoreUnavailable: true, + ignore_unavailable: true, track_total_hits: false, body: { ...(!isEmpty(docValueFields) ? { docvalue_fields: docValueFields } : {}), diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/overview/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/overview/__mocks__/index.ts index 2b4e4b8291401..85bce797f52ae 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/overview/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/overview/__mocks__/index.ts @@ -117,7 +117,7 @@ export const formattedSearchStrategyResponse = { dsl: [ JSON.stringify( { - allowNoIndices: true, + allow_no_indices: true, index: [ 'apm-*-transaction*', 'traces-apm*', @@ -128,7 +128,7 @@ export const formattedSearchStrategyResponse = { 'packetbeat-*', 'winlogbeat-*', ], - ignoreUnavailable: true, + ignore_unavailable: true, track_total_hits: false, body: { aggregations: { @@ -330,7 +330,7 @@ export const formattedSearchStrategyResponse = { }; export const expectedDsl = { - allowNoIndices: true, + allow_no_indices: true, index: [ 'apm-*-transaction*', 'traces-apm*', @@ -341,7 +341,7 @@ export const expectedDsl = { 'packetbeat-*', 'winlogbeat-*', ], - ignoreUnavailable: true, + ignore_unavailable: true, track_total_hits: false, body: { aggregations: { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/overview/query.overview_host.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/overview/query.overview_host.dsl.ts index a0ae368ac0fe2..92d194e78284b 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/overview/query.overview_host.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/overview/query.overview_host.dsl.ts @@ -28,9 +28,9 @@ export const buildOverviewHostQuery = ({ ]; const dslQuery = { - allowNoIndices: true, + allow_no_indices: true, index: defaultIndex, - ignoreUnavailable: true, + ignore_unavailable: true, track_total_hits: false, body: { aggregations: { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/risk_score/query.hosts_risk.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/risk_score/query.hosts_risk.dsl.ts index 43930ab3de2ef..5bbc9b7726002 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/risk_score/query.hosts_risk.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/risk_score/query.hosts_risk.dsl.ts @@ -32,8 +32,8 @@ export const buildHostsRiskScoreQuery = ({ const dslQuery = { index: defaultIndex, - allowNoIndices: false, - ignoreUnavailable: true, + allow_no_indices: false, + ignore_unavailable: true, track_total_hits: false, body: { query: { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/uncommon_processes/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/uncommon_processes/__mocks__/index.ts index 0ad976a0f498c..2730465323c19 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/uncommon_processes/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/uncommon_processes/__mocks__/index.ts @@ -4300,7 +4300,7 @@ export const formattedSearchStrategyResponse = { dsl: [ JSON.stringify( { - allowNoIndices: true, + allow_no_indices: true, index: [ 'apm-*-transaction*', 'traces-apm*', @@ -4311,7 +4311,7 @@ export const formattedSearchStrategyResponse = { 'packetbeat-*', 'winlogbeat-*', ], - ignoreUnavailable: true, + ignore_unavailable: true, body: { aggregations: { process_count: { cardinality: { field: 'process.name' } }, @@ -4435,7 +4435,7 @@ export const formattedSearchStrategyResponse = { }; export const expectedDsl = { - allowNoIndices: true, + allow_no_indices: true, index: [ 'apm-*-transaction*', 'traces-apm*', @@ -4446,7 +4446,7 @@ export const expectedDsl = { 'packetbeat-*', 'winlogbeat-*', ], - ignoreUnavailable: true, + ignore_unavailable: true, body: { aggregations: { process_count: { cardinality: { field: 'process.name' } }, diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/uncommon_processes/dsl/query.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/uncommon_processes/dsl/query.dsl.ts index 5d4f45c68160a..c5a78354ed866 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/uncommon_processes/dsl/query.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/uncommon_processes/dsl/query.dsl.ts @@ -48,9 +48,9 @@ export const buildQuery = ({ }; const dslQuery = { - allowNoIndices: true, + allow_no_indices: true, index: defaultIndex, - ignoreUnavailable: true, + ignore_unavailable: true, body: { aggregations: { ...agg, diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/__mocks__/index.ts index 7f36e3551e5be..d5478dad15d50 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/__mocks__/index.ts @@ -41,8 +41,8 @@ export const formattedAlertsSearchStrategyResponse: MatrixHistogramStrategyRespo 'packetbeat-*', 'winlogbeat-*', ], - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, track_total_hits: true, body: { aggregations: { @@ -127,7 +127,7 @@ export const formattedAlertsSearchStrategyResponse: MatrixHistogramStrategyRespo }; export const expectedDsl = { - allowNoIndices: true, + allow_no_indices: true, track_total_hits: false, body: { aggregations: { @@ -164,7 +164,7 @@ export const expectedDsl = { }, size: 0, }, - ignoreUnavailable: true, + ignore_unavailable: true, index: [ 'apm-*-transaction*', 'traces-apm*', @@ -209,8 +209,8 @@ export const formattedAnomaliesSearchStrategyResponse: MatrixHistogramStrategyRe 'packetbeat-*', 'winlogbeat-*', ], - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, track_total_hits: true, body: { aggs: { @@ -392,8 +392,8 @@ export const formattedAuthenticationsSearchStrategyResponse: MatrixHistogramStra 'packetbeat-*', 'winlogbeat-*', ], - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, track_total_hits: true, body: { aggregations: { @@ -959,8 +959,8 @@ export const formattedEventsSearchStrategyResponse: MatrixHistogramStrategyRespo 'packetbeat-*', 'winlogbeat-*', ], - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, track_total_hits: true, body: { aggregations: { @@ -1927,7 +1927,7 @@ export const formattedDnsSearchStrategyResponse: MatrixHistogramStrategyResponse dsl: [ JSON.stringify( { - allowNoIndices: true, + allow_no_indices: true, index: [ 'apm-*-transaction*', 'traces-apm*', @@ -1938,7 +1938,7 @@ export const formattedDnsSearchStrategyResponse: MatrixHistogramStrategyResponse 'packetbeat-*', 'winlogbeat-*', ], - ignoreUnavailable: true, + ignore_unavailable: true, body: { aggregations: { dns_count: { cardinality: { field: 'dns.question.registered_domain' } }, diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/__mocks__/index.ts index 82531f35b09ab..4f6b910fbccf7 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/__mocks__/index.ts @@ -36,8 +36,8 @@ export const expectedDsl = { 'packetbeat-*', 'winlogbeat-*', ], - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, track_total_hits: true, body: { aggregations: { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/query.alerts_histogram.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/query.alerts_histogram.dsl.ts index 54ee066b64119..60df6023a13d0 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/query.alerts_histogram.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/query.alerts_histogram.dsl.ts @@ -83,8 +83,8 @@ export const buildAlertsHistogramQuery = ({ const dslQuery = { index: defaultIndex, - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, track_total_hits: true, body: { aggregations: getHistogramAggregation(), diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/__mocks__/index.ts index ab76d54dee11f..700580655f1b0 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/__mocks__/index.ts @@ -36,8 +36,8 @@ export const expectedDsl = { 'packetbeat-*', 'winlogbeat-*', ], - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, track_total_hits: true, body: { aggs: { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/query.anomalies_histogram.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/query.anomalies_histogram.dsl.ts index 78fc0a30d0477..b82e7823fd847 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/query.anomalies_histogram.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/query.anomalies_histogram.dsl.ts @@ -64,8 +64,8 @@ export const buildAnomaliesHistogramQuery = ({ const dslQuery = { index: defaultIndex, - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, track_total_hits: true, body: { aggs: getHistogramAggregation(), diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/__mocks__/index.ts index 1fd7b85242df6..b917da12c9ad7 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/__mocks__/index.ts @@ -35,8 +35,8 @@ export const expectedDsl = { 'packetbeat-*', 'winlogbeat-*', ], - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, track_total_hits: true, body: { aggregations: { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/query.authentications_histogram.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/query.authentications_histogram.dsl.ts index 8661fff574b4a..b16efcd8301e0 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/query.authentications_histogram.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/query.authentications_histogram.dsl.ts @@ -76,8 +76,8 @@ export const buildAuthenticationsHistogramQuery = ({ const dslQuery = { index: defaultIndex, - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, track_total_hits: true, body: { aggregations: getHistogramAggregation(), diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/query.authentications_histogram_entities.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/query.authentications_histogram_entities.dsl.ts index c66a0d6c11b94..886dcf11123bd 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/query.authentications_histogram_entities.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/query.authentications_histogram_entities.dsl.ts @@ -59,8 +59,8 @@ export const buildAuthenticationsHistogramQueryEntities = ({ const dslQuery = { index: defaultIndex, - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, track_total_hits: true, body: { aggregations: getHistogramAggregation(), diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/__mocks__/index.ts index 4d97fba3cb80c..d4e721a5ebe80 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/__mocks__/index.ts @@ -26,7 +26,7 @@ export const mockOptions = { }; export const expectedDsl = { - allowNoIndices: true, + allow_no_indices: true, index: [ 'apm-*-transaction*', 'traces-apm*', @@ -37,7 +37,7 @@ export const expectedDsl = { 'packetbeat-*', 'winlogbeat-*', ], - ignoreUnavailable: true, + ignore_unavailable: true, body: { aggregations: { dns_count: { cardinality: { field: 'dns.question.registered_domain' } }, diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/query.dns_histogram.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/query.dns_histogram.dsl.ts index d9dfc57a264a8..7a7b4b49d17c1 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/query.dns_histogram.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/query.dns_histogram.dsl.ts @@ -77,9 +77,9 @@ export const buildDnsHistogramQuery = ({ ]; const dslQuery = { - allowNoIndices: true, + allow_no_indices: true, index: defaultIndex, - ignoreUnavailable: true, + ignore_unavailable: true, body: { ...(!isEmpty(docValueFields) ? { docvalue_fields: docValueFields } : {}), aggregations: { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/__mocks__/index.ts index 5dab2bcd5cf9d..d2ee38ddd66a8 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/__mocks__/index.ts @@ -40,8 +40,8 @@ export const expectedDsl = { 'packetbeat-*', 'winlogbeat-*', ], - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, track_total_hits: true, body: { aggregations: { @@ -95,8 +95,8 @@ export const expectedThresholdDsl = { 'packetbeat-*', 'winlogbeat-*', ], - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, track_total_hits: true, body: { aggregations: { @@ -152,8 +152,8 @@ export const expectedThresholdMissingFieldDsl = { 'packetbeat-*', 'winlogbeat-*', ], - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, track_total_hits: true, body: { aggregations: { @@ -197,7 +197,7 @@ export const expectedThresholdMissingFieldDsl = { }; export const expectedThresholdWithCardinalityDsl = { - allowNoIndices: true, + allow_no_indices: true, body: { aggregations: { eventActionGroup: { @@ -244,7 +244,7 @@ export const expectedThresholdWithCardinalityDsl = { }, size: 0, }, - ignoreUnavailable: true, + ignore_unavailable: true, index: [ 'apm-*-transaction*', 'traces-apm*', @@ -269,8 +269,8 @@ export const expectedThresholdWithGroupFieldsAndCardinalityDsl = { 'packetbeat-*', 'winlogbeat-*', ], - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, track_total_hits: true, body: { aggregations: { @@ -316,7 +316,7 @@ export const expectedThresholdWithGroupFieldsAndCardinalityDsl = { }; export const expectedThresholdGroupWithCardinalityDsl = { - allowNoIndices: true, + allow_no_indices: true, body: { aggregations: { eventActionGroup: { @@ -365,7 +365,7 @@ export const expectedThresholdGroupWithCardinalityDsl = { }, size: 0, }, - ignoreUnavailable: true, + ignore_unavailable: true, index: [ 'apm-*-transaction*', 'traces-apm*', @@ -390,8 +390,8 @@ export const expectedIpIncludingMissingDataDsl = { 'packetbeat-*', 'winlogbeat-*', ], - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, track_total_hits: true, body: { aggregations: { @@ -453,8 +453,8 @@ export const expectedIpNotIncludingMissingDataDsl = { 'packetbeat-*', 'winlogbeat-*', ], - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, track_total_hits: true, body: { aggregations: { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/query.events_histogram.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/query.events_histogram.dsl.ts index 16bcb3cdbfcb1..91aeb50448d4e 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/query.events_histogram.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/query.events_histogram.dsl.ts @@ -152,8 +152,8 @@ export const buildEventsHistogramQuery = ({ const dslQuery = { index: defaultIndex, - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, track_total_hits: true, body: { aggregations: getHistogramAggregation(), diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/details/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/details/__mocks__/index.ts index 7f71906bcaa97..158b63e6e8455 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/details/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/details/__mocks__/index.ts @@ -304,7 +304,7 @@ export const formattedSearchStrategyResponse = { dsl: [ JSON.stringify( { - allowNoIndices: true, + allow_no_indices: true, index: [ 'apm-*-transaction*', 'traces-apm*', @@ -315,7 +315,7 @@ export const formattedSearchStrategyResponse = { 'packetbeat-*', 'winlogbeat-*', ], - ignoreUnavailable: true, + ignore_unavailable: true, track_total_hits: false, body: { docvalue_fields: mockOptions.docValueFields, @@ -446,7 +446,7 @@ export const formattedSearchStrategyResponse = { }; export const expectedDsl = { - allowNoIndices: true, + allow_no_indices: true, index: [ 'apm-*-transaction*', 'traces-apm*', @@ -457,7 +457,7 @@ export const expectedDsl = { 'packetbeat-*', 'winlogbeat-*', ], - ignoreUnavailable: true, + ignore_unavailable: true, track_total_hits: false, body: { aggs: { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/details/query.details_network.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/details/query.details_network.dsl.ts index e5a508663a2e0..5684c7685231e 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/details/query.details_network.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/details/query.details_network.dsl.ts @@ -103,9 +103,9 @@ export const buildNetworkDetailsQuery = ({ ip, }: NetworkDetailsRequestOptions) => { const dslQuery = { - allowNoIndices: true, + allow_no_indices: true, index: defaultIndex, - ignoreUnavailable: true, + ignore_unavailable: true, track_total_hits: false, body: { ...(!isEmpty(docValueFields) ? { docvalue_fields: docValueFields } : {}), diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/dns/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/dns/__mocks__/index.ts index cc01450e5bec5..f1bae35f53ebb 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/dns/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/dns/__mocks__/index.ts @@ -131,7 +131,7 @@ export const formattedSearchStrategyResponse = { dsl: [ JSON.stringify( { - allowNoIndices: true, + allow_no_indices: true, index: [ 'apm-*-transaction*', 'traces-apm*', @@ -142,7 +142,7 @@ export const formattedSearchStrategyResponse = { 'packetbeat-*', 'winlogbeat-*', ], - ignoreUnavailable: true, + ignore_unavailable: true, body: { aggregations: { dns_count: { cardinality: { field: 'dns.question.registered_domain' } }, @@ -203,7 +203,7 @@ export const formattedSearchStrategyResponse = { }; export const expectedDsl = { - allowNoIndices: true, + allow_no_indices: true, index: [ 'apm-*-transaction*', 'traces-apm*', @@ -214,7 +214,7 @@ export const expectedDsl = { 'packetbeat-*', 'winlogbeat-*', ], - ignoreUnavailable: true, + ignore_unavailable: true, body: { aggregations: { dns_count: { cardinality: { field: 'dns.question.registered_domain' } }, diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/dns/query.dns_network.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/dns/query.dns_network.dsl.ts index 90e8ee02ee684..612d83e81660d 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/dns/query.dns_network.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/dns/query.dns_network.dsl.ts @@ -88,9 +88,9 @@ export const buildDnsQuery = ({ ]; const dslQuery = { - allowNoIndices: true, + allow_no_indices: true, index: defaultIndex, - ignoreUnavailable: true, + ignore_unavailable: true, body: { ...(!isEmpty(docValueFields) ? { docvalue_fields: docValueFields } : {}), aggregations: { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/http/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/http/__mocks__/index.ts index 9d52eb638eac6..f831b9f20e8ca 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/http/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/http/__mocks__/index.ts @@ -613,7 +613,7 @@ export const formattedSearchStrategyResponse = { dsl: [ JSON.stringify( { - allowNoIndices: true, + allow_no_indices: true, index: [ 'apm-*-transaction*', 'traces-apm*', @@ -624,7 +624,7 @@ export const formattedSearchStrategyResponse = { 'packetbeat-*', 'winlogbeat-*', ], - ignoreUnavailable: true, + ignore_unavailable: true, body: { aggregations: { http_count: { cardinality: { field: 'url.path' } }, @@ -671,7 +671,7 @@ export const formattedSearchStrategyResponse = { }; export const expectedDsl = { - allowNoIndices: true, + allow_no_indices: true, index: [ 'apm-*-transaction*', 'traces-apm*', @@ -682,7 +682,7 @@ export const expectedDsl = { 'packetbeat-*', 'winlogbeat-*', ], - ignoreUnavailable: true, + ignore_unavailable: true, body: { aggregations: { http_count: { cardinality: { field: 'url.path' } }, diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/http/query.http_network.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/http/query.http_network.dsl.ts index 85db56cd72167..8882d17804261 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/http/query.http_network.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/http/query.http_network.dsl.ts @@ -36,9 +36,9 @@ export const buildHttpQuery = ({ ]; const dslQuery = { - allowNoIndices: true, + allow_no_indices: true, index: defaultIndex, - ignoreUnavailable: true, + ignore_unavailable: true, body: { aggregations: { ...getCountAgg(), diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/dns/query.network_kip_dns_entities.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/dns/query.network_kip_dns_entities.dsl.ts index 75b32af4b01f5..c0317772e5fa9 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/dns/query.network_kip_dns_entities.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/dns/query.network_kip_dns_entities.dsl.ts @@ -28,8 +28,8 @@ export const buildDnsQueryEntities = ({ const dslQuery = { index: defaultIndex, - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, track_total_hits: false, body: { aggs: { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/dns/query.network_kpi_dns.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/dns/query.network_kpi_dns.dsl.ts index 1d1aa50cc3ee2..8d27f7d289d03 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/dns/query.network_kpi_dns.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/dns/query.network_kpi_dns.dsl.ts @@ -56,8 +56,8 @@ export const buildDnsQuery = ({ const dslQuery = { index: defaultIndex, - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, track_total_hits: true, body: { query: { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/network_events/query.network_kpi_network_events.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/network_events/query.network_kpi_network_events.dsl.ts index 3d5607c8b443a..4d5ca88fe383a 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/network_events/query.network_kpi_network_events.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/network_events/query.network_kpi_network_events.dsl.ts @@ -30,8 +30,8 @@ export const buildNetworkEventsQuery = ({ const dslQuery = { index: defaultIndex, - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, track_total_hits: true, body: { query: { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/network_events/query.network_kpi_network_events_entities.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/network_events/query.network_kpi_network_events_entities.dsl.ts index 6311bb6ea2039..bb40da0a064fb 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/network_events/query.network_kpi_network_events_entities.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/network_events/query.network_kpi_network_events_entities.dsl.ts @@ -28,8 +28,8 @@ export const buildNetworkEventsQueryEntities = ({ const dslQuery = { index: defaultIndex, - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, track_total_hits: false, body: { aggs: { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/tls_handshakes/query.network_kpi_tls_handshakes.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/tls_handshakes/query.network_kpi_tls_handshakes.dsl.ts index 0a826938e95b8..eae7f7a29ce72 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/tls_handshakes/query.network_kpi_tls_handshakes.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/tls_handshakes/query.network_kpi_tls_handshakes.dsl.ts @@ -56,8 +56,8 @@ export const buildTlsHandshakeQuery = ({ const dslQuery = { index: defaultIndex, - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, track_total_hits: true, body: { query: { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/tls_handshakes/query.network_kpi_tls_handshakes_entities.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/tls_handshakes/query.network_kpi_tls_handshakes_entities.dsl.ts index 5b0ac92b35049..bd7a464e1a090 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/tls_handshakes/query.network_kpi_tls_handshakes_entities.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/tls_handshakes/query.network_kpi_tls_handshakes_entities.dsl.ts @@ -28,8 +28,8 @@ export const buildTlsHandshakeQueryEntities = ({ const dslQuery = { index: defaultIndex, - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, track_total_hits: false, body: { aggs: { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/unique_flows/query.network_kpi_unique_flows.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/unique_flows/query.network_kpi_unique_flows.dsl.ts index ec8de30cfff85..3cb04caf5afe5 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/unique_flows/query.network_kpi_unique_flows.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/unique_flows/query.network_kpi_unique_flows.dsl.ts @@ -30,8 +30,8 @@ export const buildUniqueFlowsQuery = ({ const dslQuery = { index: defaultIndex, - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, track_total_hits: false, body: { aggregations: { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/unique_private_ips/query.network_kpi_unique_private_ips.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/unique_private_ips/query.network_kpi_unique_private_ips.dsl.ts index 590e7117826d7..c915cd4fb58d6 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/unique_private_ips/query.network_kpi_unique_private_ips.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/unique_private_ips/query.network_kpi_unique_private_ips.dsl.ts @@ -84,9 +84,9 @@ export const buildUniquePrivateIpsQuery = ({ ]; const dslQuery = { - allowNoIndices: true, + allow_no_indices: true, index: defaultIndex, - ignoreUnavailable: true, + ignore_unavailable: true, track_total_hits: false, body: { aggregations: { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/unique_private_ips/query.network_kpi_unique_private_ips_entities.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/unique_private_ips/query.network_kpi_unique_private_ips_entities.dsl.ts index a56cf4c3d1ced..922a082439807 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/unique_private_ips/query.network_kpi_unique_private_ips_entities.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/unique_private_ips/query.network_kpi_unique_private_ips_entities.dsl.ts @@ -84,9 +84,9 @@ export const buildUniquePrivateIpsQueryEntities = ({ ]; const dslQuery = { - allowNoIndices: true, + allow_no_indices: true, index: defaultIndex, - ignoreUnavailable: true, + ignore_unavailable: true, track_total_hits: false, body: { aggregations: { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/overview/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/overview/__mocks__/index.ts index 74b201e9a2294..0b5019d6fec9a 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/overview/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/overview/__mocks__/index.ts @@ -101,7 +101,7 @@ export const formattedSearchStrategyResponse = { dsl: [ JSON.stringify( { - allowNoIndices: true, + allow_no_indices: true, index: [ 'apm-*-transaction*', 'traces-apm*', @@ -112,7 +112,7 @@ export const formattedSearchStrategyResponse = { 'packetbeat-*', 'winlogbeat-*', ], - ignoreUnavailable: true, + ignore_unavailable: true, track_total_hits: false, body: { aggregations: { @@ -206,8 +206,8 @@ export const formattedSearchStrategyResponse = { }; export const expectedDsl = { - allowNoIndices: true, - ignoreUnavailable: true, + allow_no_indices: true, + ignore_unavailable: true, index: [ 'apm-*-transaction*', 'traces-apm*', diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/overview/query.overview_network.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/overview/query.overview_network.dsl.ts index 7e35ae2fd4308..f911850c8cd94 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/overview/query.overview_network.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/overview/query.overview_network.dsl.ts @@ -28,9 +28,9 @@ export const buildOverviewNetworkQuery = ({ ]; const dslQuery = { - allowNoIndices: true, + allow_no_indices: true, index: defaultIndex, - ignoreUnavailable: true, + ignore_unavailable: true, track_total_hits: false, body: { aggregations: { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/tls/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/tls/__mocks__/index.ts index e46c2c20aa1a8..c34ec2225ed95 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/tls/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/tls/__mocks__/index.ts @@ -59,7 +59,7 @@ export const formattedSearchStrategyResponse = { dsl: [ JSON.stringify( { - allowNoIndices: true, + allow_no_indices: true, index: [ 'apm-*-transaction*', 'traces-apm*', @@ -70,7 +70,7 @@ export const formattedSearchStrategyResponse = { 'packetbeat-*', 'winlogbeat-*', ], - ignoreUnavailable: true, + ignore_unavailable: true, track_total_hits: false, body: { aggs: { @@ -114,7 +114,7 @@ export const formattedSearchStrategyResponse = { }; export const expectedDsl = { - allowNoIndices: true, + allow_no_indices: true, index: [ 'apm-*-transaction*', 'traces-apm*', @@ -125,7 +125,7 @@ export const expectedDsl = { 'packetbeat-*', 'winlogbeat-*', ], - ignoreUnavailable: true, + ignore_unavailable: true, track_total_hits: false, body: { aggs: { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/tls/query.tls_network.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/tls/query.tls_network.dsl.ts index 4f724cf9b15f9..297643fe56952 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/tls/query.tls_network.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/tls/query.tls_network.dsl.ts @@ -75,9 +75,9 @@ export const buildNetworkTlsQuery = ({ const filter = ip ? [...defaultFilter, { term: { [`${flowTarget}.ip`]: ip } }] : defaultFilter; const dslQuery = { - allowNoIndices: true, + allow_no_indices: true, index: defaultIndex, - ignoreUnavailable: true, + ignore_unavailable: true, track_total_hits: false, body: { aggs: { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/top_countries/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/top_countries/__mocks__/index.ts index ba5db90df8245..490ade26ad2a5 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/top_countries/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/top_countries/__mocks__/index.ts @@ -58,7 +58,7 @@ export const formattedSearchStrategyResponse = { dsl: [ JSON.stringify( { - allowNoIndices: true, + allow_no_indices: true, index: [ 'apm-*-transaction*', 'traces-apm*', @@ -69,7 +69,7 @@ export const formattedSearchStrategyResponse = { 'packetbeat-*', 'winlogbeat-*', ], - ignoreUnavailable: true, + ignore_unavailable: true, body: { aggregations: { top_countries_count: { cardinality: { field: 'destination.geo.country_iso_code' } }, @@ -118,7 +118,7 @@ export const formattedSearchStrategyResponse = { }; export const expectedDsl = { - allowNoIndices: true, + allow_no_indices: true, index: [ 'apm-*-transaction*', 'traces-apm*', @@ -129,7 +129,7 @@ export const expectedDsl = { 'packetbeat-*', 'winlogbeat-*', ], - ignoreUnavailable: true, + ignore_unavailable: true, body: { aggregations: { top_countries_count: { cardinality: { field: 'destination.geo.country_iso_code' } }, diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/top_countries/query.top_countries_network.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/top_countries/query.top_countries_network.dsl.ts index 880cd4002086a..463d3c9b11bd2 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/top_countries/query.top_countries_network.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/top_countries/query.top_countries_network.dsl.ts @@ -42,9 +42,9 @@ export const buildTopCountriesQuery = ({ ]; const dslQuery = { - allowNoIndices: true, + allow_no_indices: true, index: defaultIndex, - ignoreUnavailable: true, + ignore_unavailable: true, body: { aggregations: { ...getCountAgg(flowTarget), diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/top_countries/query.top_countries_network_entities.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/top_countries/query.top_countries_network_entities.dsl.ts index d661bfa0d6707..7f1d27db45d2d 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/top_countries/query.top_countries_network_entities.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/top_countries/query.top_countries_network_entities.dsl.ts @@ -47,9 +47,9 @@ export const buildTopCountriesQueryEntities = ({ ]; const dslQuery = { - allowNoIndices: true, + allow_no_indices: true, index: defaultIndex, - ignoreUnavailable: true, + ignore_unavailable: true, body: { aggregations: { ...getCountAgg(flowTarget), diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/top_n_flow/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/top_n_flow/__mocks__/index.ts index e881a9ef93949..fa759661772d5 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/top_n_flow/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/top_n_flow/__mocks__/index.ts @@ -810,7 +810,7 @@ export const formattedSearchStrategyResponse: NetworkTopNFlowStrategyResponse = dsl: [ JSON.stringify( { - allowNoIndices: true, + allow_no_indices: true, index: [ 'apm-*-transaction*', 'traces-apm*', @@ -821,7 +821,7 @@ export const formattedSearchStrategyResponse: NetworkTopNFlowStrategyResponse = 'packetbeat-*', 'winlogbeat-*', ], - ignoreUnavailable: true, + ignore_unavailable: true, body: { aggregations: { top_n_flow_count: { cardinality: { field: 'source.ip' } }, @@ -878,7 +878,7 @@ export const formattedSearchStrategyResponse: NetworkTopNFlowStrategyResponse = }; export const expectedDsl = { - allowNoIndices: true, + allow_no_indices: true, index: [ 'apm-*-transaction*', 'traces-apm*', @@ -889,7 +889,7 @@ export const expectedDsl = { 'packetbeat-*', 'winlogbeat-*', ], - ignoreUnavailable: true, + ignore_unavailable: true, body: { aggregations: { top_n_flow_count: { cardinality: { field: 'source.ip' } }, diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/top_n_flow/query.top_n_flow_network.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/top_n_flow/query.top_n_flow_network.dsl.ts index f1ac87e59d392..52efb50f00b4b 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/top_n_flow/query.top_n_flow_network.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/top_n_flow/query.top_n_flow_network.dsl.ts @@ -42,9 +42,9 @@ export const buildTopNFlowQuery = ({ ]; const dslQuery = { - allowNoIndices: true, + allow_no_indices: true, index: defaultIndex, - ignoreUnavailable: true, + ignore_unavailable: true, body: { aggregations: { ...getCountAgg(flowTarget), diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/top_n_flow/query.top_n_flow_network_entities.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/top_n_flow/query.top_n_flow_network_entities.dsl.ts index 3ea3c6f363de0..d11a2debf3cff 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/top_n_flow/query.top_n_flow_network_entities.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/top_n_flow/query.top_n_flow_network_entities.dsl.ts @@ -47,9 +47,9 @@ export const buildTopNFlowQueryEntities = ({ ]; const dslQuery = { - allowNoIndices: true, + allow_no_indices: true, index: defaultIndex, - ignoreUnavailable: true, + ignore_unavailable: true, body: { aggregations: { ...getCountAgg(flowTarget), diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/users/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/users/__mocks__/index.ts index 686730dbe7927..acb98b7e347bc 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/users/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/users/__mocks__/index.ts @@ -119,7 +119,7 @@ export const formattedSearchStrategyResponse = { dsl: [ JSON.stringify( { - allowNoIndices: true, + allow_no_indices: true, index: [ 'apm-*-transaction*', 'traces-apm*', @@ -130,7 +130,7 @@ export const formattedSearchStrategyResponse = { 'packetbeat-*', 'winlogbeat-*', ], - ignoreUnavailable: true, + ignore_unavailable: true, track_total_hits: false, body: { aggs: { @@ -175,7 +175,7 @@ export const formattedSearchStrategyResponse = { }; export const expectedDsl = { - allowNoIndices: true, + allow_no_indices: true, track_total_hits: false, body: { aggs: { @@ -209,7 +209,7 @@ export const expectedDsl = { }, size: 0, }, - ignoreUnavailable: true, + ignore_unavailable: true, index: [ 'apm-*-transaction*', 'traces-apm*', diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/users/query.users_network.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/users/query.users_network.dsl.ts index 2b02b25292a32..0c35c967c2ac5 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/users/query.users_network.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/users/query.users_network.dsl.ts @@ -34,9 +34,9 @@ export const buildUsersQuery = ({ ]; const dslQuery = { - allowNoIndices: true, + allow_no_indices: true, index: defaultIndex, - ignoreUnavailable: true, + ignore_unavailable: true, track_total_hits: false, body: { aggs: { diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/host_rules/query.host_rules.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/host_rules/query.host_rules.dsl.ts index 4c116104b3e14..d2aeb63b743f5 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/host_rules/query.host_rules.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/host_rules/query.host_rules.dsl.ts @@ -30,9 +30,9 @@ export const buildHostRulesQuery = ({ ]; return { - allowNoIndices: true, + allow_no_indices: true, index: defaultIndex, // can stop getting this from sourcerer and assume default detections index if we want - ignoreUnavailable: true, + ignore_unavailable: true, track_total_hits: true, body: { ...(!isEmpty(docValueFields) ? { docvalue_fields: docValueFields } : {}), diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/host_tactics/query.host_tactics.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/host_tactics/query.host_tactics.dsl.ts index ec1afe247011b..f9bbcc8077278 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/host_tactics/query.host_tactics.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/host_tactics/query.host_tactics.dsl.ts @@ -30,9 +30,9 @@ export const buildHostTacticsQuery = ({ ]; return { - allowNoIndices: true, + allow_no_indices: true, index: defaultIndex, // can stop getting this from sourcerer and assume default detections index if we want - ignoreUnavailable: true, + ignore_unavailable: true, track_total_hits: true, body: { ...(!isEmpty(docValueFields) ? { docvalue_fields: docValueFields } : {}), diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/risk_score/query.risk_score.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/risk_score/query.risk_score.dsl.ts index 79c50d84e3c92..90d2e51c8ff9e 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/risk_score/query.risk_score.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/risk_score/query.risk_score.dsl.ts @@ -31,9 +31,9 @@ export const buildRiskScoreQuery = ({ ]; return { - allowNoIndices: true, + allow_no_indices: true, index: defaultIndex, - ignoreUnavailable: true, + ignore_unavailable: true, track_total_hits: true, body: { ...(!isEmpty(docValueFields) ? { docvalue_fields: docValueFields } : {}), diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/user_rules/query.user_rules.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/user_rules/query.user_rules.dsl.ts index c2242ff00a6c1..d3111eed4aef8 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/user_rules/query.user_rules.dsl.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/ueba/user_rules/query.user_rules.dsl.ts @@ -30,9 +30,9 @@ export const buildUserRulesQuery = ({ ]; return { - allowNoIndices: true, + allow_no_indices: true, index: defaultIndex, // can stop getting this from sourcerer and assume default detections index if we want - ignoreUnavailable: true, + ignore_unavailable: true, track_total_hits: true, body: { ...(!isEmpty(docValueFields) ? { docvalue_fields: docValueFields } : {}), diff --git a/x-pack/plugins/security_solution/server/usage/detections/detection_rule_helpers.ts b/x-pack/plugins/security_solution/server/usage/detections/detection_rule_helpers.ts index 8d5a2efc7fae1..eaeceb8ab57ee 100644 --- a/x-pack/plugins/security_solution/server/usage/detections/detection_rule_helpers.ts +++ b/x-pack/plugins/security_solution/server/usage/detections/detection_rule_helpers.ts @@ -188,8 +188,8 @@ export const getDetectionRuleMetrics = async ( let rulesUsage: DetectionRulesTypeUsage = initialDetectionRulesUsage; const ruleSearchOptions: RuleSearchParams = { body: { query: { bool: { filter: { term: { 'alert.alertTypeId': SIGNALS_ID } } } } }, - filterPath: [], - ignoreUnavailable: true, + filter_path: [], + ignore_unavailable: true, index: kibanaIndex, size: MAX_RESULTS_WINDOW, }; diff --git a/x-pack/plugins/security_solution/server/usage/detections/types.ts b/x-pack/plugins/security_solution/server/usage/detections/types.ts index 0e3ba97ca0f7c..430a524f3f03a 100644 --- a/x-pack/plugins/security_solution/server/usage/detections/types.ts +++ b/x-pack/plugins/security_solution/server/usage/detections/types.ts @@ -17,8 +17,8 @@ interface RuleSearchBody { export interface RuleSearchParams { body: RuleSearchBody; - filterPath: string[]; - ignoreUnavailable: boolean; + filter_path: string[]; + ignore_unavailable: boolean; index: string; size: number; } diff --git a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/all/query.events_all.dsl.ts b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/all/query.events_all.dsl.ts index 72a7d6e2692e8..3653cb60d3653 100644 --- a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/all/query.events_all.dsl.ts +++ b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/all/query.events_all.dsl.ts @@ -63,9 +63,9 @@ export const buildTimelineEventsAllQuery = ({ }); const dslQuery = { - allowNoIndices: true, + allow_no_indices: true, index: defaultIndex, - ignoreUnavailable: true, + ignore_unavailable: true, body: { ...(!isEmpty(docValueFields) ? { docvalue_fields: docValueFields } : {}), aggregations: { diff --git a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/details/query.events_details.dsl.test.ts b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/details/query.events_details.dsl.test.ts index 3572a2b9c497e..9066861cdb818 100644 --- a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/details/query.events_details.dsl.test.ts +++ b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/details/query.events_details.dsl.test.ts @@ -22,7 +22,7 @@ describe('buildTimelineDetailsQuery', () => { expect(query).toMatchInlineSnapshot(` Object { - "allowNoIndices": true, + "allow_no_indices": true, "body": Object { "_source": true, "docvalue_fields": Array [ @@ -53,7 +53,7 @@ describe('buildTimelineDetailsQuery', () => { }, }, }, - "ignoreUnavailable": true, + "ignore_unavailable": true, "index": ".siem-signals-default", "size": 1, } diff --git a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/details/query.events_details.dsl.ts b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/details/query.events_details.dsl.ts index 4297cd595e261..70b592440468e 100644 --- a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/details/query.events_details.dsl.ts +++ b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/details/query.events_details.dsl.ts @@ -33,9 +33,9 @@ export const buildTimelineDetailsQuery = ( }; return { - allowNoIndices: true, + allow_no_indices: true, index: indexName, - ignoreUnavailable: true, + ignore_unavailable: true, body: { docvalue_fields: docValueFields, query, diff --git a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/kpi/query.kpi.dsl.ts b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/kpi/query.kpi.dsl.ts index 41eed7cbb4fa3..dd87ef177bfe6 100644 --- a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/kpi/query.kpi.dsl.ts +++ b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/kpi/query.kpi.dsl.ts @@ -44,9 +44,9 @@ export const buildTimelineKpiQuery = ({ const filter = [...filterClause, ...getTimerangeFilter(timerange), { match_all: {} }]; const dslQuery = { - allowNoIndices: true, + allow_no_indices: true, index: defaultIndex, - ignoreUnavailable: true, + ignore_unavailable: true, body: { aggs: { userCount: { diff --git a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/last_event_time/query.events_last_event_time.dsl.ts b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/last_event_time/query.events_last_event_time.dsl.ts index 354c682377bac..4ff86c9a9f290 100644 --- a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/last_event_time/query.events_last_event_time.dsl.ts +++ b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/last_event_time/query.events_last_event_time.dsl.ts @@ -38,9 +38,9 @@ export const buildLastEventTimeQuery = ({ case LastEventIndexKey.ipDetails: if (details.ip) { return { - allowNoIndices: true, + allow_no_indices: true, index: indicesToQuery.network, - ignoreUnavailable: true, + ignore_unavailable: true, track_total_hits: false, body: { ...(!isEmpty(docValueFields) ? { docvalue_fields: docValueFields } : {}), @@ -61,9 +61,9 @@ export const buildLastEventTimeQuery = ({ case LastEventIndexKey.hostDetails: if (details.hostName) { return { - allowNoIndices: true, + allow_no_indices: true, index: indicesToQuery.hosts, - ignoreUnavailable: true, + ignore_unavailable: true, track_total_hits: false, body: { ...(!isEmpty(docValueFields) ? { docvalue_fields: docValueFields } : {}), @@ -85,9 +85,9 @@ export const buildLastEventTimeQuery = ({ case LastEventIndexKey.network: case LastEventIndexKey.ueba: return { - allowNoIndices: true, + allow_no_indices: true, index: indicesToQuery[indexKey], - ignoreUnavailable: true, + ignore_unavailable: true, track_total_hits: false, body: { ...(!isEmpty(docValueFields) ? { docvalue_fields: docValueFields } : {}), From a7a1e5436b92bb08db262b59de7d982da6afac67 Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Wed, 20 Oct 2021 17:03:44 +0200 Subject: [PATCH 131/204] Fix save session UI appears in report (#115746) --- x-pack/plugins/data_enhanced/kibana.json | 10 +++- x-pack/plugins/data_enhanced/public/plugin.ts | 3 + ...onnected_search_session_indicator.test.tsx | 56 +++++++++++++++++++ .../connected_search_session_indicator.tsx | 3 + .../search_session_tour.tsx | 3 + x-pack/plugins/data_enhanced/tsconfig.json | 1 + 6 files changed, 75 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/data_enhanced/kibana.json b/x-pack/plugins/data_enhanced/kibana.json index d678921e9ac7b..d89e76013ebd4 100644 --- a/x-pack/plugins/data_enhanced/kibana.json +++ b/x-pack/plugins/data_enhanced/kibana.json @@ -8,7 +8,15 @@ "githubTeam": "kibana-app-services" }, "configPath": ["xpack", "data_enhanced"], - "requiredPlugins": ["bfetch", "data", "features", "management", "share", "taskManager"], + "requiredPlugins": [ + "bfetch", + "data", + "features", + "management", + "share", + "taskManager", + "screenshotMode" + ], "optionalPlugins": ["kibanaUtils", "usageCollection", "security"], "server": true, "ui": true, diff --git a/x-pack/plugins/data_enhanced/public/plugin.ts b/x-pack/plugins/data_enhanced/public/plugin.ts index f26c1e8d0b62b..6ec645c932e05 100644 --- a/x-pack/plugins/data_enhanced/public/plugin.ts +++ b/x-pack/plugins/data_enhanced/public/plugin.ts @@ -22,6 +22,7 @@ import { toMountPoint } from '../../../../src/plugins/kibana_react/public'; import { createConnectedSearchSessionIndicator } from './search'; import { ConfigSchema } from '../config'; import { Storage } from '../../../../src/plugins/kibana_utils/public'; +import { ScreenshotModePluginStart } from '../../../../src/plugins/screenshot_mode/public'; export interface DataEnhancedSetupDependencies { bfetch: BfetchPublicSetup; @@ -31,6 +32,7 @@ export interface DataEnhancedSetupDependencies { export interface DataEnhancedStartDependencies { data: DataPublicPluginStart; share: SharePluginStart; + screenshotMode: ScreenshotModePluginStart; } export type DataEnhancedSetup = ReturnType; @@ -77,6 +79,7 @@ export class DataEnhancedPlugin .duration(this.config.search.sessions.notTouchedTimeout) .asMilliseconds(), usageCollector: this.usageCollector, + tourDisabled: plugins.screenshotMode.isScreenshotMode(), }) ) ), diff --git a/x-pack/plugins/data_enhanced/public/search/ui/connected_search_session_indicator/connected_search_session_indicator.test.tsx b/x-pack/plugins/data_enhanced/public/search/ui/connected_search_session_indicator/connected_search_session_indicator.test.tsx index 893f352b5d828..2fcdd8a7a6745 100644 --- a/x-pack/plugins/data_enhanced/public/search/ui/connected_search_session_indicator/connected_search_session_indicator.test.tsx +++ b/x-pack/plugins/data_enhanced/public/search/ui/connected_search_session_indicator/connected_search_session_indicator.test.tsx @@ -39,6 +39,7 @@ timeFilter.getRefreshIntervalUpdate$.mockImplementation(() => refreshInterval$); timeFilter.getRefreshInterval.mockImplementation(() => refreshInterval$.getValue()); const disableSaveAfterSessionCompletesTimeout = 5 * 60 * 1000; +const tourDisabled = false; function Container({ children }: { children?: ReactNode }) { return {children}; @@ -64,6 +65,7 @@ test("shouldn't show indicator in case no active search session", async () => { disableSaveAfterSessionCompletesTimeout, usageCollector, basePath, + tourDisabled, }); const { getByTestId, container } = render( @@ -92,6 +94,7 @@ test("shouldn't show indicator in case app hasn't opt-in", async () => { disableSaveAfterSessionCompletesTimeout, usageCollector, basePath, + tourDisabled, }); const { getByTestId, container } = render( @@ -122,6 +125,7 @@ test('should show indicator in case there is an active search session', async () disableSaveAfterSessionCompletesTimeout, usageCollector, basePath, + tourDisabled, }); const { getByTestId } = render( @@ -147,6 +151,7 @@ test('should be disabled in case uiConfig says so ', async () => { disableSaveAfterSessionCompletesTimeout, usageCollector, basePath, + tourDisabled, }); render( @@ -170,6 +175,7 @@ test('should be disabled in case not enough permissions', async () => { storage, disableSaveAfterSessionCompletesTimeout, basePath, + tourDisabled, }); render( @@ -203,6 +209,7 @@ describe('Completed inactivity', () => { disableSaveAfterSessionCompletesTimeout, usageCollector, basePath, + tourDisabled, }); render( @@ -264,6 +271,7 @@ describe('tour steps', () => { disableSaveAfterSessionCompletesTimeout, usageCollector, basePath, + tourDisabled, }); const rendered = render( @@ -305,6 +313,7 @@ describe('tour steps', () => { disableSaveAfterSessionCompletesTimeout, usageCollector, basePath, + tourDisabled, }); const rendered = render( @@ -329,6 +338,51 @@ describe('tour steps', () => { expect(usageCollector.trackSessionIndicatorTourLoading).toHaveBeenCalledTimes(0); expect(usageCollector.trackSessionIndicatorTourRestored).toHaveBeenCalledTimes(0); }); + + test("doesn't show tour step on slow loading when tour is disabled", async () => { + const state$ = new BehaviorSubject(SearchSessionState.Loading); + const SearchSessionIndicator = createConnectedSearchSessionIndicator({ + sessionService: { ...sessionService, state$ }, + application, + storage, + disableSaveAfterSessionCompletesTimeout, + usageCollector, + basePath, + tourDisabled: true, + }); + const rendered = render( + + + + ); + + await waitFor(() => rendered.getByTestId('searchSessionIndicator')); + + expect(() => screen.getByTestId('searchSessionIndicatorPopoverContainer')).toThrow(); + + act(() => { + jest.advanceTimersByTime(10001); + }); + + expect( + screen.queryByTestId('searchSessionIndicatorPopoverContainer') + ).not.toBeInTheDocument(); + + act(() => { + jest.advanceTimersByTime(5000); + state$.next(SearchSessionState.Completed); + }); + + expect( + screen.queryByTestId('searchSessionIndicatorPopoverContainer') + ).not.toBeInTheDocument(); + + expect(storage.get(TOUR_RESTORE_STEP_KEY)).toBeFalsy(); + expect(storage.get(TOUR_TAKING_TOO_LONG_STEP_KEY)).toBeFalsy(); + + expect(usageCollector.trackSessionIndicatorTourLoading).toHaveBeenCalledTimes(0); + expect(usageCollector.trackSessionIndicatorTourRestored).toHaveBeenCalledTimes(0); + }); }); test('shows tour step for restored', async () => { @@ -340,6 +394,7 @@ describe('tour steps', () => { disableSaveAfterSessionCompletesTimeout, usageCollector, basePath, + tourDisabled, }); const rendered = render( @@ -367,6 +422,7 @@ describe('tour steps', () => { disableSaveAfterSessionCompletesTimeout, usageCollector, basePath, + tourDisabled, }); const rendered = render( diff --git a/x-pack/plugins/data_enhanced/public/search/ui/connected_search_session_indicator/connected_search_session_indicator.tsx b/x-pack/plugins/data_enhanced/public/search/ui/connected_search_session_indicator/connected_search_session_indicator.tsx index eed85a9d84ba8..f1ce93181f1ac 100644 --- a/x-pack/plugins/data_enhanced/public/search/ui/connected_search_session_indicator/connected_search_session_indicator.tsx +++ b/x-pack/plugins/data_enhanced/public/search/ui/connected_search_session_indicator/connected_search_session_indicator.tsx @@ -31,6 +31,7 @@ export interface SearchSessionIndicatorDeps { * after the last search in the session has completed */ disableSaveAfterSessionCompletesTimeout: number; + tourDisabled: boolean; usageCollector?: SearchUsageCollector; } @@ -41,6 +42,7 @@ export const createConnectedSearchSessionIndicator = ({ disableSaveAfterSessionCompletesTimeout, usageCollector, basePath, + tourDisabled, }: SearchSessionIndicatorDeps): React.FC => { const searchSessionsManagementUrl = basePath.prepend('/app/management/kibana/search_sessions'); @@ -113,6 +115,7 @@ export const createConnectedSearchSessionIndicator = ({ searchSessionIndicator, state, saveDisabled, + tourDisabled, usageCollector ); diff --git a/x-pack/plugins/data_enhanced/public/search/ui/connected_search_session_indicator/search_session_tour.tsx b/x-pack/plugins/data_enhanced/public/search/ui/connected_search_session_indicator/search_session_tour.tsx index 1568d54962eca..50ee3737ad92b 100644 --- a/x-pack/plugins/data_enhanced/public/search/ui/connected_search_session_indicator/search_session_tour.tsx +++ b/x-pack/plugins/data_enhanced/public/search/ui/connected_search_session_indicator/search_session_tour.tsx @@ -23,6 +23,7 @@ export function useSearchSessionTour( searchSessionIndicatorRef: SearchSessionIndicatorRef | null, state: SearchSessionState, searchSessionsDisabled: boolean, + disableSearchSessionsTour: boolean, usageCollector?: SearchUsageCollector ) { const markOpenedDone = useCallback(() => { @@ -55,6 +56,7 @@ export function useSearchSessionTour( useEffect(() => { if (searchSessionsDisabled) return; + if (disableSearchSessionsTour) return; if (!searchSessionIndicatorRef) return; let timeoutHandle: number; @@ -82,6 +84,7 @@ export function useSearchSessionTour( searchSessionIndicatorRef, state, searchSessionsDisabled, + disableSearchSessionsTour, markOpenedDone, markRestoredDone, usageCollector, diff --git a/x-pack/plugins/data_enhanced/tsconfig.json b/x-pack/plugins/data_enhanced/tsconfig.json index 544b50c21224f..5627951c3d9eb 100644 --- a/x-pack/plugins/data_enhanced/tsconfig.json +++ b/x-pack/plugins/data_enhanced/tsconfig.json @@ -22,6 +22,7 @@ { "path": "../../../src/plugins/kibana_utils/tsconfig.json" }, { "path": "../../../src/plugins/usage_collection/tsconfig.json" }, { "path": "../../../src/plugins/management/tsconfig.json" }, + { "path": "../../../src/plugins/screenshot_mode/tsconfig.json"}, { "path": "../security/tsconfig.json" }, { "path": "../task_manager/tsconfig.json" }, From a7fff86390fbbf45d82104f95e64506ba2c5f304 Mon Sep 17 00:00:00 2001 From: Luke Elmers Date: Wed, 20 Oct 2021 09:17:52 -0600 Subject: [PATCH 132/204] [saved objects] Remove migrations `enableV2` config. (#115655) --- docs/setup/settings.asciidoc | 3 - .../deprecations/unknown_object_types.test.ts | 12 - .../deprecations/unknown_object_types.ts | 10 - .../migrations/kibana/kibana_migrator.mock.ts | 2 - .../migrations/kibana/kibana_migrator.test.ts | 218 ++++++------------ .../migrations/kibana/kibana_migrator.ts | 75 ++---- .../7.7.2_xpack_100k.test.ts | 1 - .../7_13_0_failed_action_tasks.test.ts | 1 - .../7_13_0_transform_failures.test.ts | 1 - .../7_13_0_unknown_types.test.ts | 1 - .../batch_size_bytes.test.ts | 1 - ...ze_bytes_exceeds_es_content_length.test.ts | 1 - .../integration_tests/cleanup.test.ts | 1 - .../collects_corrupt_docs.test.ts | 1 - .../corrupt_outdated_docs.test.ts | 1 - .../migration_from_older_v1.test.ts | 1 - .../migration_from_same_v1.test.ts | 1 - .../multiple_es_nodes.test.ts | 1 - .../multiple_kibana_nodes.test.ts | 1 - .../integration_tests/outdated_docs.test.ts | 1 - .../integration_tests/rewriting_id.test.ts | 1 - .../migrations_state_action_machine.test.ts | 1 - .../deprecations/delete_unknown_types.ts | 5 +- src/core/server/saved_objects/routes/index.ts | 2 +- .../delete_unknown_types.test.ts | 9 - .../saved_objects_config.test.ts | 4 +- .../saved_objects/saved_objects_config.ts | 21 +- .../service/lib/get_index_for_type.test.ts | 76 ++---- .../service/lib/get_index_for_type.ts | 13 +- .../service/lib/repository.test.js | 20 +- .../saved_objects/service/lib/repository.ts | 1 - .../resources/base/bin/kibana-docker | 1 - x-pack/plugins/uptime/e2e/config.ts | 1 - .../test/apm_api_integration/configs/index.ts | 9 - 34 files changed, 129 insertions(+), 369 deletions(-) diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index af22ad4ad157f..7235c2a673376 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -395,9 +395,6 @@ override this parameter to use their own Tile Map Service. For example: | `migrations.maxBatchSizeBytes:` | Defines the maximum payload size for indexing batches of upgraded saved objects to avoid migrations failing due to a 413 Request Entity Too Large response from Elasticsearch. This value should be lower than or equal to your Elasticsearch cluster's `http.max_content_length` configuration option. *Default: `100mb`* -| `migrations.enableV2:` - | experimental[]. Enables the new Saved Objects migration algorithm. For information about the migration algorithm, refer to <>. When `migrations v2` is stable, the setting will be removed in an upcoming release without any further notice. Setting the value to `false` causes {kib} to use the legacy migration algorithm, which shipped in 7.11 and earlier versions. *Default: `true`* - | `migrations.retryAttempts:` | The number of times migrations retry temporary failures, such as a network timeout, 503 status code, or `snapshot_in_progress_exception`. When upgrade migrations frequently fail after exhausting all retry attempts with a message such as `Unable to complete the [...] step after 15 attempts, terminating.`, increase the setting value. *Default: `15`* diff --git a/src/core/server/saved_objects/deprecations/unknown_object_types.test.ts b/src/core/server/saved_objects/deprecations/unknown_object_types.test.ts index d7ea73456e236..1f9ca741691d1 100644 --- a/src/core/server/saved_objects/deprecations/unknown_object_types.test.ts +++ b/src/core/server/saved_objects/deprecations/unknown_object_types.test.ts @@ -13,7 +13,6 @@ import { deleteUnknownTypeObjects, getUnknownTypesDeprecations } from './unknown import { typeRegistryMock } from '../saved_objects_type_registry.mock'; import { elasticsearchClientMock } from '../../elasticsearch/client/mocks'; import type { KibanaConfigType } from '../../kibana_config'; -import type { SavedObjectConfig } from '../saved_objects_config'; import { SavedObjectsType } from 'kibana/server'; const createSearchResponse = (count: number): estypes.SearchResponse => { @@ -32,7 +31,6 @@ describe('unknown saved object types deprecation', () => { let typeRegistry: ReturnType; let esClient: ReturnType; let kibanaConfig: KibanaConfigType; - let savedObjectsConfig: SavedObjectConfig; beforeEach(() => { typeRegistry = typeRegistryMock.create(); @@ -48,12 +46,6 @@ describe('unknown saved object types deprecation', () => { index: '.kibana', enabled: true, }; - - savedObjectsConfig = { - migration: { - enableV2: true, - }, - } as SavedObjectConfig; }); afterEach(() => { @@ -69,7 +61,6 @@ describe('unknown saved object types deprecation', () => { it('calls `esClient.asInternalUser.search` with the correct parameters', async () => { await getUnknownTypesDeprecations({ - savedObjectsConfig, esClient, typeRegistry, kibanaConfig, @@ -96,7 +87,6 @@ describe('unknown saved object types deprecation', () => { ); const deprecations = await getUnknownTypesDeprecations({ - savedObjectsConfig, esClient, typeRegistry, kibanaConfig, @@ -112,7 +102,6 @@ describe('unknown saved object types deprecation', () => { ); const deprecations = await getUnknownTypesDeprecations({ - savedObjectsConfig, esClient, typeRegistry, kibanaConfig, @@ -141,7 +130,6 @@ describe('unknown saved object types deprecation', () => { describe('deleteUnknownTypeObjects', () => { it('calls `esClient.asInternalUser.search` with the correct parameters', async () => { await deleteUnknownTypeObjects({ - savedObjectsConfig, esClient, typeRegistry, kibanaConfig, diff --git a/src/core/server/saved_objects/deprecations/unknown_object_types.ts b/src/core/server/saved_objects/deprecations/unknown_object_types.ts index c966e621ca605..8cd650bac8a2d 100644 --- a/src/core/server/saved_objects/deprecations/unknown_object_types.ts +++ b/src/core/server/saved_objects/deprecations/unknown_object_types.ts @@ -13,14 +13,12 @@ import { IScopedClusterClient } from '../../elasticsearch'; import { ISavedObjectTypeRegistry } from '../saved_objects_type_registry'; import { SavedObjectsRawDocSource } from '../serialization'; import type { KibanaConfigType } from '../../kibana_config'; -import type { SavedObjectConfig } from '../saved_objects_config'; import { getIndexForType } from '../service/lib'; interface UnknownTypesDeprecationOptions { typeRegistry: ISavedObjectTypeRegistry; esClient: IScopedClusterClient; kibanaConfig: KibanaConfigType; - savedObjectsConfig: SavedObjectConfig; kibanaVersion: string; } @@ -32,11 +30,9 @@ const getTargetIndices = ({ typeRegistry, kibanaVersion, kibanaConfig, - savedObjectsConfig, }: { types: string[]; typeRegistry: ISavedObjectTypeRegistry; - savedObjectsConfig: SavedObjectConfig; kibanaConfig: KibanaConfigType; kibanaVersion: string; }) => { @@ -46,7 +42,6 @@ const getTargetIndices = ({ getIndexForType({ type, typeRegistry, - migV2Enabled: savedObjectsConfig.migration.enableV2, kibanaVersion, defaultIndex: kibanaConfig.index, }) @@ -69,7 +64,6 @@ const getUnknownSavedObjects = async ({ typeRegistry, esClient, kibanaConfig, - savedObjectsConfig, kibanaVersion, }: UnknownTypesDeprecationOptions) => { const knownTypes = getKnownTypes(typeRegistry); @@ -78,7 +72,6 @@ const getUnknownSavedObjects = async ({ typeRegistry, kibanaConfig, kibanaVersion, - savedObjectsConfig, }); const query = getUnknownTypesQuery(knownTypes); @@ -141,7 +134,6 @@ interface DeleteUnknownTypesOptions { typeRegistry: ISavedObjectTypeRegistry; esClient: IScopedClusterClient; kibanaConfig: KibanaConfigType; - savedObjectsConfig: SavedObjectConfig; kibanaVersion: string; } @@ -149,7 +141,6 @@ export const deleteUnknownTypeObjects = async ({ esClient, typeRegistry, kibanaConfig, - savedObjectsConfig, kibanaVersion, }: DeleteUnknownTypesOptions) => { const knownTypes = getKnownTypes(typeRegistry); @@ -158,7 +149,6 @@ export const deleteUnknownTypeObjects = async ({ typeRegistry, kibanaConfig, kibanaVersion, - savedObjectsConfig, }); const query = getUnknownTypesQuery(knownTypes); diff --git a/src/core/server/saved_objects/migrations/kibana/kibana_migrator.mock.ts b/src/core/server/saved_objects/migrations/kibana/kibana_migrator.mock.ts index 9471bbc1b87a6..660300ea867ff 100644 --- a/src/core/server/saved_objects/migrations/kibana/kibana_migrator.mock.ts +++ b/src/core/server/saved_objects/migrations/kibana/kibana_migrator.mock.ts @@ -42,8 +42,6 @@ const createMigrator = ( scrollDuration: '15m', pollInterval: 1500, skip: false, - // TODO migrationsV2: remove/deprecate once we remove migrations v1 - enableV2: false, retryAttempts: 10, }, runMigrations: jest.fn(), diff --git a/src/core/server/saved_objects/migrations/kibana/kibana_migrator.test.ts b/src/core/server/saved_objects/migrations/kibana/kibana_migrator.test.ts index 6e10349f4b57c..c397559b52570 100644 --- a/src/core/server/saved_objects/migrations/kibana/kibana_migrator.test.ts +++ b/src/core/server/saved_objects/migrations/kibana/kibana_migrator.test.ts @@ -7,7 +7,7 @@ */ import { take } from 'rxjs/operators'; -import { estypes, errors as esErrors } from '@elastic/elasticsearch'; +import { estypes } from '@elastic/elasticsearch'; import { elasticsearchClientMock } from '../../../elasticsearch/client/mocks'; import { KibanaMigratorOptions, KibanaMigrator } from './kibana_migrator'; @@ -125,13 +125,6 @@ describe('KibanaMigrator', () => { it('only runs migrations once if called multiple times', async () => { const options = mockOptions(); - options.client.cat.templates.mockReturnValue( - elasticsearchClientMock.createSuccessTransportRequestPromise( - // @ts-expect-error - { templates: [] } as CatTemplatesResponse, - { statusCode: 404 } - ) - ); options.client.indices.get.mockReturnValue( elasticsearchClientMock.createSuccessTransportRequestPromise({}, { statusCode: 404 }) ); @@ -144,159 +137,79 @@ describe('KibanaMigrator', () => { migrator.prepareMigrations(); await migrator.runMigrations(); await migrator.runMigrations(); + await migrator.runMigrations(); - expect(options.client.cat.templates).toHaveBeenCalledTimes(1); + // indices.get is called twice during a single migration + expect(options.client.indices.get).toHaveBeenCalledTimes(2); }); - describe('when enableV2 = false', () => { - it('when enableV2 = false creates an IndexMigrator which retries NoLivingConnectionsError errors from ES client', async () => { - const options = mockOptions(); - - options.client.cat.templates.mockReturnValue( - elasticsearchClientMock.createSuccessTransportRequestPromise( - // @ts-expect-error - { templates: [] } as CatTemplatesResponse, - { statusCode: 404 } - ) - ); - options.client.indices.get.mockReturnValue( - elasticsearchClientMock.createSuccessTransportRequestPromise({}, { statusCode: 404 }) - ); - options.client.indices.getAlias.mockReturnValue( - elasticsearchClientMock.createSuccessTransportRequestPromise({}, { statusCode: 404 }) - ); - - options.client.indices.create = jest - .fn() - .mockReturnValueOnce( - elasticsearchClientMock.createErrorTransportRequestPromise( - new esErrors.NoLivingConnectionsError('reason', {} as any) - ) - ) - .mockImplementationOnce(() => - elasticsearchClientMock.createSuccessTransportRequestPromise('success') - ); - - const migrator = new KibanaMigrator(options); - const migratorStatus = migrator.getStatus$().pipe(take(3)).toPromise(); - - migrator.prepareMigrations(); - await migrator.runMigrations(); + it('emits results on getMigratorResult$()', async () => { + const options = mockV2MigrationOptions(); + const migrator = new KibanaMigrator(options); + const migratorStatus = migrator.getStatus$().pipe(take(3)).toPromise(); + migrator.prepareMigrations(); + await migrator.runMigrations(); - expect(options.client.indices.create).toHaveBeenCalledTimes(3); - const { status } = await migratorStatus; - return expect(status).toEqual('completed'); + const { status, result } = await migratorStatus; + expect(status).toEqual('completed'); + expect(result![0]).toMatchObject({ + destIndex: '.my-index_8.2.3_001', + sourceIndex: '.my-index_pre8.2.3_001', + elapsedMs: expect.any(Number), + status: 'migrated', }); - - it('emits results on getMigratorResult$()', async () => { - const options = mockOptions(); - - options.client.cat.templates.mockReturnValue( - elasticsearchClientMock.createSuccessTransportRequestPromise( - // @ts-expect-error - { templates: [] } as CatTemplatesResponse, - { statusCode: 404 } - ) - ); - options.client.indices.get.mockReturnValue( - elasticsearchClientMock.createSuccessTransportRequestPromise({}, { statusCode: 404 }) - ); - options.client.indices.getAlias.mockReturnValue( - elasticsearchClientMock.createSuccessTransportRequestPromise({}, { statusCode: 404 }) - ); - - const migrator = new KibanaMigrator(options); - const migratorStatus = migrator.getStatus$().pipe(take(3)).toPromise(); - migrator.prepareMigrations(); - await migrator.runMigrations(); - const { status, result } = await migratorStatus; - expect(status).toEqual('completed'); - expect(result![0]).toMatchObject({ - destIndex: '.my-index_1', - elapsedMs: expect.any(Number), - sourceIndex: '.my-index', - status: 'migrated', - }); - expect(result![1]).toMatchObject({ - destIndex: 'other-index_1', - elapsedMs: expect.any(Number), - sourceIndex: 'other-index', - status: 'migrated', - }); + expect(result![1]).toMatchObject({ + destIndex: 'other-index_8.2.3_001', + elapsedMs: expect.any(Number), + status: 'patched', }); }); - describe('when enableV2 = true', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - it('emits results on getMigratorResult$()', async () => { - const options = mockV2MigrationOptions(); - const migrator = new KibanaMigrator(options); - const migratorStatus = migrator.getStatus$().pipe(take(3)).toPromise(); - migrator.prepareMigrations(); - await migrator.runMigrations(); - - const { status, result } = await migratorStatus; - expect(status).toEqual('completed'); - expect(result![0]).toMatchObject({ - destIndex: '.my-index_8.2.3_001', - sourceIndex: '.my-index_pre8.2.3_001', - elapsedMs: expect.any(Number), - status: 'migrated', - }); - expect(result![1]).toMatchObject({ - destIndex: 'other-index_8.2.3_001', - elapsedMs: expect.any(Number), - status: 'patched', - }); - }); - it('rejects when the migration state machine terminates in a FATAL state', () => { - const options = mockV2MigrationOptions(); - options.client.indices.get.mockReturnValue( - elasticsearchClientMock.createSuccessTransportRequestPromise( - { - '.my-index_8.2.4_001': { - aliases: { - '.my-index': {}, - '.my-index_8.2.4': {}, - }, - mappings: { properties: {}, _meta: { migrationMappingPropertyHashes: {} } }, - settings: {}, + it('rejects when the migration state machine terminates in a FATAL state', () => { + const options = mockV2MigrationOptions(); + options.client.indices.get.mockReturnValue( + elasticsearchClientMock.createSuccessTransportRequestPromise( + { + '.my-index_8.2.4_001': { + aliases: { + '.my-index': {}, + '.my-index_8.2.4': {}, }, + mappings: { properties: {}, _meta: { migrationMappingPropertyHashes: {} } }, + settings: {}, }, - { statusCode: 200 } - ) - ); + }, + { statusCode: 200 } + ) + ); - const migrator = new KibanaMigrator(options); - migrator.prepareMigrations(); - return expect(migrator.runMigrations()).rejects.toMatchInlineSnapshot( - `[Error: Unable to complete saved object migrations for the [.my-index] index: The .my-index alias is pointing to a newer version of Kibana: v8.2.4]` - ); - }); - it('rejects when an unexpected exception occurs in an action', async () => { - const options = mockV2MigrationOptions(); - options.client.tasks.get.mockReturnValue( - elasticsearchClientMock.createSuccessTransportRequestPromise({ - completed: true, - error: { type: 'elasticsearch_exception', reason: 'task failed with an error' }, - failures: [], - task: { description: 'task description' } as any, - }) - ); + const migrator = new KibanaMigrator(options); + migrator.prepareMigrations(); + return expect(migrator.runMigrations()).rejects.toMatchInlineSnapshot( + `[Error: Unable to complete saved object migrations for the [.my-index] index: The .my-index alias is pointing to a newer version of Kibana: v8.2.4]` + ); + }); - const migrator = new KibanaMigrator(options); - migrator.prepareMigrations(); - await expect(migrator.runMigrations()).rejects.toMatchInlineSnapshot(` - [Error: Unable to complete saved object migrations for the [.my-index] index. Error: Reindex failed with the following error: - {"_tag":"Some","value":{"type":"elasticsearch_exception","reason":"task failed with an error"}}] - `); - expect(loggingSystemMock.collect(options.logger).error[0][0]).toMatchInlineSnapshot(` - [Error: Reindex failed with the following error: - {"_tag":"Some","value":{"type":"elasticsearch_exception","reason":"task failed with an error"}}] - `); - }); + it('rejects when an unexpected exception occurs in an action', async () => { + const options = mockV2MigrationOptions(); + options.client.tasks.get.mockReturnValue( + elasticsearchClientMock.createSuccessTransportRequestPromise({ + completed: true, + error: { type: 'elasticsearch_exception', reason: 'task failed with an error' }, + failures: [], + task: { description: 'task description' } as any, + }) + ); + + const migrator = new KibanaMigrator(options); + migrator.prepareMigrations(); + await expect(migrator.runMigrations()).rejects.toMatchInlineSnapshot(` + [Error: Unable to complete saved object migrations for the [.my-index] index. Error: Reindex failed with the following error: + {"_tag":"Some","value":{"type":"elasticsearch_exception","reason":"task failed with an error"}}] + `); + expect(loggingSystemMock.collect(options.logger).error[0][0]).toMatchInlineSnapshot(` + [Error: Reindex failed with the following error: + {"_tag":"Some","value":{"type":"elasticsearch_exception","reason":"task failed with an error"}}] + `); }); }); }); @@ -306,7 +219,7 @@ type MockedOptions = KibanaMigratorOptions & { }; const mockV2MigrationOptions = () => { - const options = mockOptions({ enableV2: true }); + const options = mockOptions(); options.client.indices.get.mockReturnValue( elasticsearchClientMock.createSuccessTransportRequestPromise( @@ -362,7 +275,7 @@ const mockV2MigrationOptions = () => { return options; }; -const mockOptions = ({ enableV2 }: { enableV2: boolean } = { enableV2: false }) => { +const mockOptions = () => { const options: MockedOptions = { logger: loggingSystemMock.create().get(), kibanaVersion: '8.2.3', @@ -401,7 +314,6 @@ const mockOptions = ({ enableV2 }: { enableV2: boolean } = { enableV2: false }) pollInterval: 20000, scrollDuration: '10m', skip: false, - enableV2, retryAttempts: 20, }, client: elasticsearchClientMock.createElasticsearchClient(), diff --git a/src/core/server/saved_objects/migrations/kibana/kibana_migrator.ts b/src/core/server/saved_objects/migrations/kibana/kibana_migrator.ts index 572b2934e49b8..d3755f8c7e666 100644 --- a/src/core/server/saved_objects/migrations/kibana/kibana_migrator.ts +++ b/src/core/server/saved_objects/migrations/kibana/kibana_migrator.ts @@ -22,13 +22,7 @@ import { SavedObjectsSerializer, SavedObjectsRawDoc, } from '../../serialization'; -import { - buildActiveMappings, - createMigrationEsClient, - IndexMigrator, - MigrationResult, - MigrationStatus, -} from '../core'; +import { buildActiveMappings, MigrationResult, MigrationStatus } from '../core'; import { DocumentMigrator, VersionedTransformer } from '../core/document_migrator'; import { createIndexMap } from '../core/build_index_map'; import { SavedObjectsMigrationConfigType } from '../../saved_objects_config'; @@ -71,7 +65,6 @@ export class KibanaMigrator { status: 'waiting_to_start', }); private readonly activeMappings: IndexMapping; - private migrationsRetryDelay?: number; // TODO migrationsV2: make private once we remove migrations v1 public readonly kibanaVersion: string; // TODO migrationsV2: make private once we remove migrations v1 @@ -105,7 +98,6 @@ export class KibanaMigrator { // Building the active mappings (and associated md5sums) is an expensive // operation so we cache the result this.activeMappings = buildActiveMappings(this.mappingProperties); - this.migrationsRetryDelay = migrationsRetryDelay; } /** @@ -173,49 +165,28 @@ export class KibanaMigrator { }); const migrators = Object.keys(indexMap).map((index) => { - // TODO migrationsV2: remove old migrations algorithm - if (this.soMigrationsConfig.enableV2) { - return { - migrate: (): Promise => { - return runResilientMigrator({ - client: this.client, - kibanaVersion: this.kibanaVersion, - targetMappings: buildActiveMappings(indexMap[index].typeMappings), - logger: this.log, - preMigrationScript: indexMap[index].script, - transformRawDocs: (rawDocs: SavedObjectsRawDoc[]) => - migrateRawDocsSafely({ - serializer: this.serializer, - knownTypes: new Set(this.typeRegistry.getAllTypes().map((t) => t.name)), - migrateDoc: this.documentMigrator.migrateAndConvert, - rawDocs, - }), - migrationVersionPerType: this.documentMigrator.migrationVersion, - indexPrefix: index, - migrationsConfig: this.soMigrationsConfig, - typeRegistry: this.typeRegistry, - }); - }, - }; - } else { - return new IndexMigrator({ - batchSize: this.soMigrationsConfig.batchSize, - client: createMigrationEsClient(this.client, this.log, this.migrationsRetryDelay), - documentMigrator: this.documentMigrator, - index, - kibanaVersion: this.kibanaVersion, - log: this.log, - mappingProperties: indexMap[index].typeMappings, - setStatus: (status) => this.status$.next(status), - pollInterval: this.soMigrationsConfig.pollInterval, - scrollDuration: this.soMigrationsConfig.scrollDuration, - serializer: this.serializer, - // Only necessary for the migrator of the kibana index. - obsoleteIndexTemplatePattern: - index === kibanaIndexName ? 'kibana_index_template*' : undefined, - convertToAliasScript: indexMap[index].script, - }); - } + return { + migrate: (): Promise => { + return runResilientMigrator({ + client: this.client, + kibanaVersion: this.kibanaVersion, + targetMappings: buildActiveMappings(indexMap[index].typeMappings), + logger: this.log, + preMigrationScript: indexMap[index].script, + transformRawDocs: (rawDocs: SavedObjectsRawDoc[]) => + migrateRawDocsSafely({ + serializer: this.serializer, + knownTypes: new Set(this.typeRegistry.getAllTypes().map((t) => t.name)), + migrateDoc: this.documentMigrator.migrateAndConvert, + rawDocs, + }), + migrationVersionPerType: this.documentMigrator.migrationVersion, + indexPrefix: index, + migrationsConfig: this.soMigrationsConfig, + typeRegistry: this.typeRegistry, + }); + }, + }; }); return Promise.all(migrators.map((migrator) => migrator.migrate())); diff --git a/src/core/server/saved_objects/migrationsv2/integration_tests/7.7.2_xpack_100k.test.ts b/src/core/server/saved_objects/migrationsv2/integration_tests/7.7.2_xpack_100k.test.ts index 41d89e2a01541..c22c6154c2605 100644 --- a/src/core/server/saved_objects/migrationsv2/integration_tests/7.7.2_xpack_100k.test.ts +++ b/src/core/server/saved_objects/migrationsv2/integration_tests/7.7.2_xpack_100k.test.ts @@ -49,7 +49,6 @@ describe('migration from 7.7.2-xpack with 100k objects', () => { { migrations: { skip: false, - enableV2: true, }, logging: { appenders: { diff --git a/src/core/server/saved_objects/migrationsv2/integration_tests/7_13_0_failed_action_tasks.test.ts b/src/core/server/saved_objects/migrationsv2/integration_tests/7_13_0_failed_action_tasks.test.ts index d70e034703158..a4ce95a9e0584 100644 --- a/src/core/server/saved_objects/migrationsv2/integration_tests/7_13_0_failed_action_tasks.test.ts +++ b/src/core/server/saved_objects/migrationsv2/integration_tests/7_13_0_failed_action_tasks.test.ts @@ -113,7 +113,6 @@ function createRoot() { { migrations: { skip: false, - enableV2: true, batchSize: 250, }, logging: { diff --git a/src/core/server/saved_objects/migrationsv2/integration_tests/7_13_0_transform_failures.test.ts b/src/core/server/saved_objects/migrationsv2/integration_tests/7_13_0_transform_failures.test.ts index fb40bda81cba5..c8e17a64a3fa3 100644 --- a/src/core/server/saved_objects/migrationsv2/integration_tests/7_13_0_transform_failures.test.ts +++ b/src/core/server/saved_objects/migrationsv2/integration_tests/7_13_0_transform_failures.test.ts @@ -155,7 +155,6 @@ function createRoot() { { migrations: { skip: false, - enableV2: true, batchSize: 5, }, logging: { diff --git a/src/core/server/saved_objects/migrationsv2/integration_tests/7_13_0_unknown_types.test.ts b/src/core/server/saved_objects/migrationsv2/integration_tests/7_13_0_unknown_types.test.ts index 0be8b1187af71..a04300ffea626 100644 --- a/src/core/server/saved_objects/migrationsv2/integration_tests/7_13_0_unknown_types.test.ts +++ b/src/core/server/saved_objects/migrationsv2/integration_tests/7_13_0_unknown_types.test.ts @@ -217,7 +217,6 @@ function createRoot() { { migrations: { skip: false, - enableV2: true, batchSize: 5, }, logging: { diff --git a/src/core/server/saved_objects/migrationsv2/integration_tests/batch_size_bytes.test.ts b/src/core/server/saved_objects/migrationsv2/integration_tests/batch_size_bytes.test.ts index b39c0b80cf42b..de25c7b1c6412 100644 --- a/src/core/server/saved_objects/migrationsv2/integration_tests/batch_size_bytes.test.ts +++ b/src/core/server/saved_objects/migrationsv2/integration_tests/batch_size_bytes.test.ts @@ -137,7 +137,6 @@ function createRoot(options: { maxBatchSizeBytes?: number }) { { migrations: { skip: false, - enableV2: true, batchSize: 1000, maxBatchSizeBytes: options.maxBatchSizeBytes, }, diff --git a/src/core/server/saved_objects/migrationsv2/integration_tests/batch_size_bytes_exceeds_es_content_length.test.ts b/src/core/server/saved_objects/migrationsv2/integration_tests/batch_size_bytes_exceeds_es_content_length.test.ts index 192321227d4ae..b47156e3a1e9e 100644 --- a/src/core/server/saved_objects/migrationsv2/integration_tests/batch_size_bytes_exceeds_es_content_length.test.ts +++ b/src/core/server/saved_objects/migrationsv2/integration_tests/batch_size_bytes_exceeds_es_content_length.test.ts @@ -88,7 +88,6 @@ function createRoot(options: { maxBatchSizeBytes?: number }) { { migrations: { skip: false, - enableV2: true, batchSize: 1000, maxBatchSizeBytes: options.maxBatchSizeBytes, }, diff --git a/src/core/server/saved_objects/migrationsv2/integration_tests/cleanup.test.ts b/src/core/server/saved_objects/migrationsv2/integration_tests/cleanup.test.ts index d76bbc786cffc..c84f72b184261 100644 --- a/src/core/server/saved_objects/migrationsv2/integration_tests/cleanup.test.ts +++ b/src/core/server/saved_objects/migrationsv2/integration_tests/cleanup.test.ts @@ -28,7 +28,6 @@ function createRoot() { { migrations: { skip: false, - enableV2: true, }, logging: { appenders: { diff --git a/src/core/server/saved_objects/migrationsv2/integration_tests/collects_corrupt_docs.test.ts b/src/core/server/saved_objects/migrationsv2/integration_tests/collects_corrupt_docs.test.ts index 779db252154a3..e330653089c6e 100644 --- a/src/core/server/saved_objects/migrationsv2/integration_tests/collects_corrupt_docs.test.ts +++ b/src/core/server/saved_objects/migrationsv2/integration_tests/collects_corrupt_docs.test.ts @@ -149,7 +149,6 @@ function createRoot() { { migrations: { skip: false, - enableV2: true, batchSize: 5, }, logging: { diff --git a/src/core/server/saved_objects/migrationsv2/integration_tests/corrupt_outdated_docs.test.ts b/src/core/server/saved_objects/migrationsv2/integration_tests/corrupt_outdated_docs.test.ts index 7368d856e2c2c..348cbe88cd8a7 100644 --- a/src/core/server/saved_objects/migrationsv2/integration_tests/corrupt_outdated_docs.test.ts +++ b/src/core/server/saved_objects/migrationsv2/integration_tests/corrupt_outdated_docs.test.ts @@ -153,7 +153,6 @@ function createRoot() { { migrations: { skip: false, - enableV2: true, batchSize: 5, }, logging: { diff --git a/src/core/server/saved_objects/migrationsv2/integration_tests/migration_from_older_v1.test.ts b/src/core/server/saved_objects/migrationsv2/integration_tests/migration_from_older_v1.test.ts index 46fecdf05bbaf..0ed9262017263 100644 --- a/src/core/server/saved_objects/migrationsv2/integration_tests/migration_from_older_v1.test.ts +++ b/src/core/server/saved_objects/migrationsv2/integration_tests/migration_from_older_v1.test.ts @@ -77,7 +77,6 @@ describe('migrating from 7.3.0-xpack which used v1 migrations', () => { { migrations: { skip: false, - enableV2: true, // There are 40 docs in fixtures. Batch size configured to enforce 3 migration steps. batchSize: 15, }, diff --git a/src/core/server/saved_objects/migrationsv2/integration_tests/migration_from_same_v1.test.ts b/src/core/server/saved_objects/migrationsv2/integration_tests/migration_from_same_v1.test.ts index 18eb5cc96e496..15d985daccba6 100644 --- a/src/core/server/saved_objects/migrationsv2/integration_tests/migration_from_same_v1.test.ts +++ b/src/core/server/saved_objects/migrationsv2/integration_tests/migration_from_same_v1.test.ts @@ -77,7 +77,6 @@ describe('migrating from the same Kibana version that used v1 migrations', () => { migrations: { skip: false, - enableV2: true, // There are 40 docs in fixtures. Batch size configured to enforce 3 migration steps. batchSize: 15, }, diff --git a/src/core/server/saved_objects/migrationsv2/integration_tests/multiple_es_nodes.test.ts b/src/core/server/saved_objects/migrationsv2/integration_tests/multiple_es_nodes.test.ts index 755bb5f946e4f..6956e53ebc7fa 100644 --- a/src/core/server/saved_objects/migrationsv2/integration_tests/multiple_es_nodes.test.ts +++ b/src/core/server/saved_objects/migrationsv2/integration_tests/multiple_es_nodes.test.ts @@ -67,7 +67,6 @@ function createRoot({ logFileName, hosts }: RootConfig) { }, migrations: { skip: false, - enableV2: true, batchSize: 100, // fixture contains 5000 docs }, logging: { diff --git a/src/core/server/saved_objects/migrationsv2/integration_tests/multiple_kibana_nodes.test.ts b/src/core/server/saved_objects/migrationsv2/integration_tests/multiple_kibana_nodes.test.ts index 11c5b33c0fd3d..ef92c823182d8 100644 --- a/src/core/server/saved_objects/migrationsv2/integration_tests/multiple_kibana_nodes.test.ts +++ b/src/core/server/saved_objects/migrationsv2/integration_tests/multiple_kibana_nodes.test.ts @@ -67,7 +67,6 @@ async function createRoot({ logFileName }: CreateRootConfig) { }, migrations: { skip: false, - enableV2: true, batchSize: 100, // fixture contains 5000 docs }, logging: { diff --git a/src/core/server/saved_objects/migrationsv2/integration_tests/outdated_docs.test.ts b/src/core/server/saved_objects/migrationsv2/integration_tests/outdated_docs.test.ts index 58ff34913f5d4..506f42cb2e402 100644 --- a/src/core/server/saved_objects/migrationsv2/integration_tests/outdated_docs.test.ts +++ b/src/core/server/saved_objects/migrationsv2/integration_tests/outdated_docs.test.ts @@ -95,7 +95,6 @@ function createRoot() { { migrations: { skip: false, - enableV2: true, }, logging: { appenders: { diff --git a/src/core/server/saved_objects/migrationsv2/integration_tests/rewriting_id.test.ts b/src/core/server/saved_objects/migrationsv2/integration_tests/rewriting_id.test.ts index 4564a89ee0816..2fd1ce9b6b14b 100644 --- a/src/core/server/saved_objects/migrationsv2/integration_tests/rewriting_id.test.ts +++ b/src/core/server/saved_objects/migrationsv2/integration_tests/rewriting_id.test.ts @@ -62,7 +62,6 @@ function createRoot() { { migrations: { skip: false, - enableV2: true, }, logging: { appenders: { diff --git a/src/core/server/saved_objects/migrationsv2/migrations_state_action_machine.test.ts b/src/core/server/saved_objects/migrationsv2/migrations_state_action_machine.test.ts index 21468d7552320..338eecf151174 100644 --- a/src/core/server/saved_objects/migrationsv2/migrations_state_action_machine.test.ts +++ b/src/core/server/saved_objects/migrationsv2/migrations_state_action_machine.test.ts @@ -45,7 +45,6 @@ describe('migrationsStateActionMachine', () => { pollInterval: 0, scrollDuration: '0s', skip: false, - enableV2: true, retryAttempts: 5, }, typeRegistry, diff --git a/src/core/server/saved_objects/routes/deprecations/delete_unknown_types.ts b/src/core/server/saved_objects/routes/deprecations/delete_unknown_types.ts index a9e1a41f01d91..2b6d64bef4f1a 100644 --- a/src/core/server/saved_objects/routes/deprecations/delete_unknown_types.ts +++ b/src/core/server/saved_objects/routes/deprecations/delete_unknown_types.ts @@ -9,18 +9,16 @@ import { IRouter } from '../../../http'; import { catchAndReturnBoomErrors } from '../utils'; import { deleteUnknownTypeObjects } from '../../deprecations'; -import { SavedObjectConfig } from '../../saved_objects_config'; import { KibanaConfigType } from '../../../kibana_config'; interface RouteDependencies { - config: SavedObjectConfig; kibanaConfig: KibanaConfigType; kibanaVersion: string; } export const registerDeleteUnknownTypesRoute = ( router: IRouter, - { config, kibanaConfig, kibanaVersion }: RouteDependencies + { kibanaConfig, kibanaVersion }: RouteDependencies ) => { router.post( { @@ -31,7 +29,6 @@ export const registerDeleteUnknownTypesRoute = ( await deleteUnknownTypeObjects({ esClient: context.core.elasticsearch.client, typeRegistry: context.core.savedObjects.typeRegistry, - savedObjectsConfig: config, kibanaConfig, kibanaVersion, }); diff --git a/src/core/server/saved_objects/routes/index.ts b/src/core/server/saved_objects/routes/index.ts index d7cc8af07b0ab..a85070867ae8f 100644 --- a/src/core/server/saved_objects/routes/index.ts +++ b/src/core/server/saved_objects/routes/index.ts @@ -74,5 +74,5 @@ export function registerRoutes({ const internalRouter = http.createRouter('/internal/saved_objects/'); registerMigrateRoute(internalRouter, migratorPromise); - registerDeleteUnknownTypesRoute(internalRouter, { config, kibanaConfig, kibanaVersion }); + registerDeleteUnknownTypesRoute(internalRouter, { kibanaConfig, kibanaVersion }); } diff --git a/src/core/server/saved_objects/routes/integration_tests/delete_unknown_types.test.ts b/src/core/server/saved_objects/routes/integration_tests/delete_unknown_types.test.ts index fef2b2d5870e0..0c7fbdda89fbf 100644 --- a/src/core/server/saved_objects/routes/integration_tests/delete_unknown_types.test.ts +++ b/src/core/server/saved_objects/routes/integration_tests/delete_unknown_types.test.ts @@ -13,7 +13,6 @@ import { elasticsearchServiceMock } from '../../../../../core/server/elasticsear import { typeRegistryMock } from '../../saved_objects_type_registry.mock'; import { setupServer } from '../test_utils'; import { KibanaConfigType } from '../../../kibana_config'; -import { SavedObjectConfig } from '../../saved_objects_config'; import { SavedObjectsType } from 'kibana/server'; type SetupServerReturn = UnwrapPromise>; @@ -24,13 +23,6 @@ describe('POST /internal/saved_objects/deprecations/_delete_unknown_types', () = enabled: true, index: '.kibana', }; - const config: SavedObjectConfig = { - maxImportExportSize: 10000, - maxImportPayloadBytes: 24000000, - migration: { - enableV2: true, - } as SavedObjectConfig['migration'], - }; let server: SetupServerReturn['server']; let httpSetup: SetupServerReturn['httpSetup']; @@ -54,7 +46,6 @@ describe('POST /internal/saved_objects/deprecations/_delete_unknown_types', () = registerDeleteUnknownTypesRoute(router, { kibanaVersion, kibanaConfig, - config, }); await server.start(); diff --git a/src/core/server/saved_objects/saved_objects_config.test.ts b/src/core/server/saved_objects/saved_objects_config.test.ts index 720b28403edf2..06b9e9661b746 100644 --- a/src/core/server/saved_objects/saved_objects_config.test.ts +++ b/src/core/server/saved_objects/saved_objects_config.test.ts @@ -22,7 +22,7 @@ describe('migrations config', function () { const { messages } = applyMigrationsDeprecations({ enableV2: true }); expect(messages).toMatchInlineSnapshot(` Array [ - "\\"migrations.enableV2\\" is deprecated and will be removed in an upcoming release without any further notice.", + "You no longer need to configure \\"migrations.enableV2\\".", ] `); }); @@ -31,7 +31,7 @@ describe('migrations config', function () { const { messages } = applyMigrationsDeprecations({ enableV2: false }); expect(messages).toMatchInlineSnapshot(` Array [ - "\\"migrations.enableV2\\" is deprecated and will be removed in an upcoming release without any further notice.", + "You no longer need to configure \\"migrations.enableV2\\".", ] `); }); diff --git a/src/core/server/saved_objects/saved_objects_config.ts b/src/core/server/saved_objects/saved_objects_config.ts index c9b4b4499fa80..02fbd974da4ae 100644 --- a/src/core/server/saved_objects/saved_objects_config.ts +++ b/src/core/server/saved_objects/saved_objects_config.ts @@ -7,8 +7,8 @@ */ import { schema, TypeOf } from '@kbn/config-schema'; +import { ConfigDeprecationProvider } from '../config'; import type { ServiceConfigDescriptor } from '../internal_types'; -import type { ConfigDeprecationProvider } from '../config'; const migrationSchema = schema.object({ batchSize: schema.number({ defaultValue: 1_000 }), @@ -16,29 +16,12 @@ const migrationSchema = schema.object({ scrollDuration: schema.string({ defaultValue: '15m' }), pollInterval: schema.number({ defaultValue: 1_500 }), skip: schema.boolean({ defaultValue: false }), - enableV2: schema.boolean({ defaultValue: true }), retryAttempts: schema.number({ defaultValue: 15 }), }); export type SavedObjectsMigrationConfigType = TypeOf; -const migrationDeprecations: ConfigDeprecationProvider = () => [ - (settings, fromPath, addDeprecation) => { - const migrationsConfig = settings[fromPath]; - if (migrationsConfig?.enableV2 !== undefined) { - addDeprecation({ - configPath: `${fromPath}.enableV2`, - message: - '"migrations.enableV2" is deprecated and will be removed in an upcoming release without any further notice.', - documentationUrl: 'https://ela.st/kbn-so-migration-v2', - correctiveActions: { - manualSteps: [`Remove "migrations.enableV2" from your kibana configs.`], - }, - }); - } - return settings; - }, -]; +const migrationDeprecations: ConfigDeprecationProvider = ({ unused }) => [unused('enableV2')]; export const savedObjectsMigrationConfig: ServiceConfigDescriptor = { diff --git a/src/core/server/saved_objects/service/lib/get_index_for_type.test.ts b/src/core/server/saved_objects/service/lib/get_index_for_type.test.ts index fa065b02b8050..16e3ba9495f04 100644 --- a/src/core/server/saved_objects/service/lib/get_index_for_type.test.ts +++ b/src/core/server/saved_objects/service/lib/get_index_for_type.test.ts @@ -18,63 +18,27 @@ describe('getIndexForType', () => { typeRegistry = typeRegistryMock.create(); }); - describe('when migV2 is enabled', () => { - const migV2Enabled = true; - - it('returns the correct index for a type specifying a custom index', () => { - typeRegistry.getIndex.mockImplementation((type) => `.${type}-index`); - expect( - getIndexForType({ - type: 'foo', - typeRegistry, - defaultIndex, - kibanaVersion, - migV2Enabled, - }) - ).toEqual('.foo-index_8.0.0'); - }); - - it('returns the correct index for a type not specifying a custom index', () => { - typeRegistry.getIndex.mockImplementation((type) => undefined); - expect( - getIndexForType({ - type: 'foo', - typeRegistry, - defaultIndex, - kibanaVersion, - migV2Enabled, - }) - ).toEqual('.kibana_8.0.0'); - }); + it('returns the correct index for a type specifying a custom index', () => { + typeRegistry.getIndex.mockImplementation((type) => `.${type}-index`); + expect( + getIndexForType({ + type: 'foo', + typeRegistry, + defaultIndex, + kibanaVersion, + }) + ).toEqual('.foo-index_8.0.0'); }); - describe('when migV2 is disabled', () => { - const migV2Enabled = false; - - it('returns the correct index for a type specifying a custom index', () => { - typeRegistry.getIndex.mockImplementation((type) => `.${type}-index`); - expect( - getIndexForType({ - type: 'foo', - typeRegistry, - defaultIndex, - kibanaVersion, - migV2Enabled, - }) - ).toEqual('.foo-index'); - }); - - it('returns the correct index for a type not specifying a custom index', () => { - typeRegistry.getIndex.mockImplementation((type) => undefined); - expect( - getIndexForType({ - type: 'foo', - typeRegistry, - defaultIndex, - kibanaVersion, - migV2Enabled, - }) - ).toEqual('.kibana'); - }); + it('returns the correct index for a type not specifying a custom index', () => { + typeRegistry.getIndex.mockImplementation((type) => undefined); + expect( + getIndexForType({ + type: 'foo', + typeRegistry, + defaultIndex, + kibanaVersion, + }) + ).toEqual('.kibana_8.0.0'); }); }); diff --git a/src/core/server/saved_objects/service/lib/get_index_for_type.ts b/src/core/server/saved_objects/service/lib/get_index_for_type.ts index cef477e6dd840..ae34e6063e0a5 100644 --- a/src/core/server/saved_objects/service/lib/get_index_for_type.ts +++ b/src/core/server/saved_objects/service/lib/get_index_for_type.ts @@ -11,7 +11,6 @@ import { ISavedObjectTypeRegistry } from '../../saved_objects_type_registry'; interface GetIndexForTypeOptions { type: string; typeRegistry: ISavedObjectTypeRegistry; - migV2Enabled: boolean; kibanaVersion: string; defaultIndex: string; } @@ -19,18 +18,8 @@ interface GetIndexForTypeOptions { export const getIndexForType = ({ type, typeRegistry, - migV2Enabled, defaultIndex, kibanaVersion, }: GetIndexForTypeOptions): string => { - // TODO migrationsV2: Remove once we remove migrations v1 - // This is a hacky, but it required the least amount of changes to - // existing code to support a migrations v2 index. Long term we would - // want to always use the type registry to resolve a type's index - // (including the default index). - if (migV2Enabled) { - return `${typeRegistry.getIndex(type) || defaultIndex}_${kibanaVersion}`; - } else { - return typeRegistry.getIndex(type) || defaultIndex; - } + return `${typeRegistry.getIndex(type) || defaultIndex}_${kibanaVersion}`; }; diff --git a/src/core/server/saved_objects/service/lib/repository.test.js b/src/core/server/saved_objects/service/lib/repository.test.js index 84359147fccbc..985d609f2da59 100644 --- a/src/core/server/saved_objects/service/lib/repository.test.js +++ b/src/core/server/saved_objects/service/lib/repository.test.js @@ -613,12 +613,18 @@ describe('SavedObjectsRepository', () => { it(`should use default index`, async () => { await bulkCreateSuccess([obj1, obj2]); - expectClientCallArgsAction([obj1, obj2], { method: 'create', _index: '.kibana-test' }); + expectClientCallArgsAction([obj1, obj2], { + method: 'create', + _index: '.kibana-test_8.0.0-testing', + }); }); it(`should use custom index`, async () => { await bulkCreateSuccess([obj1, obj2].map((x) => ({ ...x, type: CUSTOM_INDEX_TYPE }))); - expectClientCallArgsAction([obj1, obj2], { method: 'create', _index: 'custom' }); + expectClientCallArgsAction([obj1, obj2], { + method: 'create', + _index: 'custom_8.0.0-testing', + }); }); it(`prepends namespace to the id when providing namespace for single-namespace type`, async () => { @@ -2092,7 +2098,7 @@ describe('SavedObjectsRepository', () => { it(`should use default index`, async () => { await createSuccess(type, attributes, { id }); expect(client.create).toHaveBeenCalledWith( - expect.objectContaining({ index: '.kibana-test' }), + expect.objectContaining({ index: '.kibana-test_8.0.0-testing' }), expect.anything() ); }); @@ -2100,7 +2106,7 @@ describe('SavedObjectsRepository', () => { it(`should use custom index`, async () => { await createSuccess(CUSTOM_INDEX_TYPE, attributes, { id }); expect(client.create).toHaveBeenCalledWith( - expect.objectContaining({ index: 'custom' }), + expect.objectContaining({ index: 'custom_8.0.0-testing' }), expect.anything() ); }); @@ -2680,7 +2686,9 @@ describe('SavedObjectsRepository', () => { it(`should use all indices for types that are not namespace-agnostic`, async () => { await deleteByNamespaceSuccess(namespace); expect(client.updateByQuery).toHaveBeenCalledWith( - expect.objectContaining({ index: ['.kibana-test', 'custom'] }), + expect.objectContaining({ + index: ['.kibana-test_8.0.0-testing', 'custom_8.0.0-testing'], + }), expect.anything() ); }); @@ -2774,7 +2782,7 @@ describe('SavedObjectsRepository', () => { await removeReferencesToSuccess(); expect(client.updateByQuery).toHaveBeenCalledWith( expect.objectContaining({ - index: ['.kibana-test', 'custom'], + index: ['.kibana-test_8.0.0-testing', 'custom_8.0.0-testing'], }), expect.anything() ); diff --git a/src/core/server/saved_objects/service/lib/repository.ts b/src/core/server/saved_objects/service/lib/repository.ts index c081c59911405..6798f411d87a9 100644 --- a/src/core/server/saved_objects/service/lib/repository.ts +++ b/src/core/server/saved_objects/service/lib/repository.ts @@ -2089,7 +2089,6 @@ export class SavedObjectsRepository { defaultIndex: this._index, typeRegistry: this._registry, kibanaVersion: this._migrator.kibanaVersion, - migV2Enabled: this._migrator.soMigrationsConfig.enableV2, }); } diff --git a/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker b/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker index 4b5c2e25084ed..235a5fbe1a1a3 100755 --- a/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker +++ b/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker @@ -94,7 +94,6 @@ kibana_vars=( map.tilemap.url migrations.batchSize migrations.maxBatchSizeBytes - migrations.enableV2 migrations.pollInterval migrations.retryAttempts migrations.scrollDuration diff --git a/x-pack/plugins/uptime/e2e/config.ts b/x-pack/plugins/uptime/e2e/config.ts index c5d573afccd96..d2c7a691e0a49 100644 --- a/x-pack/plugins/uptime/e2e/config.ts +++ b/x-pack/plugins/uptime/e2e/config.ts @@ -43,7 +43,6 @@ async function config({ readConfigFile }: FtrConfigProviderContext) { `--uiSettings.overrides.theme:darkMode=true`, `--elasticsearch.username=kibana_system`, `--elasticsearch.password=changeme`, - '--migrations.enableV2=false', '--xpack.reporting.enabled=false', ], }, diff --git a/x-pack/test/apm_api_integration/configs/index.ts b/x-pack/test/apm_api_integration/configs/index.ts index ad1f897debe32..3bc03eb5b4259 100644 --- a/x-pack/test/apm_api_integration/configs/index.ts +++ b/x-pack/test/apm_api_integration/configs/index.ts @@ -11,22 +11,13 @@ import { createTestConfig, CreateTestConfig } from '../common/config'; const apmFtrConfigs = { basic: { license: 'basic' as const, - kibanaConfig: { - // disable v2 migrations to prevent issue where kibana index is deleted - // during a migration - 'migrations.enableV2': 'false', - }, }, trial: { license: 'trial' as const, - kibanaConfig: { - 'migrations.enableV2': 'false', - }, }, rules: { license: 'trial' as const, kibanaConfig: { - 'migrations.enableV2': 'false', 'xpack.ruleRegistry.write.enabled': 'true', }, }, From f1fe71650b5882ca76f1615a153e46469cbc3a25 Mon Sep 17 00:00:00 2001 From: Liza Katz Date: Wed, 20 Oct 2021 18:19:53 +0300 Subject: [PATCH 133/204] [Bug] fix memory info extraction for fullstory (#115756) * fix memory info extraction * ts --- x-pack/plugins/cloud/public/plugin.test.ts | 18 ++++++++++++++---- x-pack/plugins/cloud/public/plugin.ts | 16 ++++++++++------ 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/cloud/public/plugin.test.ts b/x-pack/plugins/cloud/public/plugin.test.ts index a19a28d6c4713..c1c94375d7063 100644 --- a/x-pack/plugins/cloud/public/plugin.test.ts +++ b/x-pack/plugins/cloud/public/plugin.test.ts @@ -138,14 +138,22 @@ describe('Cloud Plugin', () => { describe('with memory', () => { beforeAll(() => { - // @ts-expect-error + // @ts-expect-error 2339 window.performance.memory = { - someMetric: 1, + get jsHeapSizeLimit() { + return 3; + }, + get totalJSHeapSize() { + return 2; + }, + get usedJSHeapSize() { + return 1; + }, }; }); afterAll(() => { - // @ts-expect-error + // @ts-expect-error 2339 delete window.performance.memory; }); @@ -159,7 +167,9 @@ describe('Cloud Plugin', () => { expect(fullStoryApiMock.event).toHaveBeenCalledWith('Loaded Kibana', { kibana_version_str: initContext.env.packageInfo.version, - some_metric_int: 1, + memory_js_heap_size_limit_int: 3, + memory_js_heap_size_total_int: 2, + memory_js_heap_size_used_int: 1, }); }); }); diff --git a/x-pack/plugins/cloud/public/plugin.ts b/x-pack/plugins/cloud/public/plugin.ts index 82fabea8b5a56..64b03acdc3ffd 100644 --- a/x-pack/plugins/cloud/public/plugin.ts +++ b/x-pack/plugins/cloud/public/plugin.ts @@ -16,7 +16,6 @@ import { } from 'src/core/public'; import { i18n } from '@kbn/i18n'; import { Subscription } from 'rxjs'; -import { mapKeys, snakeCase } from 'lodash'; import type { AuthenticatedUser, SecurityPluginSetup, @@ -250,11 +249,16 @@ export class CloudPlugin implements Plugin { } // Get performance information from the browser (non standard property - const memoryInfo = mapKeys( - // @ts-expect-error - window.performance.memory || {}, - (_, key) => `${snakeCase(key)}_int` - ); + // @ts-expect-error 2339 + const memory = window.performance.memory; + let memoryInfo = {}; + if (memory) { + memoryInfo = { + memory_js_heap_size_limit_int: memory.jsHeapSizeLimit, + memory_js_heap_size_total_int: memory.totalJSHeapSize, + memory_js_heap_size_used_int: memory.usedJSHeapSize, + }; + } // Record an event that Kibana was opened so we can easily search for sessions that use Kibana fullStory.event('Loaded Kibana', { // `str` suffix is required, see docs: https://help.fullstory.com/hc/en-us/articles/360020623234 From c9bca2cd59c79702f6d379ffda2bfc46f24193c7 Mon Sep 17 00:00:00 2001 From: Byron Hulcher Date: Wed, 20 Oct 2021 11:51:00 -0400 Subject: [PATCH 134/204] [App Search] Load curation settings at root /curations/ path (#115690) --- .../curations/curations_logic.test.ts | 11 -- .../components/curations/curations_logic.ts | 1 - .../curations/curations_router.test.tsx | 100 ++++++++++++++++++ .../components/curations/curations_router.tsx | 35 +++++- .../curations/views/curations.test.tsx | 6 +- .../components/curations/views/curations.tsx | 7 +- .../views/curations_overview.test.tsx | 49 ++++++++- .../curations/views/curations_overview.tsx | 8 +- .../curations_settings.test.tsx | 61 ----------- .../curations_settings/curations_settings.tsx | 26 +---- 10 files changed, 195 insertions(+), 109 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_logic.test.ts index 0d02fbe413870..42c3985e4dcf1 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_logic.test.ts @@ -107,17 +107,6 @@ describe('CurationsLogic', () => { describe('listeners', () => { describe('loadCurations', () => { - it('should set dataLoading state', () => { - mount({ dataLoading: false }); - - CurationsLogic.actions.loadCurations(); - - expect(CurationsLogic.values).toEqual({ - ...DEFAULT_VALUES, - dataLoading: true, - }); - }); - it('should make an API call and set curations & meta state', async () => { http.get.mockReturnValueOnce(Promise.resolve(MOCK_CURATIONS_RESPONSE)); mount(); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_logic.ts index 04d04b297050a..4419603efddf0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_logic.ts @@ -61,7 +61,6 @@ export const CurationsLogic = kea true, onCurationsLoad: () => false, }, ], diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.test.tsx index 9598212d3e0c9..a0fd778ac7dde 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.test.tsx @@ -5,6 +5,9 @@ * 2.0. */ +import '../../../__mocks__/shallow_useeffect.mock'; +import '../../../__mocks__/react_router'; +import { setMockActions, setMockValues } from '../../../__mocks__/kea_logic'; import '../../__mocks__/engine_logic.mock'; import React from 'react'; @@ -12,13 +15,110 @@ import { Route, Switch } from 'react-router-dom'; import { shallow } from 'enzyme'; +import { LogRetentionOptions } from '../log_retention'; + import { CurationsRouter } from './'; +const MOCK_VALUES = { + // CurationsSettingsLogic + dataLoading: false, + curationsSettings: { + enabled: true, + mode: 'automatic', + }, + // LogRetentionLogic + logRetention: { + [LogRetentionOptions.Analytics]: { + enabled: true, + }, + }, + // LicensingLogic + hasPlatinumLicense: true, +}; + +const MOCK_ACTIONS = { + // CurationsSettingsLogic + loadCurationsSettings: jest.fn(), + onSkipLoadingCurationsSettings: jest.fn(), + // LogRetentionLogic + fetchLogRetention: jest.fn(), +}; + describe('CurationsRouter', () => { + beforeEach(() => { + jest.clearAllMocks(); + setMockActions(MOCK_ACTIONS); + }); + it('renders', () => { const wrapper = shallow(); expect(wrapper.find(Switch)).toHaveLength(1); expect(wrapper.find(Route)).toHaveLength(4); }); + + it('loads log retention settings', () => { + setMockValues(MOCK_VALUES); + shallow(); + + expect(MOCK_ACTIONS.fetchLogRetention).toHaveBeenCalled(); + }); + + describe('when the user has no platinum license', () => { + beforeEach(() => { + setMockValues({ + ...MOCK_VALUES, + hasPlatinumLicense: false, + }); + }); + + it('it does not fetch log retention', () => { + shallow(); + expect(MOCK_ACTIONS.fetchLogRetention).toHaveBeenCalledTimes(0); + }); + }); + + describe('loading curation settings based on log retention', () => { + it('loads curation settings when log retention is enabled', () => { + setMockValues({ + ...MOCK_VALUES, + logRetention: { + [LogRetentionOptions.Analytics]: { + enabled: true, + }, + }, + }); + + shallow(); + + expect(MOCK_ACTIONS.loadCurationsSettings).toHaveBeenCalledTimes(1); + }); + + it('skips loading curation settings when log retention is enabled', () => { + setMockValues({ + ...MOCK_VALUES, + logRetention: { + [LogRetentionOptions.Analytics]: { + enabled: false, + }, + }, + }); + + shallow(); + + expect(MOCK_ACTIONS.onSkipLoadingCurationsSettings).toHaveBeenCalledTimes(1); + }); + + it('takes no action if log retention has not yet been loaded', () => { + setMockValues({ + ...MOCK_VALUES, + logRetention: null, + }); + + shallow(); + + expect(MOCK_ACTIONS.loadCurationsSettings).toHaveBeenCalledTimes(0); + expect(MOCK_ACTIONS.onSkipLoadingCurationsSettings).toHaveBeenCalledTimes(0); + }); + }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.tsx index 693e5406b714b..a3b000ea5054a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.tsx @@ -5,20 +5,53 @@ * 2.0. */ -import React from 'react'; +import React, { useEffect } from 'react'; import { Route, Switch } from 'react-router-dom'; +import { useValues, useActions } from 'kea'; + +import { LicensingLogic } from '../../../shared/licensing'; import { ENGINE_CURATIONS_PATH, ENGINE_CURATIONS_NEW_PATH, ENGINE_CURATION_PATH, ENGINE_CURATION_SUGGESTION_PATH, } from '../../routes'; +import { LogRetentionLogic, LogRetentionOptions } from '../log_retention'; import { Curation } from './curation'; import { Curations, CurationCreation, CurationSuggestion } from './views'; +import { CurationsSettingsLogic } from './views/curations_settings'; export const CurationsRouter: React.FC = () => { + // We need to loadCurationsSettings here so they are available across all views + + const { hasPlatinumLicense } = useValues(LicensingLogic); + + const { loadCurationsSettings, onSkipLoadingCurationsSettings } = + useActions(CurationsSettingsLogic); + + const { logRetention } = useValues(LogRetentionLogic); + const { fetchLogRetention } = useActions(LogRetentionLogic); + + const analyticsDisabled = !logRetention?.[LogRetentionOptions.Analytics].enabled; + + useEffect(() => { + if (hasPlatinumLicense) { + fetchLogRetention(); + } + }, [hasPlatinumLicense]); + + useEffect(() => { + if (logRetention) { + if (!analyticsDisabled) { + loadCurationsSettings(); + } else { + onSkipLoadingCurationsSettings(); + } + } + }, [logRetention]); + return ( diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.test.tsx index 42d808da6d9ee..aacabf0ac7303 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.test.tsx @@ -126,14 +126,14 @@ describe('Curations', () => { describe('loading state', () => { it('renders a full-page loading state on initial page load', () => { - setMockValues({ ...values, dataLoading: true, curations: [] }); + setMockValues({ ...values, dataLoading: true }); const wrapper = shallow(); expect(wrapper.prop('isLoading')).toEqual(true); }); - it('does not re-render a full-page loading state after initial page load (uses component-level loading state instead)', () => { - setMockValues({ ...values, dataLoading: true, curations: [{}] }); + it('does not re-render a full-page loading state when data is loaded', () => { + setMockValues({ ...values, dataLoading: false }); const wrapper = shallow(); expect(wrapper.prop('isLoading')).toEqual(false); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.tsx index 7440e0cf42b44..3d4751fcb343f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.tsx @@ -25,12 +25,13 @@ import { getCurationsBreadcrumbs } from '../utils'; import { CurationsHistory } from './curations_history/curations_history'; import { CurationsOverview } from './curations_overview'; -import { CurationsSettings } from './curations_settings'; +import { CurationsSettings, CurationsSettingsLogic } from './curations_settings'; export const Curations: React.FC = () => { - const { dataLoading, curations, meta, selectedPageTab } = useValues(CurationsLogic); + const { dataLoading: curationsDataLoading, meta, selectedPageTab } = useValues(CurationsLogic); const { loadCurations, onSelectPageTab } = useActions(CurationsLogic); const { hasPlatinumLicense } = useValues(LicensingLogic); + const { dataLoading: curationsSettingsDataLoading } = useValues(CurationsSettingsLogic); const OVERVIEW_TAB = { label: i18n.translate( @@ -92,7 +93,7 @@ export const Curations: React.FC = () => { ], tabs: pageTabs, }} - isLoading={dataLoading && !curations.length} + isLoading={curationsSettingsDataLoading || curationsDataLoading} > {selectedPageTab === 'overview' && } {selectedPageTab === 'history' && } diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_overview.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_overview.test.tsx index ff6ee66d8cb10..809157704a14e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_overview.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_overview.test.tsx @@ -19,13 +19,32 @@ import { SuggestionsTable } from '../components/suggestions_table'; import { CurationsOverview } from './curations_overview'; +const MOCK_VALUES = { + // CurationsSettingsLogic + curationsSettings: { + enabled: true, + }, + // CurationsLogic + curations: [ + { + id: 'cur-id-1', + }, + { + id: 'cur-id-2', + }, + ], + // LicensingLogics + hasPlatinumLicense: true, +}; + describe('CurationsOverview', () => { beforeEach(() => { jest.clearAllMocks(); + setMockValues(MOCK_VALUES); }); it('renders an empty message when there are no curations', () => { - setMockValues({ curations: [] }); + setMockValues({ ...MOCK_VALUES, curations: [] }); const wrapper = shallow(); expect(wrapper.find(EmptyState).exists()).toBe(true); @@ -33,6 +52,7 @@ describe('CurationsOverview', () => { it('renders a curations table when there are curations present', () => { setMockValues({ + ...MOCK_VALUES, curations: [ { id: 'cur-id-1', @@ -47,15 +67,36 @@ describe('CurationsOverview', () => { expect(wrapper.find(CurationsTable)).toHaveLength(1); }); - it('renders a suggestions table when the user has a platinum license', () => { - setMockValues({ curations: [], hasPlatinumLicense: true }); + it('renders a suggestions table when the user has a platinum license and curations suggestions enabled', () => { + setMockValues({ + ...MOCK_VALUES, + hasPlatinumLicense: true, + curationsSettings: { + enabled: true, + }, + }); const wrapper = shallow(); expect(wrapper.find(SuggestionsTable).exists()).toBe(true); }); it('doesn\t render a suggestions table when the user has no platinum license', () => { - setMockValues({ curations: [], hasPlatinumLicense: false }); + setMockValues({ + ...MOCK_VALUES, + hasPlatinumLicense: false, + }); + const wrapper = shallow(); + + expect(wrapper.find(SuggestionsTable).exists()).toBe(false); + }); + + it('doesn\t render a suggestions table when the user has disabled suggestions', () => { + setMockValues({ + ...MOCK_VALUES, + curationsSettings: { + enabled: false, + }, + }); const wrapper = shallow(); expect(wrapper.find(SuggestionsTable).exists()).toBe(false); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_overview.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_overview.tsx index 079f0046cb9bf..00593403b08cf 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_overview.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_overview.tsx @@ -16,11 +16,17 @@ import { CurationsTable, EmptyState } from '../components'; import { SuggestionsTable } from '../components/suggestions_table'; import { CurationsLogic } from '../curations_logic'; +import { CurationsSettingsLogic } from './curations_settings'; + export const CurationsOverview: React.FC = () => { const { curations } = useValues(CurationsLogic); const { hasPlatinumLicense } = useValues(LicensingLogic); - const shouldShowSuggestions = hasPlatinumLicense; + const { + curationsSettings: { enabled }, + } = useValues(CurationsSettingsLogic); + + const shouldShowSuggestions = enabled && hasPlatinumLicense; return ( <> diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings.test.tsx index 4b4e11c31d4b8..3b01d1e41c271 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings.test.tsx @@ -17,8 +17,6 @@ import { shallow, ShallowWrapper } from 'enzyme'; import { EuiButtonEmpty, EuiCallOut, EuiSwitch } from '@elastic/eui'; -import { mountWithIntl } from '@kbn/test/jest'; - import { Loading } from '../../../../../shared/loading'; import { EuiButtonTo } from '../../../../../shared/react_router_helpers'; import { DataPanel } from '../../../data_panel'; @@ -46,8 +44,6 @@ const MOCK_VALUES = { const MOCK_ACTIONS = { // CurationsSettingsLogic - loadCurationsSettings: jest.fn(), - onSkipLoadingCurationsSettings: jest.fn(), toggleCurationsEnabled: jest.fn(), toggleCurationsMode: jest.fn(), // LogRetentionLogic @@ -60,14 +56,6 @@ describe('CurationsSettings', () => { setMockActions(MOCK_ACTIONS); }); - it('loads curations and log retention settings on load', () => { - setMockValues(MOCK_VALUES); - mountWithIntl(); - - expect(MOCK_ACTIONS.loadCurationsSettings).toHaveBeenCalled(); - expect(MOCK_ACTIONS.fetchLogRetention).toHaveBeenCalled(); - }); - it('contains a switch to toggle curations settings', () => { let wrapper: ShallowWrapper; @@ -166,50 +154,6 @@ describe('CurationsSettings', () => { expect(wrapper.is(Loading)).toBe(true); }); - describe('loading curation settings based on log retention', () => { - it('loads curation settings when log retention is enabled', () => { - setMockValues({ - ...MOCK_VALUES, - logRetention: { - [LogRetentionOptions.Analytics]: { - enabled: true, - }, - }, - }); - - shallow(); - - expect(MOCK_ACTIONS.loadCurationsSettings).toHaveBeenCalledTimes(1); - }); - - it('skips loading curation settings when log retention is enabled', () => { - setMockValues({ - ...MOCK_VALUES, - logRetention: { - [LogRetentionOptions.Analytics]: { - enabled: false, - }, - }, - }); - - shallow(); - - expect(MOCK_ACTIONS.onSkipLoadingCurationsSettings).toHaveBeenCalledTimes(1); - }); - - it('takes no action if log retention has not yet been loaded', () => { - setMockValues({ - ...MOCK_VALUES, - logRetention: null, - }); - - shallow(); - - expect(MOCK_ACTIONS.loadCurationsSettings).toHaveBeenCalledTimes(0); - expect(MOCK_ACTIONS.onSkipLoadingCurationsSettings).toHaveBeenCalledTimes(0); - }); - }); - describe('when the user has no platinum license', () => { beforeEach(() => { setMockValues({ @@ -218,11 +162,6 @@ describe('CurationsSettings', () => { }); }); - it('it does not fetch log retention', () => { - shallow(); - expect(MOCK_ACTIONS.fetchLogRetention).toHaveBeenCalledTimes(0); - }); - it('shows a CTA to upgrade your license when the user when the user', () => { const wrapper = shallow(); expect(wrapper.is(DataPanel)).toBe(true); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings.tsx index de669298b11d9..a5d4a33d8b870 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useEffect } from 'react'; +import React from 'react'; import { useActions, useValues } from 'kea'; @@ -43,34 +43,12 @@ export const CurationsSettings: React.FC = () => { curationsSettings: { enabled, mode }, dataLoading, } = useValues(CurationsSettingsLogic); - const { - loadCurationsSettings, - onSkipLoadingCurationsSettings, - toggleCurationsEnabled, - toggleCurationsMode, - } = useActions(CurationsSettingsLogic); + const { toggleCurationsEnabled, toggleCurationsMode } = useActions(CurationsSettingsLogic); const { isLogRetentionUpdating, logRetention } = useValues(LogRetentionLogic); - const { fetchLogRetention } = useActions(LogRetentionLogic); const analyticsDisabled = !logRetention?.[LogRetentionOptions.Analytics].enabled; - useEffect(() => { - if (hasPlatinumLicense) { - fetchLogRetention(); - } - }, [hasPlatinumLicense]); - - useEffect(() => { - if (logRetention) { - if (!analyticsDisabled) { - loadCurationsSettings(); - } else { - onSkipLoadingCurationsSettings(); - } - } - }, [logRetention]); - if (!hasPlatinumLicense) return ( Date: Wed, 20 Oct 2021 12:06:50 -0400 Subject: [PATCH 135/204] [Security Solution][CTI] Rule Preview backend update (introduces /preview endpoint) (#112441) Co-authored-by: Davis Plumlee Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../security_solution/common/constants.ts | 3 + .../schemas/request/rule_schemas.ts | 5 + .../components/rules/query_preview/helpers.ts | 8 + .../rules/query_preview/translations.ts | 16 ++ .../rules/step_define_rule/index.test.tsx | 1 + .../rules/step_define_rule/index.tsx | 2 + .../detection_engine/alerts/api.test.ts | 20 ++ .../containers/detection_engine/alerts/api.ts | 13 + .../alerts/use_preview_index.tsx | 15 ++ .../rules/create/index.test.tsx | 1 + .../rules/details/index.test.tsx | 1 + .../security_solution/server/client/client.ts | 4 + .../index/create_preview_index_route.ts | 88 +++++++ .../routes/index/get_signals_template.ts | 12 +- .../routes/index/preview_policy.json | 21 ++ .../routes/rules/preview_rules_route.ts | 226 ++++++++++++++++++ .../create_security_rule_type_wrapper.ts | 53 ++-- .../rule_types/eql/create_eql_alert_type.ts | 6 +- .../build_alert_group_from_sequence.test.ts | 31 +-- .../utils/build_alert_group_from_sequence.ts | 18 +- .../factories/utils/build_bulk_body.ts | 12 +- .../rule_types/factories/wrap_hits_factory.ts | 67 +++--- .../factories/wrap_sequences_factory.ts | 9 +- .../create_indicator_match_alert_type.test.ts | 2 + .../create_indicator_match_alert_type.ts | 6 +- .../rule_types/ml/create_ml_alert_type.ts | 10 +- .../query/create_query_alert_type.test.ts | 3 + .../query/create_query_alert_type.ts | 6 +- .../threshold/create_threshold_alert_type.ts | 6 +- .../lib/detection_engine/rule_types/types.ts | 6 +- .../schemas/rule_schemas.mock.ts | 30 +++ .../detection_engine/schemas/rule_schemas.ts | 7 + .../scripts/post_rule_preview.sh | 32 +++ .../queries/query_preview_threat_match.json | 49 ++++ .../signals/build_bulk_body.test.ts | 48 ++-- .../signals/build_bulk_body.ts | 29 ++- .../signals/build_rule.test.ts | 72 +++--- .../detection_engine/signals/build_rule.ts | 50 ++-- .../signals/bulk_create_factory.ts | 5 +- .../signals/bulk_create_ml_signals.ts | 8 +- .../signals/executors/eql.test.ts | 28 +-- .../detection_engine/signals/executors/eql.ts | 12 +- .../signals/executors/ml.test.ts | 21 +- .../detection_engine/signals/executors/ml.ts | 17 +- .../signals/executors/query.ts | 16 +- .../signals/executors/threat_match.ts | 45 ++-- .../signals/executors/threshold.test.ts | 38 +-- .../signals/executors/threshold.ts | 12 +- .../preview/alert_instance_factory_stub.ts | 51 ++++ .../preview_rule_execution_log_client.ts | 48 ++++ .../signals/search_after_bulk_create.test.ts | 35 +-- .../signals/search_after_bulk_create.ts | 24 +- .../signals/signal_rule_alert_type.test.ts | 54 ++--- .../signals/signal_rule_alert_type.ts | 134 ++++++----- .../threat_mapping/create_threat_signal.ts | 58 ++--- .../threat_mapping/create_threat_signals.ts | 70 +++--- .../signals/threat_mapping/types.ts | 107 ++++----- .../bulk_create_threshold_signals.ts | 9 +- .../lib/detection_engine/signals/types.ts | 6 +- .../detection_engine/signals/utils.test.ts | 184 +++----------- .../lib/detection_engine/signals/utils.ts | 85 ++++--- .../signals/wrap_hits_factory.ts | 18 +- .../signals/wrap_sequences_factory.ts | 9 +- .../security_solution/server/plugin.ts | 33 ++- .../security_solution/server/routes/index.ts | 11 +- .../security_and_spaces/tests/create_ml.ts | 1 - .../tests/generating_signals.ts | 18 +- 67 files changed, 1328 insertions(+), 817 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_preview_index.tsx create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/create_preview_index_route.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/preview_policy.json create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/preview_rules_route.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/scripts/post_rule_preview.sh create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/scripts/rules/queries/query_preview_threat_match.json create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/preview/alert_instance_factory_stub.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/signals/preview/preview_rule_execution_log_client.ts diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts index 442718e0975ee..515f2beb53980 100644 --- a/x-pack/plugins/security_solution/common/constants.ts +++ b/x-pack/plugins/security_solution/common/constants.ts @@ -31,6 +31,7 @@ export const DEFAULT_APP_TIME_RANGE = 'securitySolution:timeDefaults'; export const DEFAULT_APP_REFRESH_INTERVAL = 'securitySolution:refreshIntervalDefaults'; export const DEFAULT_ALERTS_INDEX = '.alerts-security.alerts'; export const DEFAULT_SIGNALS_INDEX = '.siem-signals'; +export const DEFAULT_PREVIEW_INDEX = '.siem-preview-signals'; export const DEFAULT_LISTS_INDEX = '.lists'; export const DEFAULT_ITEMS_INDEX = '.items'; // The DEFAULT_MAX_SIGNALS value exists also in `x-pack/plugins/cases/common/constants.ts` @@ -248,6 +249,8 @@ export const DETECTION_ENGINE_TAGS_URL = `${DETECTION_ENGINE_URL}/tags`; export const DETECTION_ENGINE_RULES_STATUS_URL = `${DETECTION_ENGINE_RULES_URL}/_find_statuses`; export const DETECTION_ENGINE_PREPACKAGED_RULES_STATUS_URL = `${DETECTION_ENGINE_RULES_URL}/prepackaged/_status`; export const DETECTION_ENGINE_RULES_BULK_ACTION = `${DETECTION_ENGINE_RULES_URL}/_bulk_action`; +export const DETECTION_ENGINE_RULES_PREVIEW = `${DETECTION_ENGINE_RULES_URL}/preview`; +export const DETECTION_ENGINE_RULES_PREVIEW_INDEX_URL = `${DETECTION_ENGINE_RULES_PREVIEW}/index`; export const TIMELINE_RESOLVE_URL = '/api/timeline/resolve'; export const TIMELINE_URL = '/api/timeline'; diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/rule_schemas.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/rule_schemas.ts index 12e72fb6fc697..524302b0050dd 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/rule_schemas.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/rule_schemas.ts @@ -362,6 +362,11 @@ export type MachineLearningCreateSchema = CreateSchema< export const createRulesSchema = t.intersection([sharedCreateSchema, createTypeSpecific]); export type CreateRulesSchema = t.TypeOf; +export const previewRulesSchema = t.intersection([ + sharedCreateSchema, + createTypeSpecific, + t.type({ invocationCount: t.number }), +]); type UpdateSchema = SharedUpdateSchema & T; export type EqlUpdateSchema = UpdateSchema>; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/helpers.ts b/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/helpers.ts index 1d3135b8cb34a..3d1ac8a185a59 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/helpers.ts +++ b/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/helpers.ts @@ -29,6 +29,8 @@ export const isNoisy = (hits: number, timeframe: Unit): boolean => { return hits > 1; } else if (timeframe === 'd') { return hits / 24 > 1; + } else if (timeframe === 'w') { + return hits / 168 > 1; } else if (timeframe === 'M') { return hits / 730 > 1; } @@ -48,6 +50,12 @@ export const getTimeframeOptions = (ruleType: Type): EuiSelectOption[] => { { value: 'h', text: 'Last hour' }, { value: 'd', text: 'Last day' }, ]; + } else if (ruleType === 'threat_match') { + return [ + { value: 'h', text: i18n.LAST_HOUR }, + { value: 'd', text: i18n.LAST_DAY }, + { value: 'w', text: i18n.LAST_WEEK }, + ]; } else { return [ { value: 'h', text: 'Last hour' }, diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/translations.ts b/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/translations.ts index 4809a39ef2937..1722a0bdb46d6 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/components/rules/query_preview/translations.ts @@ -7,6 +7,22 @@ import { i18n } from '@kbn/i18n'; +export const LAST_HOUR = i18n.translate('xpack.securitySolution.stepDefineRule.lastHour', { + defaultMessage: 'Last hour', +}); + +export const LAST_DAY = i18n.translate('xpack.securitySolution.stepDefineRule.lastDay', { + defaultMessage: 'Last day', +}); + +export const LAST_WEEK = i18n.translate('xpack.securitySolution.stepDefineRule.lastWeek', { + defaultMessage: 'Last week', +}); + +export const LAST_MONTH = i18n.translate('xpack.securitySolution.stepDefineRule.lastMonth', { + defaultMessage: 'Last month', +}); + export const QUERY_PREVIEW_BUTTON = i18n.translate( 'xpack.securitySolution.stepDefineRule.previewQueryButton', { diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.test.tsx index 7936c24e8635f..26ea6e67e7a47 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.test.tsx @@ -11,6 +11,7 @@ import { shallow } from 'enzyme'; import { StepDefineRule } from './index'; jest.mock('../../../../common/lib/kibana'); +jest.mock('../../../containers/detection_engine/alerts/use_preview_index'); describe('StepDefineRule', () => { it('renders correctly', () => { diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx index 785afa49c9791..8379d21714124 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.tsx @@ -57,6 +57,7 @@ import { EqlQueryBar } from '../eql_query_bar'; import { ThreatMatchInput } from '../threatmatch_input'; import { BrowserField, BrowserFields, useFetchIndex } from '../../../../common/containers/source'; import { PreviewQuery } from '../query_preview'; +import { usePreviewIndex } from '../../../containers/detection_engine/alerts/use_preview_index'; const CommonUseField = getUseField({ component: Field }); @@ -136,6 +137,7 @@ const StepDefineRuleComponent: FC = ({ onSubmit, setForm, }) => { + usePreviewIndex(); const mlCapabilities = useMlCapabilities(); const [openTimelineSearch, setOpenTimelineSearch] = useState(false); const [indexModified, setIndexModified] = useState(false); diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/api.test.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/api.test.ts index fa850ce6b36ea..eab916f2cf85b 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/api.test.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/api.test.ts @@ -21,6 +21,7 @@ import { getUserPrivilege, createSignalIndex, createHostIsolation, + createPreviewIndex, } from './api'; import { coreMock } from '../../../../../../../../src/core/public/mocks'; @@ -165,6 +166,25 @@ describe('Detections Alerts API', () => { }); }); + describe('createPreviewIndex', () => { + beforeEach(() => { + fetchMock.mockClear(); + fetchMock.mockResolvedValue({ acknowledged: true }); + }); + + test('check parameter url', async () => { + await createPreviewIndex(); + expect(fetchMock).toHaveBeenCalledWith('/api/detection_engine/rules/preview/index', { + method: 'POST', + }); + }); + + test('happy path', async () => { + const previewResp = await createPreviewIndex(); + expect(previewResp).toEqual({ acknowledged: true }); + }); + }); + describe('createHostIsolation', () => { const postMock = coreStartMock.http.post; diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/api.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/api.ts index 88882131fed03..98be810fa264f 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/api.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/api.ts @@ -14,6 +14,7 @@ import { DETECTION_ENGINE_INDEX_URL, DETECTION_ENGINE_PRIVILEGES_URL, ALERTS_AS_DATA_FIND_URL, + DETECTION_ENGINE_RULES_PREVIEW_INDEX_URL, } from '../../../../../common/constants'; import { HOST_METADATA_GET_ROUTE } from '../../../../../common/endpoint/constants'; import { KibanaServices } from '../../../../common/lib/kibana'; @@ -132,6 +133,18 @@ export const createSignalIndex = async ({ signal }: BasicSignals): Promise => + KibanaServices.get().http.fetch(DETECTION_ENGINE_RULES_PREVIEW_INDEX_URL, { + method: 'POST', + }); + /** * Get Host Isolation index * diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_preview_index.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_preview_index.tsx new file mode 100644 index 0000000000000..7a35e35acefe8 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/use_preview_index.tsx @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useEffect } from 'react'; +import { createPreviewIndex } from './api'; + +export const usePreviewIndex = () => { + useEffect(() => { + createPreviewIndex(); + }, []); +}; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/index.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/index.test.tsx index 45713b6b0667f..7baf3231f9bee 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/index.test.tsx @@ -27,6 +27,7 @@ jest.mock('react-router-dom', () => { }); jest.mock('../../../../../common/lib/kibana'); jest.mock('../../../../containers/detection_engine/lists/use_lists_config'); +jest.mock('../../../../containers/detection_engine/alerts/use_preview_index'); jest.mock('../../../../../common/components/link_to'); jest.mock('../../../../components/user_info'); jest.mock('../../../../../common/hooks/use_app_toasts'); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.test.tsx index 9c1667e7b4910..006e497a20a7b 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/details/index.test.tsx @@ -85,6 +85,7 @@ jest.mock('react-router-dom', () => { }); jest.mock('../../../../../common/lib/kibana'); +jest.mock('../../../../containers/detection_engine/alerts/use_preview_index'); const mockRedirectLegacyUrl = jest.fn(); const mockGetLegacyUrlConflict = jest.fn(); diff --git a/x-pack/plugins/security_solution/server/client/client.ts b/x-pack/plugins/security_solution/server/client/client.ts index a94a0fa920c65..12a13e06c2ffe 100644 --- a/x-pack/plugins/security_solution/server/client/client.ts +++ b/x-pack/plugins/security_solution/server/client/client.ts @@ -6,18 +6,22 @@ */ import { ConfigType } from '../config'; +import { DEFAULT_PREVIEW_INDEX } from '../../common/constants'; export class AppClient { private readonly signalsIndex: string; private readonly spaceId: string; + private readonly previewIndex: string; constructor(_spaceId: string, private config: ConfigType) { const configuredSignalsIndex = this.config.signalsIndex; this.signalsIndex = `${configuredSignalsIndex}-${_spaceId}`; + this.previewIndex = `${DEFAULT_PREVIEW_INDEX}-${_spaceId}`; this.spaceId = _spaceId; } public getSignalsIndex = (): string => this.signalsIndex; + public getPreviewIndex = (): string => this.previewIndex; public getSpaceId = (): string => this.spaceId; } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/create_preview_index_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/create_preview_index_route.ts new file mode 100644 index 0000000000000..f6f5d394c6a90 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/create_preview_index_route.ts @@ -0,0 +1,88 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + transformError, + getIndexExists, + getPolicyExists, + setPolicy, + createBootstrapIndex, +} from '@kbn/securitysolution-es-utils'; +import type { + AppClient, + SecuritySolutionPluginRouter, + SecuritySolutionRequestHandlerContext, +} from '../../../../types'; +import { DETECTION_ENGINE_RULES_PREVIEW_INDEX_URL } from '../../../../../common/constants'; +import { buildSiemResponse } from '../utils'; +import { getSignalsTemplate, SIGNALS_TEMPLATE_VERSION } from './get_signals_template'; +import previewPolicy from './preview_policy.json'; +import { getIndexVersion } from './get_index_version'; +import { isOutdated } from '../../migrations/helpers'; +import { templateNeedsUpdate } from './check_template_version'; + +export const createPreviewIndexRoute = (router: SecuritySolutionPluginRouter) => { + router.post( + { + path: DETECTION_ENGINE_RULES_PREVIEW_INDEX_URL, + validate: false, + options: { + tags: ['access:securitySolution'], + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + + try { + const siemClient = context.securitySolution?.getAppClient(); + if (!siemClient) { + return siemResponse.error({ statusCode: 404 }); + } + await createPreviewIndex(context, siemClient); + + return response.ok({ body: { acknowledged: true } }); + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; + +export const createPreviewIndex = async ( + context: SecuritySolutionRequestHandlerContext, + siemClient: AppClient +) => { + const esClient = context.core.elasticsearch.client.asCurrentUser; + const index = siemClient.getPreviewIndex(); + + const indexExists = await getIndexExists(esClient, index); + + const policyExists = await getPolicyExists(esClient, index); + if (!policyExists) { + await setPolicy(esClient, index, previewPolicy); + } + + if (await templateNeedsUpdate({ alias: index, esClient })) { + await esClient.indices.putIndexTemplate({ + name: index, + body: getSignalsTemplate(index) as Record, + }); + } + + if (indexExists) { + const indexVersion = await getIndexVersion(esClient, index); + if (isOutdated({ current: indexVersion, target: SIGNALS_TEMPLATE_VERSION })) { + await esClient.indices.rollover({ alias: index }); + } + } else { + await createBootstrapIndex(esClient, index); + } +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/get_signals_template.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/get_signals_template.ts index b7a0521e5c3ce..2c4a1e43cd4b9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/get_signals_template.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/get_signals_template.ts @@ -24,8 +24,8 @@ import signalExtraFields from './signal_extra_fields.json'; @description This value represents the template version assumed by app code. If this number is greater than the user's signals index version, the detections UI will attempt to update the signals template and roll over to - a new signals index. - + a new signals index. + Since we create a new index for new versions, this version on an existing index should never change. If making mappings changes in a patch release, this number should be incremented by 1. @@ -43,8 +43,8 @@ export const SIGNALS_TEMPLATE_VERSION = 57; This version number can change over time on existing indices as we add backwards compatibility fields. - If any .siem-signals- indices have an aliases_version less than this value, the detections - UI will call create_index_route and and go through the index update process. Increment this number if + If any .siem-signals- indices have an aliases_version less than this value, the detections + UI will call create_index_route and and go through the index update process. Increment this number if making changes to the field aliases we use to make signals forwards-compatible. */ export const SIGNALS_FIELD_ALIASES_VERSION = 1; @@ -52,14 +52,14 @@ export const SIGNALS_FIELD_ALIASES_VERSION = 1; /** @constant @type {number} - @description This value represents the minimum required index version (SIGNALS_TEMPLATE_VERSION) for EQL + @description This value represents the minimum required index version (SIGNALS_TEMPLATE_VERSION) for EQL rules to write signals correctly. If the write index has a `version` less than this value, the EQL rule will throw an error on execution. */ export const MIN_EQL_RULE_INDEX_VERSION = 2; export const ALIAS_VERSION_FIELD = 'aliases_version'; -export const getSignalsTemplate = (index: string, spaceId: string, aadIndexAliasName: string) => { +export const getSignalsTemplate = (index: string, spaceId?: string, aadIndexAliasName?: string) => { const fieldAliases = createSignalsFieldAliases(); const template = { index_patterns: [`${index}-*`], diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/preview_policy.json b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/preview_policy.json new file mode 100644 index 0000000000000..d04983095b172 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/preview_policy.json @@ -0,0 +1,21 @@ +{ + "policy": { + "phases": { + "hot": { + "actions": { + "rollover": { + "max_age": "1d", + "max_primary_shard_size": "50gb" + } + }, + "min_age": "0ms" + }, + "delete": { + "min_age": "1d", + "actions": { + "delete": {} + } + } + } + } +} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/preview_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/preview_rules_route.ts new file mode 100644 index 0000000000000..7af4127848bb1 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/preview_rules_route.ts @@ -0,0 +1,226 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import moment from 'moment'; +import uuid from 'uuid'; +import { transformError } from '@kbn/securitysolution-es-utils'; +import { buildSiemResponse } from '../utils'; +import { convertCreateAPIToInternalSchema } from '../../schemas/rule_converters'; +import { RuleParams } from '../../schemas/rule_schemas'; +import { signalRulesAlertType } from '../../signals/signal_rule_alert_type'; +import { createWarningsAndErrors } from '../../signals/preview/preview_rule_execution_log_client'; +import { parseInterval } from '../../signals/utils'; +import { buildMlAuthz } from '../../../machine_learning/authz'; +import { throwHttpError } from '../../../machine_learning/validation'; +import { buildRouteValidation } from '../../../../utils/build_validation/route_validation'; +import { SetupPlugins } from '../../../../plugin'; +import type { SecuritySolutionPluginRouter } from '../../../../types'; +import { createRuleValidateTypeDependents } from '../../../../../common/detection_engine/schemas/request/create_rules_type_dependents'; +import { DETECTION_ENGINE_RULES_PREVIEW } from '../../../../../common/constants'; +import { previewRulesSchema } from '../../../../../common/detection_engine/schemas/request'; +import { RuleExecutionStatus } from '../../../../../common/detection_engine/schemas/common/schemas'; + +import { + AlertInstanceContext, + AlertInstanceState, + AlertTypeState, + parseDuration, +} from '../../../../../../alerting/common'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { ExecutorType } from '../../../../../../alerting/server/types'; +import { AlertInstance } from '../../../../../../alerting/server'; +import { ConfigType } from '../../../../config'; +import { IEventLogService } from '../../../../../../event_log/server'; +import { alertInstanceFactoryStub } from '../../signals/preview/alert_instance_factory_stub'; +import { CreateRuleOptions } from '../../rule_types/types'; + +enum InvocationCount { + HOUR = 1, + DAY = 24, + WEEK = 168, +} + +export const previewRulesRoute = async ( + router: SecuritySolutionPluginRouter, + config: ConfigType, + ml: SetupPlugins['ml'], + security: SetupPlugins['security'], + ruleOptions: CreateRuleOptions +) => { + router.post( + { + path: DETECTION_ENGINE_RULES_PREVIEW, + validate: { + body: buildRouteValidation(previewRulesSchema), + }, + options: { + tags: ['access:securitySolution'], + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + const validationErrors = createRuleValidateTypeDependents(request.body); + if (validationErrors.length) { + return siemResponse.error({ statusCode: 400, body: validationErrors }); + } + try { + const savedObjectsClient = context.core.savedObjects.client; + const siemClient = context.securitySolution?.getAppClient(); + if (!siemClient) { + return siemResponse.error({ statusCode: 404 }); + } + + if (request.body.type !== 'threat_match') { + return response.ok({ body: { errors: ['Not an indicator match rule'] } }); + } + + let invocationCount = request.body.invocationCount; + if ( + ![InvocationCount.HOUR, InvocationCount.DAY, InvocationCount.WEEK].includes( + invocationCount + ) + ) { + return response.ok({ body: { errors: ['Invalid invocation count'] } }); + } + + const internalRule = convertCreateAPIToInternalSchema(request.body, siemClient, false); + const previewRuleParams = internalRule.params; + + const mlAuthz = buildMlAuthz({ + license: context.licensing.license, + ml, + request, + savedObjectsClient, + }); + throwHttpError(await mlAuthz.validateRuleType(internalRule.params.type)); + await context.lists?.getExceptionListClient().createEndpointList(); + + const spaceId = siemClient.getSpaceId(); + const previewIndex = siemClient.getPreviewIndex(); + const previewId = uuid.v4(); + const username = security?.authc.getCurrentUser(request)?.username; + const { previewRuleExecutionLogClient, warningsAndErrorsStore } = createWarningsAndErrors(); + const runState: Record = {}; + + const runExecutors = async < + TParams extends RuleParams, + TState extends AlertTypeState, + TInstanceState extends AlertInstanceState, + TInstanceContext extends AlertInstanceContext, + TActionGroupIds extends string = '' + >( + executor: ExecutorType< + TParams, + TState, + TInstanceState, + TInstanceContext, + TActionGroupIds + >, + ruleTypeId: string, + ruleTypeName: string, + params: TParams, + alertInstanceFactory: ( + id: string + ) => Pick< + AlertInstance, + 'getState' | 'replaceState' | 'scheduleActions' | 'scheduleActionsWithSubGroup' + > + ) => { + let statePreview = runState as TState; + + const startedAt = moment(); + const parsedDuration = parseDuration(internalRule.schedule.interval) ?? 0; + startedAt.subtract(moment.duration(parsedDuration * invocationCount)); + + let previousStartedAt = null; + + const rule = { + ...internalRule, + createdAt: new Date(), + createdBy: username ?? 'preview-created-by', + producer: 'preview-producer', + ruleTypeId, + ruleTypeName, + updatedAt: new Date(), + updatedBy: username ?? 'preview-updated-by', + }; + + while (invocationCount > 0) { + statePreview = (await executor({ + alertId: previewId, + createdBy: rule.createdBy, + name: rule.name, + params, + previousStartedAt, + rule, + services: { + alertInstanceFactory, + savedObjectsClient: context.core.savedObjects.client, + scopedClusterClient: context.core.elasticsearch.client, + }, + spaceId, + startedAt: startedAt.toDate(), + state: statePreview, + tags: [], + updatedBy: rule.updatedBy, + })) as TState; + previousStartedAt = startedAt.toDate(); + startedAt.add(parseInterval(internalRule.schedule.interval)); + invocationCount--; + } + }; + + const signalRuleAlertType = signalRulesAlertType({ + ...ruleOptions, + lists: context.lists, + config, + indexNameOverride: previewIndex, + ruleExecutionLogClientOverride: previewRuleExecutionLogClient, + // unused as we override the ruleExecutionLogClient + eventLogService: {} as unknown as IEventLogService, + eventsTelemetry: undefined, + ml: undefined, + }); + + await runExecutors( + signalRuleAlertType.executor, + signalRuleAlertType.id, + signalRuleAlertType.name, + previewRuleParams, + alertInstanceFactoryStub + ); + + const errors = warningsAndErrorsStore + .filter((item) => item.newStatus === RuleExecutionStatus.failed) + .map((item) => item.message); + + const warnings = warningsAndErrorsStore + .filter( + (item) => + item.newStatus === RuleExecutionStatus['partial failure'] || + item.newStatus === RuleExecutionStatus.warning + ) + .map((item) => item.message); + + return response.ok({ + body: { + previewId, + errors, + warnings, + }, + }); + } catch (err) { + const error = transformError(err as Error); + return siemResponse.error({ + body: { + errors: [error.message], + }, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts index c2e4b926d6375..cda5a82aa8bc4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts @@ -36,7 +36,6 @@ import { bulkCreateFactory, wrapHitsFactory, wrapSequencesFactory } from './fact import { RuleExecutionLogClient, truncateMessageList } from '../rule_execution_log'; import { RuleExecutionStatus } from '../../../../common/detection_engine/schemas/common/schemas'; import { scheduleThrottledNotificationActions } from '../notifications/schedule_throttle_notification_actions'; -import { AlertAttributes } from '../signals/types'; /* eslint-disable complexity */ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = @@ -56,6 +55,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = spaceId, state, updatedBy: updatedByUser, + rule, } = options; let runState = state; const { from, maxSignals, meta, ruleId, timestampOverride, to } = params; @@ -69,17 +69,20 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = eventLogService, underlyingClient: config.ruleExecutionLog.underlyingClient, }); - const ruleSO = await savedObjectsClient.get>( - 'alert', - alertId - ); + + const completeRule = { + ruleConfig: rule, + ruleParams: params, + alertId, + }; const { actions, name, - alertTypeId, schedule: { interval }, - } = ruleSO.attributes; + ruleTypeId, + } = completeRule.ruleConfig; + const refresh = actions.length ? 'wait_for' : false; const buildRuleMessage = buildRuleMessageFactory({ @@ -97,7 +100,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = spaceId, ruleId: alertId, ruleName: name, - ruleType: alertTypeId, + ruleType: ruleTypeId, }; await ruleStatusClient.logStatusChange({ ...basicLogArguments, @@ -108,8 +111,8 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = const notificationRuleParams: NotificationRuleTypeParams = { ...params, - name: name as string, - id: ruleSO.id as string, + name, + id: alertId, } as unknown as NotificationRuleTypeParams; // check if rule has permissions to access given index pattern @@ -181,7 +184,9 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = interval, maxSignals: DEFAULT_MAX_SIGNALS, buildRuleMessage, + startedAt, }); + if (remainingGap.asMilliseconds() > 0) { const gapString = remainingGap.humanize(); const gapMessage = buildRuleMessage( @@ -220,18 +225,18 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = ); const wrapHits = wrapHitsFactory({ - logger, ignoreFields, mergeStrategy, - ruleSO, + completeRule, spaceId, + signalsIndex: '', }); const wrapSequences = wrapSequencesFactory({ logger, ignoreFields, mergeStrategy, - ruleSO, + completeRule, spaceId, }); @@ -245,7 +250,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = bulkCreate, exceptionItems, listClient, - rule: ruleSO, + completeRule, searchAfterSize, tuple, wrapHits, @@ -290,7 +295,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = const resultsLink = getNotificationResultsLink({ from: fromInMs, to: toInMs, - id: ruleSO.id, + id: alertId, kibanaSiemAppUrl: (meta as { kibana_siem_app_url?: string } | undefined) ?.kibana_siem_app_url, }); @@ -299,12 +304,12 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = buildRuleMessage(`Found ${createdSignalsCount} signals for notification.`) ); - if (ruleSO.attributes.throttle != null) { + if (completeRule.ruleConfig.throttle != null) { await scheduleThrottledNotificationActions({ alertInstance: services.alertInstanceFactory(alertId), - throttle: ruleSO.attributes.throttle, + throttle: completeRule.ruleConfig.throttle ?? '', startedAt, - id: ruleSO.id, + id: alertId, kibanaSiemAppUrl: (meta as { kibana_siem_app_url?: string } | undefined) ?.kibana_siem_app_url, outputIndex: ruleDataClient.indexName, @@ -358,12 +363,12 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = ); } else { // NOTE: Since this is throttled we have to call it even on an error condition, otherwise it will "reset" the throttle and fire early - if (ruleSO.attributes.throttle != null) { + if (completeRule.ruleConfig.throttle != null) { await scheduleThrottledNotificationActions({ alertInstance: services.alertInstanceFactory(alertId), - throttle: ruleSO.attributes.throttle, + throttle: completeRule.ruleConfig.throttle ?? '', startedAt, - id: ruleSO.id, + id: completeRule.alertId, kibanaSiemAppUrl: (meta as { kibana_siem_app_url?: string } | undefined) ?.kibana_siem_app_url, outputIndex: ruleDataClient.indexName, @@ -392,12 +397,12 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = } } catch (error) { // NOTE: Since this is throttled we have to call it even on an error condition, otherwise it will "reset" the throttle and fire early - if (ruleSO.attributes.throttle != null) { + if (completeRule.ruleConfig.throttle != null) { await scheduleThrottledNotificationActions({ alertInstance: services.alertInstanceFactory(alertId), - throttle: ruleSO.attributes.throttle, + throttle: completeRule.ruleConfig.throttle ?? '', startedAt, - id: ruleSO.id, + id: completeRule.alertId, kibanaSiemAppUrl: (meta as { kibana_siem_app_url?: string } | undefined) ?.kibana_siem_app_url, outputIndex: ruleDataClient.indexName, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/create_eql_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/create_eql_alert_type.ts index f09f013301dea..8b4f50248b5dd 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/create_eql_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/create_eql_alert_type.ts @@ -7,7 +7,7 @@ import { validateNonExact } from '@kbn/securitysolution-io-ts-utils'; import { EQL_RULE_TYPE_ID } from '../../../../../common/constants'; -import { EqlRuleParams, eqlRuleParams } from '../../schemas/rule_schemas'; +import { CompleteRule, eqlRuleParams, EqlRuleParams } from '../../schemas/rule_schemas'; import { eqlExecutor } from '../../signals/executors/eql'; import { CreateRuleOptions, SecurityAlertType } from '../types'; @@ -50,7 +50,7 @@ export const createEqlAlertType = ( runOpts: { bulkCreate, exceptionItems, - rule, + completeRule, searchAfterSize, tuple, wrapHits, @@ -65,7 +65,7 @@ export const createEqlAlertType = ( exceptionItems, experimentalFeatures, logger, - rule, + completeRule: completeRule as CompleteRule, searchAfterSize, services, tuple, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert_group_from_sequence.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert_group_from_sequence.test.ts index a7accc4ae8a0f..3f6d419e6ddd0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert_group_from_sequence.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert_group_from_sequence.test.ts @@ -9,9 +9,8 @@ import { Logger } from 'kibana/server'; import { ALERT_RULE_CONSUMER } from '@kbn/rule-data-utils'; -import { sampleDocNoSortId } from '../../../signals/__mocks__/es_results'; +import { sampleDocNoSortId, sampleRuleGuid } from '../../../signals/__mocks__/es_results'; import { buildAlertGroupFromSequence } from './build_alert_group_from_sequence'; -import { getRulesSchemaMock } from '../../../../../../common/detection_engine/schemas/response/rules_schema.mocks'; import { ALERT_ANCESTORS, ALERT_BUILDING_BLOCK_TYPE, @@ -19,7 +18,8 @@ import { ALERT_GROUP_ID, } from '../../field_maps/field_names'; import { SERVER_APP_ID } from '../../../../../../common/constants'; -import { getQueryRuleParams } from '../../../schemas/rule_schemas.mock'; +import { getCompleteRuleMock, getQueryRuleParams } from '../../../schemas/rule_schemas.mock'; +import { QueryRuleParams } from '../../../schemas/rule_schemas'; const SPACE_ID = 'space'; @@ -40,24 +40,7 @@ describe('buildAlert', () => { }); test('it builds an alert as expected without original_event if event does not exist', () => { - const rule = getRulesSchemaMock(); - const ruleSO = { - attributes: { - actions: [], - alertTypeId: 'siem.signals', - createdAt: new Date().toISOString(), - createdBy: 'gandalf', - params: getQueryRuleParams(), - schedule: { interval: '1m' }, - throttle: 'derp', - updatedAt: new Date().toISOString(), - updatedBy: 'galadriel', - ...rule, - }, - id: 'abcd', - references: [], - type: 'rule', - }; + const completeRule = getCompleteRuleMock(getQueryRuleParams()); const eqlSequence = { join_keys: [], events: [ @@ -68,7 +51,7 @@ describe('buildAlert', () => { const alertGroup = buildAlertGroupFromSequence( loggerMock, eqlSequence, - ruleSO, + completeRule, 'allFields', SPACE_ID, jest.fn() @@ -128,14 +111,14 @@ describe('buildAlert', () => { depth: 1, id: alertGroup[0]._id, index: '', - rule: 'abcd', + rule: sampleRuleGuid, type: 'signal', }, { depth: 1, id: alertGroup[1]._id, index: '', - rule: 'abcd', + rule: sampleRuleGuid, type: 'signal', }, ]), diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert_group_from_sequence.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert_group_from_sequence.ts index 14e0411522a19..18c02e5bd0804 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert_group_from_sequence.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert_group_from_sequence.ts @@ -9,10 +9,9 @@ import { ALERT_INSTANCE_ID } from '@kbn/rule-data-utils'; import { Logger } from 'kibana/server'; -import { SavedObject } from 'src/core/types'; import type { ConfigType } from '../../../../../config'; import { buildRuleWithoutOverrides } from '../../../signals/build_rule'; -import { AlertAttributes, Ancestor, SignalSource } from '../../../signals/types'; +import { Ancestor, SignalSource } from '../../../signals/types'; import { RACAlert, WrappedRACAlert } from '../../types'; import { buildAlert, buildAncestors, generateAlertId } from './build_alert'; import { buildBulkBody } from './build_bulk_body'; @@ -25,31 +24,32 @@ import { ALERT_GROUP_ID, ALERT_GROUP_INDEX, } from '../../field_maps/field_names'; +import { CompleteRule, RuleParams } from '../../../schemas/rule_schemas'; /** * Takes N raw documents from ES that form a sequence and builds them into N+1 signals ready to be indexed - * one signal for each event in the sequence, and a "shell" signal that ties them all together. All N+1 signals * share the same signal.group.id to make it easy to query them. * @param sequence The raw ES documents that make up the sequence - * @param ruleSO SavedObject representing the rule that found the sequence + * @param completeRule object representing the rule that found the sequence */ export const buildAlertGroupFromSequence = ( logger: Logger, sequence: EqlSequence, - ruleSO: SavedObject, + completeRule: CompleteRule, mergeStrategy: ConfigType['alertMergeStrategy'], spaceId: string | null | undefined, buildReasonMessage: BuildReasonMessage ): WrappedRACAlert[] => { const ancestors: Ancestor[] = sequence.events.flatMap((event) => buildAncestors(event)); - if (ancestors.some((ancestor) => ancestor?.rule === ruleSO.id)) { + if (ancestors.some((ancestor) => ancestor?.rule === completeRule.alertId)) { return []; } let buildingBlocks: RACAlert[] = []; try { buildingBlocks = sequence.events.map((event) => ({ - ...buildBulkBody(spaceId, ruleSO, event, mergeStrategy, [], false, buildReasonMessage), + ...buildBulkBody(spaceId, completeRule, event, mergeStrategy, [], false, buildReasonMessage), [ALERT_BUILDING_BLOCK_TYPE]: 'default', })); } catch (error) { @@ -70,7 +70,7 @@ export const buildAlertGroupFromSequence = ( // Now that we have an array of building blocks for the events in the sequence, // we can build the signal that links the building blocks together // and also insert the group id (which is also the "shell" signal _id) in each building block - const doc = buildAlertRoot(wrappedBuildingBlocks, ruleSO, spaceId, buildReasonMessage); + const doc = buildAlertRoot(wrappedBuildingBlocks, completeRule, spaceId, buildReasonMessage); const sequenceAlert = { _id: generateAlertId(doc), _index: '', @@ -87,11 +87,11 @@ export const buildAlertGroupFromSequence = ( export const buildAlertRoot = ( wrappedBuildingBlocks: WrappedRACAlert[], - ruleSO: SavedObject, + completeRule: CompleteRule, spaceId: string | null | undefined, buildReasonMessage: BuildReasonMessage ): RACAlert => { - const rule = buildRuleWithoutOverrides(ruleSO); + const rule = buildRuleWithoutOverrides(completeRule); const reason = buildReasonMessage({ rule }); const doc = buildAlert(wrappedBuildingBlocks, rule, spaceId, reason); const mergedAlerts = objectArrayIntersection(wrappedBuildingBlocks.map((alert) => alert._source)); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_bulk_body.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_bulk_body.ts index 56ad78594e9fd..d127c3e3bbaad 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_bulk_body.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_bulk_body.ts @@ -6,16 +6,16 @@ */ import { TIMESTAMP } from '@kbn/rule-data-utils'; -import { SavedObject } from 'src/core/types'; import { BaseHit } from '../../../../../../common/detection_engine/types'; import type { ConfigType } from '../../../../../config'; import { buildRuleWithOverrides, buildRuleWithoutOverrides } from '../../../signals/build_rule'; import { BuildReasonMessage } from '../../../signals/reason_formatters'; import { getMergeStrategy } from '../../../signals/source_fields_merging/strategies'; -import { AlertAttributes, SignalSource, SignalSourceHit, SimpleHit } from '../../../signals/types'; +import { SignalSource, SignalSourceHit, SimpleHit } from '../../../signals/types'; import { RACAlert } from '../../types'; import { additionalAlertFields, buildAlert } from './build_alert'; import { filterSource } from './filter_source'; +import { CompleteRule, RuleParams } from '../../../schemas/rule_schemas'; const isSourceDoc = ( hit: SignalSourceHit @@ -28,13 +28,13 @@ const isSourceDoc = ( * "best effort" merged "fields" with the "_source" object, then build the signal object, * then the event object, and finally we strip away any additional temporary data that was added * such as the "threshold_result". - * @param ruleSO The rule saved object to build overrides + * @param completeRule The rule saved object to build overrides * @param doc The SignalSourceHit with "_source", "fields", and additional data such as "threshold_result" * @returns The body that can be added to a bulk call for inserting the signal. */ export const buildBulkBody = ( spaceId: string | null | undefined, - ruleSO: SavedObject, + completeRule: CompleteRule, doc: SimpleHit, mergeStrategy: ConfigType['alertMergeStrategy'], ignoreFields: ConfigType['alertIgnoreFields'], @@ -43,8 +43,8 @@ export const buildBulkBody = ( ): RACAlert => { const mergedDoc = getMergeStrategy(mergeStrategy)({ doc, ignoreFields }); const rule = applyOverrides - ? buildRuleWithOverrides(ruleSO, mergedDoc._source ?? {}) - : buildRuleWithoutOverrides(ruleSO); + ? buildRuleWithOverrides(completeRule, mergedDoc._source ?? {}) + : buildRuleWithoutOverrides(completeRule); const filteredSource = filterSource(mergedDoc); const timestamp = new Date().toISOString(); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/wrap_hits_factory.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/wrap_hits_factory.ts index 69c1821a35edd..744e74a135920 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/wrap_hits_factory.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/wrap_hits_factory.ts @@ -5,54 +5,49 @@ * 2.0. */ -import { Logger } from 'kibana/server'; - -import type { ConfigType } from '../../../../config'; -import { filterDuplicateSignals } from '../../signals/filter_duplicate_signals'; -import { SearchAfterAndBulkCreateParams, SimpleHit, WrapHits } from '../../signals/types'; +import { CompleteRule, RuleParams } from '../../schemas/rule_schemas'; +import { ConfigType } from '../../../../config'; +import { SimpleHit, WrapHits } from '../../signals/types'; import { generateId } from '../../signals/utils'; import { buildBulkBody } from './utils/build_bulk_body'; +import { filterDuplicateSignals } from '../../signals/filter_duplicate_signals'; +import { WrappedRACAlert } from '../types'; export const wrapHitsFactory = ({ - logger, + completeRule, ignoreFields, mergeStrategy, - ruleSO, + signalsIndex, spaceId, }: { - logger: Logger; - ruleSO: SearchAfterAndBulkCreateParams['ruleSO']; - mergeStrategy: ConfigType['alertMergeStrategy']; + completeRule: CompleteRule; ignoreFields: ConfigType['alertIgnoreFields']; + mergeStrategy: ConfigType['alertMergeStrategy']; + signalsIndex: string; spaceId: string | null | undefined; }): WrapHits => (events, buildReasonMessage) => { - try { - const wrappedDocs = events.map((event) => { - return { - _index: '', - _id: generateId( - event._index, - event._id, - String(event._version), - ruleSO.attributes.params.ruleId ?? '' - ), - _source: buildBulkBody( - spaceId, - ruleSO, - event as SimpleHit, - mergeStrategy, - ignoreFields, - true, - buildReasonMessage - ), - }; - }); + const wrappedDocs: WrappedRACAlert[] = events.flatMap((event) => [ + { + _index: signalsIndex, + _id: generateId( + event._index, + event._id, + String(event._version), + completeRule.ruleParams.ruleId ?? '' + ), + _source: buildBulkBody( + spaceId, + completeRule, + event as SimpleHit, + mergeStrategy, + ignoreFields, + true, + buildReasonMessage + ), + }, + ]); - return filterDuplicateSignals(ruleSO.id, wrappedDocs, true); - } catch (error) { - logger.error(error); - return []; - } + return filterDuplicateSignals(completeRule.alertId, wrappedDocs, false); }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/wrap_sequences_factory.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/wrap_sequences_factory.ts index 9315f096552d8..916b7f4801e8e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/wrap_sequences_factory.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/wrap_sequences_factory.ts @@ -7,21 +7,22 @@ import { Logger } from 'kibana/server'; -import { SearchAfterAndBulkCreateParams, WrapSequences } from '../../signals/types'; +import { WrapSequences } from '../../signals/types'; import { buildAlertGroupFromSequence } from './utils/build_alert_group_from_sequence'; import { ConfigType } from '../../../../config'; import { WrappedRACAlert } from '../types'; +import { CompleteRule, RuleParams } from '../../schemas/rule_schemas'; export const wrapSequencesFactory = ({ logger, - ruleSO, + completeRule, ignoreFields, mergeStrategy, spaceId, }: { logger: Logger; - ruleSO: SearchAfterAndBulkCreateParams['ruleSO']; + completeRule: CompleteRule; ignoreFields: ConfigType['alertIgnoreFields']; mergeStrategy: ConfigType['alertMergeStrategy']; spaceId: string | null | undefined; @@ -33,7 +34,7 @@ export const wrapSequencesFactory = ...buildAlertGroupFromSequence( logger, sequence, - ruleSO, + completeRule, mergeStrategy, spaceId, buildReasonMessage diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.test.ts index 1bd3d411adf11..89e8e7f70e4aa 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.test.ts @@ -50,6 +50,8 @@ describe('Indicator Match Alerts', () => { threatQuery: '*:*', to: 'now', type: 'threat_match', + query: '*:*', + language: 'kuery', }; const { services, dependencies, executor } = createRuleTypeMocks('threat_match', params); const securityRuleTypeWrapper = createSecurityRuleTypeWrapper({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.ts index ee0688840811a..ae2a1d4165938 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.ts @@ -7,7 +7,7 @@ import { validateNonExact } from '@kbn/securitysolution-io-ts-utils'; import { INDICATOR_RULE_TYPE_ID } from '../../../../../common/constants'; -import { ThreatRuleParams, threatRuleParams } from '../../schemas/rule_schemas'; +import { CompleteRule, threatRuleParams, ThreatRuleParams } from '../../schemas/rule_schemas'; import { threatMatchExecutor } from '../../signals/executors/threat_match'; import { CreateRuleOptions, SecurityAlertType } from '../types'; @@ -52,7 +52,7 @@ export const createIndicatorMatchAlertType = ( bulkCreate, exceptionItems, listClient, - rule, + completeRule, searchAfterSize, tuple, wrapHits, @@ -69,7 +69,7 @@ export const createIndicatorMatchAlertType = ( eventsTelemetry: undefined, listClient, logger, - rule, + completeRule: completeRule as CompleteRule, searchAfterSize, services, tuple, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/ml/create_ml_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/ml/create_ml_alert_type.ts index 756757c7c9956..afc6995c748c0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/ml/create_ml_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/ml/create_ml_alert_type.ts @@ -7,7 +7,11 @@ import { validateNonExact } from '@kbn/securitysolution-io-ts-utils'; import { ML_RULE_TYPE_ID } from '../../../../../common/constants'; -import { MachineLearningRuleParams, machineLearningRuleParams } from '../../schemas/rule_schemas'; +import { + CompleteRule, + machineLearningRuleParams, + MachineLearningRuleParams, +} from '../../schemas/rule_schemas'; import { mlExecutor } from '../../signals/executors/ml'; import { CreateRuleOptions, SecurityAlertType } from '../types'; @@ -52,7 +56,7 @@ export const createMlAlertType = ( bulkCreate, exceptionItems, listClient, - rule, + completeRule, tuple, wrapHits, }, @@ -67,7 +71,7 @@ export const createMlAlertType = ( listClient, logger, ml, - rule, + completeRule: completeRule as CompleteRule, services, tuple, wrapHits, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.test.ts index 638c40c13cfe2..40ef2b46ed8d9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.test.ts @@ -51,6 +51,7 @@ describe('Custom Query Alerts', () => { index: ['*'], from: 'now-1m', to: 'now', + language: 'kuery', }; services.scopedClusterClient.asCurrentUser.search.mockReturnValue( @@ -95,6 +96,8 @@ describe('Custom Query Alerts', () => { index: ['*'], from: 'now-1m', to: 'now', + language: 'kuery', + type: 'query', }; services.scopedClusterClient.asCurrentUser.search.mockReturnValue( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.ts index aa2b25c422221..1830b6900de22 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.ts @@ -7,7 +7,7 @@ import { validateNonExact } from '@kbn/securitysolution-io-ts-utils'; import { QUERY_RULE_TYPE_ID } from '../../../../../common/constants'; -import { QueryRuleParams, queryRuleParams } from '../../schemas/rule_schemas'; +import { CompleteRule, queryRuleParams, QueryRuleParams } from '../../schemas/rule_schemas'; import { queryExecutor } from '../../signals/executors/query'; import { CreateRuleOptions, SecurityAlertType } from '../types'; @@ -52,7 +52,7 @@ export const createQueryAlertType = ( bulkCreate, exceptionItems, listClient, - rule, + completeRule, searchAfterSize, tuple, wrapHits, @@ -69,7 +69,7 @@ export const createQueryAlertType = ( eventsTelemetry: undefined, listClient, logger, - rule, + completeRule: completeRule as CompleteRule, searchAfterSize, services, tuple, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/create_threshold_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/create_threshold_alert_type.ts index 2b3c1c0a8965b..3fcf5e36709ee 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/create_threshold_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/create_threshold_alert_type.ts @@ -7,7 +7,7 @@ import { validateNonExact } from '@kbn/securitysolution-io-ts-utils'; import { THRESHOLD_RULE_TYPE_ID } from '../../../../../common/constants'; -import { thresholdRuleParams, ThresholdRuleParams } from '../../schemas/rule_schemas'; +import { CompleteRule, thresholdRuleParams, ThresholdRuleParams } from '../../schemas/rule_schemas'; import { thresholdExecutor } from '../../signals/executors/threshold'; import { ThresholdAlertState } from '../../signals/types'; import { CreateRuleOptions, SecurityAlertType } from '../types'; @@ -48,7 +48,7 @@ export const createThresholdAlertType = ( producer: 'security-solution', async executor(execOptions) { const { - runOpts: { buildRuleMessage, bulkCreate, exceptionItems, rule, tuple, wrapHits }, + runOpts: { buildRuleMessage, bulkCreate, exceptionItems, completeRule, tuple, wrapHits }, services, startedAt, state, @@ -60,7 +60,7 @@ export const createThresholdAlertType = ( exceptionItems, experimentalFeatures, logger, - rule, + completeRule: completeRule as CompleteRule, services, startedAt, state, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts index 393cb00939b24..545f00ddeacd8 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts @@ -12,7 +12,6 @@ import { Logger } from '@kbn/logging'; import { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; import { AlertExecutorOptions, AlertType } from '../../../../../alerting/server'; -import { SavedObject } from '../../../../../../../src/core/server'; import { AlertInstanceContext, AlertInstanceState, @@ -26,10 +25,9 @@ import { PersistenceServices, IRuleDataClient } from '../../../../../rule_regist import { BaseHit } from '../../../../common/detection_engine/types'; import { ConfigType } from '../../../config'; import { SetupPlugins } from '../../../plugin'; -import { RuleParams } from '../schemas/rule_schemas'; +import { CompleteRule, RuleParams } from '../schemas/rule_schemas'; import { BuildRuleMessage } from '../signals/rule_messages'; import { - AlertAttributes, BulkCreate, SearchAfterAndBulkCreateReturnType, WrapHits, @@ -57,7 +55,7 @@ export interface RunOpts { bulkCreate: BulkCreate; exceptionItems: ExceptionListItemSchema[]; listClient: ListClient; - rule: SavedObject>; + completeRule: CompleteRule; searchAfterSize: number; tuple: { to: Moment; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/schemas/rule_schemas.mock.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/schemas/rule_schemas.mock.ts index 506f40af2ee79..47b66e7cc3bbb 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/schemas/rule_schemas.mock.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/schemas/rule_schemas.mock.ts @@ -10,12 +10,16 @@ import { getListArrayMock } from '../../../../common/detection_engine/schemas/ty import { getThreatMappingMock } from '../signals/threat_mapping/build_threat_mapping_filter.mock'; import { BaseRuleParams, + CompleteRule, EqlRuleParams, MachineLearningRuleParams, QueryRuleParams, + RuleParams, ThreatRuleParams, ThresholdRuleParams, } from './rule_schemas'; +import { SanitizedRuleConfig } from '../../../../../alerting/common'; +import { sampleRuleGuid } from '../signals/__mocks__/es_results'; const getBaseRuleParams = (): BaseRuleParams => { return { @@ -132,3 +136,29 @@ export const getThreatRuleParams = (): ThreatRuleParams => { itemsPerSearch: undefined, }; }; + +export const getRuleConfigMock = (type: string = 'rule-type'): SanitizedRuleConfig => ({ + actions: [], + enabled: true, + name: 'rule-name', + tags: ['some fake tag 1', 'some fake tag 2'], + createdBy: 'sample user', + createdAt: new Date('2020-03-27T22:55:59.577Z'), + updatedAt: new Date('2020-03-27T22:55:59.577Z'), + updatedBy: 'sample user', + schedule: { + interval: '5m', + }, + throttle: 'no_actions', + consumer: 'sample consumer', + notifyWhen: null, + producer: 'sample producer', + ruleTypeId: `${type}-id`, + ruleTypeName: type, +}); + +export const getCompleteRuleMock = (params: T): CompleteRule => ({ + alertId: sampleRuleGuid, + ruleParams: params, + ruleConfig: getRuleConfigMock(), +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/schemas/rule_schemas.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/schemas/rule_schemas.ts index 578d8c4926b69..365fa962f6277 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/schemas/rule_schemas.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/schemas/rule_schemas.ts @@ -72,6 +72,7 @@ import { EQL_RULE_TYPE_ID, THRESHOLD_RULE_TYPE_ID, } from '../../../../common/constants'; +import { SanitizedRuleConfig } from '../../../../../alerting/common'; const nonEqlLanguages = t.keyof({ kuery: null, lucene: null }); export const baseRuleParams = t.exact( @@ -199,6 +200,12 @@ export type TypeSpecificRuleParams = t.TypeOf; export const ruleParams = t.intersection([baseRuleParams, typeSpecificRuleParams]); export type RuleParams = t.TypeOf; +export interface CompleteRule { + alertId: string; + ruleParams: T; + ruleConfig: SanitizedRuleConfig; +} + export const notifyWhen = t.union([ t.literal('onActionGroupChange'), t.literal('onActiveAlert'), diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/post_rule_preview.sh b/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/post_rule_preview.sh new file mode 100644 index 0000000000000..276e66e588189 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/post_rule_preview.sh @@ -0,0 +1,32 @@ +#!/bin/sh + +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License +# 2.0; you may not use this file except in compliance with the Elastic License +# 2.0. +# + +set -e +./check_env_variables.sh + +# Uses a default if no argument is specified +RULES=(${@:-./rules/queries/query_preview_threat_match.json}) + +# Example: ./post_rule.sh +# Example: ./post_rule.sh ./rules/queries/query_with_rule_id.json +# Example glob: ./post_rule.sh ./rules/queries/* +for RULE in "${RULES[@]}" +do { + [ -e "$RULE" ] || continue + curl -s -k \ + -H 'Content-Type: application/json' \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X POST ${KIBANA_URL}${SPACE_URL}/api/detection_engine/rules/preview \ + -d @${RULE} \ + | jq -S .; +} & +done + +wait diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/rules/queries/query_preview_threat_match.json b/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/rules/queries/query_preview_threat_match.json new file mode 100644 index 0000000000000..64ec976da769b --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/rules/queries/query_preview_threat_match.json @@ -0,0 +1,49 @@ +{ + "name": "preview query", + "description": "preview query for custom query rule", + "false_positives": [ + "https://www.example.com/some-article-about-a-false-positive", + "some text string about why another condition could be a false positive" + ], + "rule_id": "preview-placeholder-rule-id", + "filters": [ + { + "exists": { + "field": "file.hash.md5" + } + } + ], + "enabled": false, + "invocationCount": 500, + "index": ["custom-events"], + "interval": "5m", + "query": "file.hash.md5 : *", + "language": "kuery", + "risk_score": 1, + "tags": ["tag 1", "tag 2", "any tag you want"], + "to": "now", + "from": "now-6m", + "severity": "high", + "type": "threat_match", + "references": [ + "http://www.example.com/some-article-about-attack", + "Some plain text string here explaining why this is a valid thing to look out for" + ], + "timeline_id": "timeline_id", + "timeline_title": "timeline_title", + "threat_index": ["custom-threats"], + "threat_query": "*:*", + "threat_mapping": [ + { + "entries": [ + { + "field": "file.hash.md5", + "type": "mapping", + "value": "threat.indicator.file.hash.md5" + } + ] + } + ], + "note": "# note markdown", + "version": 1 +} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.test.ts index 00286e9f0d3c9..f7c8f1ffd6de7 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.test.ts @@ -9,7 +9,6 @@ import { sampleDocNoSortId, sampleIdGuid, sampleDocWithAncestors, - sampleRuleSO, sampleWrappedSignalHit, expectedRule, } from './__mocks__/es_results'; @@ -22,7 +21,12 @@ import { } from './build_bulk_body'; import { SignalHit, SignalSourceHit } from './types'; import { SIGNALS_TEMPLATE_VERSION } from '../routes/index/get_signals_template'; -import { getQueryRuleParams, getThresholdRuleParams } from '../schemas/rule_schemas.mock'; +import { + getCompleteRuleMock, + getQueryRuleParams, + getThresholdRuleParams, +} from '../schemas/rule_schemas.mock'; +import { QueryRuleParams, ThresholdRuleParams } from '../schemas/rule_schemas'; // This allows us to not have to use ts-expect-error with delete in the code. type SignalHitOptionalTimestamp = Omit & { @@ -35,12 +39,12 @@ describe('buildBulkBody', () => { }); test('bulk body builds well-defined body', () => { - const ruleSO = sampleRuleSO(getQueryRuleParams()); + const completeRule = getCompleteRuleMock(getQueryRuleParams()); const doc = sampleDocNoSortId(); const buildReasonMessage = jest.fn().mockReturnValue('reasonable reason'); delete doc._source.source; const fakeSignalSourceHit: SignalHitOptionalTimestamp = buildBulkBody( - ruleSO, + completeRule, doc, 'missingFields', [], @@ -93,7 +97,7 @@ describe('buildBulkBody', () => { }); test('bulk body builds well-defined body with threshold results', () => { - const ruleSO = sampleRuleSO(getThresholdRuleParams()); + const completeRule = getCompleteRuleMock(getThresholdRuleParams()); const baseDoc = sampleDocNoSortId(); const buildReasonMessage = jest.fn().mockReturnValue('reasonable reason'); const doc: SignalSourceHit & { _source: Required['_source'] } = { @@ -112,7 +116,7 @@ describe('buildBulkBody', () => { }; delete doc._source.source; const fakeSignalSourceHit: SignalHitOptionalTimestamp = buildBulkBody( - ruleSO, + completeRule, doc, 'missingFields', [], @@ -187,7 +191,7 @@ describe('buildBulkBody', () => { }); test('bulk body builds original_event if it exists on the event to begin with', () => { - const ruleSO = sampleRuleSO(getQueryRuleParams()); + const completeRule = getCompleteRuleMock(getQueryRuleParams()); const doc = sampleDocNoSortId(); const buildReasonMessage = jest.fn().mockReturnValue('reasonable reason'); delete doc._source.source; @@ -198,7 +202,7 @@ describe('buildBulkBody', () => { kind: 'event', }; const fakeSignalSourceHit: SignalHitOptionalTimestamp = buildBulkBody( - ruleSO, + completeRule, doc, 'missingFields', [], @@ -260,7 +264,7 @@ describe('buildBulkBody', () => { }); test('bulk body builds original_event if it exists on the event to begin with but no kind information', () => { - const ruleSO = sampleRuleSO(getQueryRuleParams()); + const completeRule = getCompleteRuleMock(getQueryRuleParams()); const doc = sampleDocNoSortId(); const buildReasonMessage = jest.fn().mockReturnValue('reasonable reason'); delete doc._source.source; @@ -270,7 +274,7 @@ describe('buildBulkBody', () => { dataset: 'socket', }; const fakeSignalSourceHit: SignalHitOptionalTimestamp = buildBulkBody( - ruleSO, + completeRule, doc, 'missingFields', [], @@ -331,7 +335,7 @@ describe('buildBulkBody', () => { }); test('bulk body builds original_event if it exists on the event to begin with with only kind information', () => { - const ruleSO = sampleRuleSO(getQueryRuleParams()); + const completeRule = getCompleteRuleMock(getQueryRuleParams()); const doc = sampleDocNoSortId(); const buildReasonMessage = jest.fn().mockReturnValue('reasonable reason'); delete doc._source.source; @@ -339,7 +343,7 @@ describe('buildBulkBody', () => { kind: 'event', }; const fakeSignalSourceHit: SignalHitOptionalTimestamp = buildBulkBody( - ruleSO, + completeRule, doc, 'missingFields', [], @@ -395,7 +399,7 @@ describe('buildBulkBody', () => { }); test('bulk body builds "original_signal" if it exists already as a numeric', () => { - const ruleSO = sampleRuleSO(getQueryRuleParams()); + const completeRule = getCompleteRuleMock(getQueryRuleParams()); const sampleDoc = sampleDocNoSortId(); const buildReasonMessage = jest.fn().mockReturnValue('reasonable reason'); delete sampleDoc._source.source; @@ -407,7 +411,7 @@ describe('buildBulkBody', () => { }, } as unknown as SignalSourceHit; const { '@timestamp': timestamp, ...fakeSignalSourceHit } = buildBulkBody( - ruleSO, + completeRule, doc, 'missingFields', [], @@ -459,7 +463,7 @@ describe('buildBulkBody', () => { }); test('bulk body builds "original_signal" if it exists already as an object', () => { - const ruleSO = sampleRuleSO(getQueryRuleParams()); + const completeRule = getCompleteRuleMock(getQueryRuleParams()); const sampleDoc = sampleDocNoSortId(); const buildReasonMessage = jest.fn().mockReturnValue('reasonable reason'); delete sampleDoc._source.source; @@ -471,7 +475,7 @@ describe('buildBulkBody', () => { }, } as unknown as SignalSourceHit; const { '@timestamp': timestamp, ...fakeSignalSourceHit } = buildBulkBody( - ruleSO, + completeRule, doc, 'missingFields', [], @@ -531,11 +535,11 @@ describe('buildSignalFromSequence', () => { const block2 = sampleWrappedSignalHit(); block2._source.new_key = 'new_key_value'; const blocks = [block1, block2]; - const ruleSO = sampleRuleSO(getQueryRuleParams()); + const completeRule = getCompleteRuleMock(getQueryRuleParams()); const buildReasonMessage = jest.fn().mockReturnValue('reasonable reason'); const signal: SignalHitOptionalTimestamp = buildSignalFromSequence( blocks, - ruleSO, + completeRule, buildReasonMessage ); // Timestamp will potentially always be different so remove it for the test @@ -622,11 +626,11 @@ describe('buildSignalFromSequence', () => { const block2 = sampleWrappedSignalHit(); block2._source['@timestamp'] = '2021-05-20T22:28:46+0000'; block2._source.someKey = 'someOtherValue'; - const ruleSO = sampleRuleSO(getQueryRuleParams()); + const completeRule = getCompleteRuleMock(getQueryRuleParams()); const buildReasonMessage = jest.fn().mockReturnValue('reasonable reason'); const signal: SignalHitOptionalTimestamp = buildSignalFromSequence( [block1, block2], - ruleSO, + completeRule, buildReasonMessage ); // Timestamp will potentially always be different so remove it for the test @@ -712,11 +716,11 @@ describe('buildSignalFromEvent', () => { test('builds a basic signal from a single event', () => { const ancestor = sampleDocWithAncestors().hits.hits[0]; delete ancestor._source.source; - const ruleSO = sampleRuleSO(getQueryRuleParams()); + const completeRule = getCompleteRuleMock(getQueryRuleParams()); const buildReasonMessage = jest.fn().mockReturnValue('reasonable reason'); const signal: SignalHitOptionalTimestamp = buildSignalFromEvent( ancestor, - ruleSO, + completeRule, true, 'missingFields', [], diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.ts index f62e6cebf719b..bccd1f498372e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.ts @@ -6,10 +6,8 @@ */ import { TIMESTAMP } from '@kbn/rule-data-utils'; -import { SavedObject } from 'src/core/types'; import { getMergeStrategy } from './source_fields_merging/strategies'; import { - AlertAttributes, SignalSourceHit, SignalHit, Signal, @@ -24,25 +22,26 @@ import { EqlSequence } from '../../../../common/detection_engine/types'; import { generateSignalId, wrapBuildingBlocks, wrapSignal } from './utils'; import type { ConfigType } from '../../../config'; import { BuildReasonMessage } from './reason_formatters'; +import { CompleteRule, RuleParams } from '../schemas/rule_schemas'; /** * Formats the search_after result for insertion into the signals index. We first create a * "best effort" merged "fields" with the "_source" object, then build the signal object, * then the event object, and finally we strip away any additional temporary data that was added * such as the "threshold_result". - * @param ruleSO The rule saved object to build overrides + * @param completeRule The rule object to build overrides * @param doc The SignalSourceHit with "_source", "fields", and additional data such as "threshold_result" * @returns The body that can be added to a bulk call for inserting the signal. */ export const buildBulkBody = ( - ruleSO: SavedObject, + completeRule: CompleteRule, doc: SignalSourceHit, mergeStrategy: ConfigType['alertMergeStrategy'], ignoreFields: ConfigType['alertIgnoreFields'], buildReasonMessage: BuildReasonMessage ): SignalHit => { const mergedDoc = getMergeStrategy(mergeStrategy)({ doc, ignoreFields }); - const rule = buildRuleWithOverrides(ruleSO, mergedDoc._source ?? {}); + const rule = buildRuleWithOverrides(completeRule, mergedDoc._source ?? {}); const timestamp = new Date().toISOString(); const reason = buildReasonMessage({ mergedDoc, rule }); const signal: Signal = { @@ -74,12 +73,12 @@ export const buildBulkBody = ( * one signal for each event in the sequence, and a "shell" signal that ties them all together. All N+1 signals * share the same signal.group.id to make it easy to query them. * @param sequence The raw ES documents that make up the sequence - * @param ruleSO SavedObject representing the rule that found the sequence + * @param completeRule rule object representing the rule that found the sequence * @param outputIndex Index to write the resulting signals to */ export const buildSignalGroupFromSequence = ( sequence: EqlSequence, - ruleSO: SavedObject, + completeRule: CompleteRule, outputIndex: string, mergeStrategy: ConfigType['alertMergeStrategy'], ignoreFields: ConfigType['alertIgnoreFields'], @@ -89,7 +88,7 @@ export const buildSignalGroupFromSequence = ( sequence.events.map((event) => { const signal = buildSignalFromEvent( event, - ruleSO, + completeRule, false, mergeStrategy, ignoreFields, @@ -103,7 +102,7 @@ export const buildSignalGroupFromSequence = ( if ( wrappedBuildingBlocks.some((block) => - block._source.signal?.ancestors.some((ancestor) => ancestor.rule === ruleSO.id) + block._source.signal?.ancestors.some((ancestor) => ancestor.rule === completeRule.alertId) ) ) { return []; @@ -113,7 +112,7 @@ export const buildSignalGroupFromSequence = ( // we can build the signal that links the building blocks together // and also insert the group id (which is also the "shell" signal _id) in each building block const sequenceSignal = wrapSignal( - buildSignalFromSequence(wrappedBuildingBlocks, ruleSO, buildReasonMessage), + buildSignalFromSequence(wrappedBuildingBlocks, completeRule, buildReasonMessage), outputIndex ); wrappedBuildingBlocks.forEach((block, idx) => { @@ -130,10 +129,10 @@ export const buildSignalGroupFromSequence = ( export const buildSignalFromSequence = ( events: WrappedSignalHit[], - ruleSO: SavedObject, + completeRule: CompleteRule, buildReasonMessage: BuildReasonMessage ): SignalHit => { - const rule = buildRuleWithoutOverrides(ruleSO); + const rule = buildRuleWithoutOverrides(completeRule); const timestamp = new Date().toISOString(); const mergedEvents = objectArrayIntersection(events.map((event) => event._source)); const reason = buildReasonMessage({ rule, mergedDoc: mergedEvents as SignalSourceHit }); @@ -157,7 +156,7 @@ export const buildSignalFromSequence = ( export const buildSignalFromEvent = ( event: BaseSignalHit, - ruleSO: SavedObject, + completeRule: CompleteRule, applyOverrides: boolean, mergeStrategy: ConfigType['alertMergeStrategy'], ignoreFields: ConfigType['alertIgnoreFields'], @@ -165,8 +164,8 @@ export const buildSignalFromEvent = ( ): SignalHit => { const mergedEvent = getMergeStrategy(mergeStrategy)({ doc: event, ignoreFields }); const rule = applyOverrides - ? buildRuleWithOverrides(ruleSO, mergedEvent._source ?? {}) - : buildRuleWithoutOverrides(ruleSO); + ? buildRuleWithOverrides(completeRule, mergedEvent._source ?? {}) + : buildRuleWithoutOverrides(completeRule); const timestamp = new Date().toISOString(); const reason = buildReasonMessage({ mergedDoc: mergedEvent, rule }); const signal: Signal = { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_rule.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_rule.test.ts index 012977da2a00f..9ae51688ee676 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_rule.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_rule.test.ts @@ -6,38 +6,47 @@ */ import { buildRuleWithOverrides, buildRuleWithoutOverrides } from './build_rule'; -import { - sampleDocNoSortId, - expectedRule, - sampleDocSeverity, - sampleRuleSO, -} from './__mocks__/es_results'; +import { sampleDocNoSortId, expectedRule, sampleDocSeverity } from './__mocks__/es_results'; import { RulesSchema } from '../../../../common/detection_engine/schemas/response/rules_schema'; import { INTERNAL_RULE_ID_KEY, INTERNAL_IMMUTABLE_KEY } from '../../../../common/constants'; -import { getQueryRuleParams, getThreatRuleParams } from '../schemas/rule_schemas.mock'; -import { ThreatRuleParams } from '../schemas/rule_schemas'; +import { + getCompleteRuleMock, + getQueryRuleParams, + getThreatRuleParams, +} from '../schemas/rule_schemas.mock'; +import { + CompleteRule, + QueryRuleParams, + RuleParams, + ThreatRuleParams, +} from '../schemas/rule_schemas'; describe('buildRuleWithoutOverrides', () => { + let params: RuleParams; + let completeRule: CompleteRule; + + beforeEach(() => { + params = getQueryRuleParams(); + completeRule = getCompleteRuleMock(params); + }); + test('builds a rule using rule alert', () => { - const ruleSO = sampleRuleSO(getQueryRuleParams()); - const rule = buildRuleWithoutOverrides(ruleSO); + const rule = buildRuleWithoutOverrides(completeRule); expect(rule).toEqual(expectedRule()); }); test('builds a rule and removes internal tags', () => { - const ruleSO = sampleRuleSO(getQueryRuleParams()); - ruleSO.attributes.tags = [ + completeRule.ruleConfig.tags = [ 'some fake tag 1', 'some fake tag 2', `${INTERNAL_RULE_ID_KEY}:rule-1`, `${INTERNAL_IMMUTABLE_KEY}:true`, ]; - const rule = buildRuleWithoutOverrides(ruleSO); + const rule = buildRuleWithoutOverrides(completeRule); expect(rule.tags).toEqual(['some fake tag 1', 'some fake tag 2']); }); test('it builds a rule as expected with filters present', () => { - const ruleSO = sampleRuleSO(getQueryRuleParams()); const ruleFilters = [ { query: 'host.name: Rebecca', @@ -49,8 +58,8 @@ describe('buildRuleWithoutOverrides', () => { query: 'host.name: Braden', }, ]; - ruleSO.attributes.params.filters = ruleFilters; - const rule = buildRuleWithoutOverrides(ruleSO); + completeRule.ruleParams.filters = ruleFilters; + const rule = buildRuleWithoutOverrides(completeRule); expect(rule.filters).toEqual(ruleFilters); }); @@ -90,8 +99,8 @@ describe('buildRuleWithoutOverrides', () => { threatIndex: ['threat_index'], threatLanguage: 'kuery', }; - const ruleSO = sampleRuleSO(ruleParams); - const threatMatchRule = buildRuleWithoutOverrides(ruleSO); + const threatMatchCompleteRule = getCompleteRuleMock(ruleParams); + const threatMatchRule = buildRuleWithoutOverrides(threatMatchCompleteRule); const expected: Partial = { threat_mapping: ruleParams.threatMapping, threat_filters: ruleParams.threatFilters, @@ -105,10 +114,17 @@ describe('buildRuleWithoutOverrides', () => { }); describe('buildRuleWithOverrides', () => { + let params: RuleParams; + let completeRule: CompleteRule; + + beforeEach(() => { + params = getQueryRuleParams(); + completeRule = getCompleteRuleMock(params); + }); + test('it applies rule name override in buildRule', () => { - const ruleSO = sampleRuleSO(getQueryRuleParams()); - ruleSO.attributes.params.ruleNameOverride = 'someKey'; - const rule = buildRuleWithOverrides(ruleSO, sampleDocNoSortId()._source); + completeRule.ruleParams.ruleNameOverride = 'someKey'; + const rule = buildRuleWithOverrides(completeRule, sampleDocNoSortId()._source!); const expected = { ...expectedRule(), name: 'someValue', @@ -123,8 +139,7 @@ describe('buildRuleWithOverrides', () => { test('it applies risk score override in buildRule', () => { const newRiskScore = 79; - const ruleSO = sampleRuleSO(getQueryRuleParams()); - ruleSO.attributes.params.riskScoreMapping = [ + completeRule.ruleParams.riskScoreMapping = [ { field: 'new_risk_score', // value and risk_score aren't used for anything but are required in the schema @@ -135,11 +150,11 @@ describe('buildRuleWithOverrides', () => { ]; const doc = sampleDocNoSortId(); doc._source.new_risk_score = newRiskScore; - const rule = buildRuleWithOverrides(ruleSO, doc._source); + const rule = buildRuleWithOverrides(completeRule, doc._source!); const expected = { ...expectedRule(), risk_score: newRiskScore, - risk_score_mapping: ruleSO.attributes.params.riskScoreMapping, + risk_score_mapping: completeRule.ruleParams.riskScoreMapping, meta: { riskScoreOverridden: true, someMeta: 'someField', @@ -150,8 +165,7 @@ describe('buildRuleWithOverrides', () => { test('it applies severity override in buildRule', () => { const eventSeverity = '42'; - const ruleSO = sampleRuleSO(getQueryRuleParams()); - ruleSO.attributes.params.severityMapping = [ + completeRule.ruleParams.severityMapping = [ { field: 'event.severity', value: eventSeverity, @@ -160,11 +174,11 @@ describe('buildRuleWithOverrides', () => { }, ]; const doc = sampleDocSeverity(Number(eventSeverity)); - const rule = buildRuleWithOverrides(ruleSO, doc._source!); + const rule = buildRuleWithOverrides(completeRule, doc._source!); const expected = { ...expectedRule(), severity: 'critical', - severity_mapping: ruleSO.attributes.params.severityMapping, + severity_mapping: completeRule.ruleParams.severityMapping, meta: { severityOverrideField: 'event.severity', someMeta: 'someField', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_rule.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_rule.ts index 55f22188a7ec8..ab40ce330370c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_rule.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_rule.ts @@ -5,41 +5,53 @@ * 2.0. */ -import { SavedObject } from 'src/core/types'; import { RulesSchema } from '../../../../common/detection_engine/schemas/response/rules_schema'; import { buildRiskScoreFromMapping } from './mappings/build_risk_score_from_mapping'; -import { AlertAttributes, SignalSource } from './types'; +import { SignalSource } from './types'; import { buildSeverityFromMapping } from './mappings/build_severity_from_mapping'; import { buildRuleNameFromMapping } from './mappings/build_rule_name_from_mapping'; -import { RuleParams } from '../schemas/rule_schemas'; +import { CompleteRule, RuleParams } from '../schemas/rule_schemas'; import { commonParamsCamelToSnake, typeSpecificCamelToSnake } from '../schemas/rule_converters'; import { transformTags } from '../routes/rules/utils'; +import { transformAlertToRuleAction } from '../../../../common/detection_engine/transform_actions'; -export const buildRuleWithoutOverrides = (ruleSO: SavedObject): RulesSchema => { - const ruleParams = ruleSO.attributes.params; +export const buildRuleWithoutOverrides = (completeRule: CompleteRule): RulesSchema => { + const ruleParams = completeRule.ruleParams; + const { + actions, + schedule, + name, + tags, + enabled, + createdBy, + updatedBy, + throttle, + createdAt, + updatedAt, + } = completeRule.ruleConfig; return { - id: ruleSO.id, - actions: ruleSO.attributes.actions, - interval: ruleSO.attributes.schedule.interval, - name: ruleSO.attributes.name, - tags: transformTags(ruleSO.attributes.tags), - enabled: ruleSO.attributes.enabled, - created_by: ruleSO.attributes.createdBy, - updated_by: ruleSO.attributes.updatedBy, - throttle: ruleSO.attributes.throttle, - created_at: ruleSO.attributes.createdAt, - updated_at: ruleSO.updated_at ?? '', + actions: actions.map(transformAlertToRuleAction), + created_at: createdAt.toISOString(), + created_by: createdBy ?? '', + enabled, + id: completeRule.alertId, + interval: schedule.interval, + name, + tags: transformTags(tags), + throttle: throttle ?? undefined, + updated_at: updatedAt.toISOString(), + updated_by: updatedBy ?? '', ...commonParamsCamelToSnake(ruleParams), ...typeSpecificCamelToSnake(ruleParams), }; }; export const buildRuleWithOverrides = ( - ruleSO: SavedObject, + completeRule: CompleteRule, eventSource: SignalSource ): RulesSchema => { - const ruleWithoutOverrides = buildRuleWithoutOverrides(ruleSO); - return applyRuleOverrides(ruleWithoutOverrides, eventSource, ruleSO.attributes.params); + const ruleWithoutOverrides = buildRuleWithoutOverrides(completeRule); + return applyRuleOverrides(ruleWithoutOverrides, eventSource, completeRule.ruleParams); }; export const applyRuleOverrides = ( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/bulk_create_factory.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/bulk_create_factory.ts index 29790bb08b8f8..0d08008be72bc 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/bulk_create_factory.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/bulk_create_factory.ts @@ -27,7 +27,8 @@ export const bulkCreateFactory = logger: Logger, esClient: ElasticsearchClient, buildRuleMessage: BuildRuleMessage, - refreshForBulkCreate: RefreshTypes + refreshForBulkCreate: RefreshTypes, + indexNameOverride?: string ) => async (wrappedDocs: Array>): Promise> => { if (wrappedDocs.length === 0) { @@ -43,7 +44,7 @@ export const bulkCreateFactory = const bulkBody = wrappedDocs.flatMap((wrappedDoc) => [ { create: { - _index: wrappedDoc._index, + _index: indexNameOverride ?? wrappedDoc._index, _id: wrappedDoc._id, }, }, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/bulk_create_ml_signals.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/bulk_create_ml_signals.ts index c2ac2a031ca9b..00acd55234ad2 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/bulk_create_ml_signals.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/bulk_create_ml_signals.ts @@ -8,7 +8,7 @@ import type { estypes } from '@elastic/elasticsearch'; import { flow, omit } from 'lodash/fp'; import set from 'set-value'; -import { Logger, SavedObject } from '../../../../../../../src/core/server'; +import { Logger } from '../../../../../../../src/core/server'; import { AlertInstanceContext, AlertInstanceState, @@ -17,13 +17,13 @@ import { import { GenericBulkCreateResponse } from './bulk_create_factory'; import { AnomalyResults, Anomaly } from '../../machine_learning'; import { BuildRuleMessage } from './rule_messages'; -import { AlertAttributes, BulkCreate, WrapHits } from './types'; -import { MachineLearningRuleParams } from '../schemas/rule_schemas'; +import { BulkCreate, WrapHits } from './types'; +import { CompleteRule, MachineLearningRuleParams } from '../schemas/rule_schemas'; import { buildReasonMessageForMlAlert } from './reason_formatters'; interface BulkCreateMlSignalsParams { someResult: AnomalyResults; - ruleSO: SavedObject>; + completeRule: CompleteRule; services: AlertServices; logger: Logger; id: string; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/eql.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/eql.test.ts index e9ca2daa22b08..2f5aaec5ea43f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/eql.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/eql.test.ts @@ -11,12 +11,13 @@ import { alertsMock, AlertServicesMock } from '../../../../../../alerting/server import { eqlExecutor } from './eql'; import { getExceptionListItemSchemaMock } from '../../../../../../lists/common/schemas/response/exception_list_item_schema.mock'; import { getEntryListMock } from '../../../../../../lists/common/schemas/types/entry_list.mock'; -import { getEqlRuleParams } from '../../schemas/rule_schemas.mock'; +import { getCompleteRuleMock, getEqlRuleParams } from '../../schemas/rule_schemas.mock'; import { getIndexVersion } from '../../routes/index/get_index_version'; import { SIGNALS_TEMPLATE_VERSION } from '../../routes/index/get_signals_template'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { elasticsearchClientMock } from 'src/core/server/elasticsearch/client/mocks'; import { allowedExperimentalValues } from '../../../../../common/experimental_features'; +import { EqlRuleParams } from '../../schemas/rule_schemas'; jest.mock('../../routes/index/get_index_version'); @@ -26,28 +27,7 @@ describe('eql_executor', () => { let alertServices: AlertServicesMock; (getIndexVersion as jest.Mock).mockReturnValue(SIGNALS_TEMPLATE_VERSION); const params = getEqlRuleParams(); - const eqlSO = { - id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', - type: 'alert', - version: '1', - updated_at: '2020-03-27T22:55:59.577Z', - attributes: { - actions: [], - alertTypeId: 'siem.signals', - enabled: true, - name: 'rule-name', - tags: ['some fake tag 1', 'some fake tag 2'], - createdBy: 'sample user', - createdAt: '2020-03-27T22:55:59.577Z', - updatedBy: 'sample user', - schedule: { - interval: '5m', - }, - throttle: 'no_actions', - params, - }, - references: [], - }; + const eqlCompleteRule = getCompleteRuleMock(params); const tuple = { from: dateMath.parse(params.from)!, to: dateMath.parse(params.to)!, @@ -72,7 +52,7 @@ describe('eql_executor', () => { it('should set a warning when exception list for eql rule contains value list exceptions', async () => { const exceptionItems = [getExceptionListItemSchemaMock({ entries: [getEntryListMock()] })]; const response = await eqlExecutor({ - rule: eqlSO, + completeRule: eqlCompleteRule, tuple, exceptionItems, experimentalFeatures: allowedExperimentalValues, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/eql.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/eql.ts index 047495031a6df..5317c508b203e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/eql.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/eql.ts @@ -7,7 +7,6 @@ import { ApiResponse } from '@elastic/elasticsearch'; import { performance } from 'perf_hooks'; -import { SavedObject } from 'src/core/types'; import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; import { Logger } from 'src/core/server'; import { @@ -20,11 +19,9 @@ import { hasLargeValueItem } from '../../../../../common/detection_engine/utils' import { isOutdated } from '../../migrations/helpers'; import { getIndexVersion } from '../../routes/index/get_index_version'; import { MIN_EQL_RULE_INDEX_VERSION } from '../../routes/index/get_signals_template'; -import { EqlRuleParams } from '../../schemas/rule_schemas'; import { getInputIndex } from '../get_input_output_index'; import { - AlertAttributes, BulkCreate, WrapHits, WrapSequences, @@ -36,9 +33,10 @@ import { import { createSearchAfterReturnType, makeFloatString } from '../utils'; import { ExperimentalFeatures } from '../../../../../common/experimental_features'; import { buildReasonMessageForEqlAlert } from '../reason_formatters'; +import { CompleteRule, EqlRuleParams } from '../../schemas/rule_schemas'; export const eqlExecutor = async ({ - rule, + completeRule, tuple, exceptionItems, experimentalFeatures, @@ -50,7 +48,7 @@ export const eqlExecutor = async ({ wrapHits, wrapSequences, }: { - rule: SavedObject>; + completeRule: CompleteRule; tuple: RuleRangeTuple; exceptionItems: ExceptionListItemSchema[]; experimentalFeatures: ExperimentalFeatures; @@ -63,7 +61,9 @@ export const eqlExecutor = async ({ wrapSequences: WrapSequences; }): Promise => { const result = createSearchAfterReturnType(); - const ruleParams = rule.attributes.params; + + const ruleParams = completeRule.ruleParams; + if (hasLargeValueItem(exceptionItems)) { result.warningMessages.push( 'Exceptions that use "is in list" or "is not in list" operators are not applied to EQL rules' diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/ml.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/ml.test.ts index 89c1392cb67ba..9b93ba182785f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/ml.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/ml.test.ts @@ -10,13 +10,13 @@ import { loggingSystemMock } from 'src/core/server/mocks'; import { alertsMock, AlertServicesMock } from '../../../../../../alerting/server/mocks'; import { mlExecutor } from './ml'; import { getExceptionListItemSchemaMock } from '../../../../../../lists/common/schemas/response/exception_list_item_schema.mock'; -import { getMlRuleParams } from '../../schemas/rule_schemas.mock'; +import { getCompleteRuleMock, getMlRuleParams } from '../../schemas/rule_schemas.mock'; import { buildRuleMessageFactory } from '../rule_messages'; import { getListClientMock } from '../../../../../../lists/server/services/lists/list_client.mock'; import { findMlSignals } from '../find_ml_signals'; import { bulkCreateMlSignals } from '../bulk_create_ml_signals'; import { mlPluginServerMock } from '../../../../../../ml/server/mocks'; -import { sampleRuleSO } from '../__mocks__/es_results'; +import { MachineLearningRuleParams } from '../../schemas/rule_schemas'; jest.mock('../find_ml_signals'); jest.mock('../bulk_create_ml_signals'); @@ -28,17 +28,18 @@ describe('ml_executor', () => { let logger: ReturnType; let alertServices: AlertServicesMock; const params = getMlRuleParams(); - const mlSO = sampleRuleSO(params); + const mlCompleteRule = getCompleteRuleMock(params); + const tuple = { from: dateMath.parse(params.from)!, to: dateMath.parse(params.to)!, maxSignals: params.maxSignals, }; const buildRuleMessage = buildRuleMessageFactory({ - id: mlSO.id, - ruleId: mlSO.attributes.params.ruleId, - name: mlSO.attributes.name, - index: mlSO.attributes.params.outputIndex, + id: mlCompleteRule.alertId, + ruleId: mlCompleteRule.ruleParams.ruleId, + name: mlCompleteRule.ruleConfig.name, + index: mlCompleteRule.ruleParams.outputIndex, }); beforeEach(() => { @@ -66,7 +67,7 @@ describe('ml_executor', () => { it('should throw an error if ML plugin was not available', async () => { await expect( mlExecutor({ - rule: mlSO, + completeRule: mlCompleteRule, tuple, ml: undefined, exceptionItems, @@ -83,7 +84,7 @@ describe('ml_executor', () => { it('should record a partial failure if Machine learning job summary was null', async () => { jobsSummaryMock.mockResolvedValue([]); const response = await mlExecutor({ - rule: mlSO, + completeRule: mlCompleteRule, tuple, ml: mlMock, exceptionItems, @@ -109,7 +110,7 @@ describe('ml_executor', () => { ]); const response = await mlExecutor({ - rule: mlSO, + completeRule: mlCompleteRule, tuple, ml: mlMock, exceptionItems, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/ml.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/ml.ts index e5776899e4942..155334709e980 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/ml.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/ml.ts @@ -6,7 +6,6 @@ */ import { KibanaRequest, Logger } from 'src/core/server'; -import { SavedObject } from 'src/core/types'; import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; import { AlertInstanceContext, @@ -15,17 +14,17 @@ import { } from '../../../../../../alerting/server'; import { ListClient } from '../../../../../../lists/server'; import { isJobStarted } from '../../../../../common/machine_learning/helpers'; -import { SetupPlugins } from '../../../../plugin'; -import { MachineLearningRuleParams } from '../../schemas/rule_schemas'; +import { CompleteRule, MachineLearningRuleParams } from '../../schemas/rule_schemas'; import { bulkCreateMlSignals } from '../bulk_create_ml_signals'; import { filterEventsAgainstList } from '../filters/filter_events_against_list'; import { findMlSignals } from '../find_ml_signals'; import { BuildRuleMessage } from '../rule_messages'; -import { AlertAttributes, BulkCreate, RuleRangeTuple, WrapHits } from '../types'; +import { BulkCreate, RuleRangeTuple, WrapHits } from '../types'; import { createErrorsFromShard, createSearchAfterReturnType, mergeReturns } from '../utils'; +import { SetupPlugins } from '../../../../plugin'; export const mlExecutor = async ({ - rule, + completeRule, tuple, ml, listClient, @@ -36,7 +35,7 @@ export const mlExecutor = async ({ bulkCreate, wrapHits, }: { - rule: SavedObject>; + completeRule: CompleteRule; tuple: RuleRangeTuple; ml: SetupPlugins['ml']; listClient: ListClient; @@ -48,7 +47,7 @@ export const mlExecutor = async ({ wrapHits: WrapHits; }) => { const result = createSearchAfterReturnType(); - const ruleParams = rule.attributes.params; + const ruleParams = completeRule.ruleParams; if (ml == null) { throw new Error('ML plugin unavailable during rule execution'); } @@ -110,10 +109,10 @@ export const mlExecutor = async ({ const { success, errors, bulkCreateDuration, createdItemsCount, createdItems } = await bulkCreateMlSignals({ someResult: filteredAnomalyResults, - ruleSO: rule, + completeRule, services, logger, - id: rule.id, + id: completeRule.alertId, signalsIndex: ruleParams.outputIndex, buildRuleMessage, bulkCreate, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/query.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/query.ts index f281475fe59eb..2bee175f357f3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/query.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/query.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { SavedObject } from 'src/core/types'; import { Logger } from 'src/core/server'; import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; import { @@ -17,15 +16,15 @@ import { ListClient } from '../../../../../../lists/server'; import { getFilter } from '../get_filter'; import { getInputIndex } from '../get_input_output_index'; import { searchAfterAndBulkCreate } from '../search_after_bulk_create'; -import { AlertAttributes, RuleRangeTuple, BulkCreate, WrapHits } from '../types'; +import { RuleRangeTuple, BulkCreate, WrapHits } from '../types'; import { TelemetryEventsSender } from '../../../telemetry/sender'; import { BuildRuleMessage } from '../rule_messages'; -import { QueryRuleParams, SavedQueryRuleParams } from '../../schemas/rule_schemas'; +import { CompleteRule, SavedQueryRuleParams, QueryRuleParams } from '../../schemas/rule_schemas'; import { ExperimentalFeatures } from '../../../../../common/experimental_features'; import { buildReasonMessageForQueryAlert } from '../reason_formatters'; export const queryExecutor = async ({ - rule, + completeRule, tuple, listClient, exceptionItems, @@ -39,7 +38,7 @@ export const queryExecutor = async ({ bulkCreate, wrapHits, }: { - rule: SavedObject>; + completeRule: CompleteRule; tuple: RuleRangeTuple; listClient: ListClient; exceptionItems: ExceptionListItemSchema[]; @@ -53,7 +52,8 @@ export const queryExecutor = async ({ bulkCreate: BulkCreate; wrapHits: WrapHits; }) => { - const ruleParams = rule.attributes.params; + const ruleParams = completeRule.ruleParams; + const inputIndex = await getInputIndex({ experimentalFeatures, services, @@ -76,11 +76,11 @@ export const queryExecutor = async ({ tuple, listClient, exceptionsList: exceptionItems, - ruleSO: rule, + completeRule, services, logger, eventsTelemetry, - id: rule.id, + id: completeRule.alertId, inputIndexPattern: inputIndex, signalsIndex: ruleParams.outputIndex, filter: esFilter, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threat_match.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threat_match.ts index 37b2c53636cfd..f2e2590ac1e2d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threat_match.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threat_match.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { SavedObject } from 'src/core/types'; import { Logger } from 'src/core/server'; import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; import { @@ -15,15 +14,15 @@ import { } from '../../../../../../alerting/server'; import { ListClient } from '../../../../../../lists/server'; import { getInputIndex } from '../get_input_output_index'; -import { RuleRangeTuple, AlertAttributes, BulkCreate, WrapHits } from '../types'; +import { RuleRangeTuple, BulkCreate, WrapHits } from '../types'; import { TelemetryEventsSender } from '../../../telemetry/sender'; import { BuildRuleMessage } from '../rule_messages'; import { createThreatSignals } from '../threat_mapping/create_threat_signals'; -import { ThreatRuleParams } from '../../schemas/rule_schemas'; +import { CompleteRule, ThreatRuleParams } from '../../schemas/rule_schemas'; import { ExperimentalFeatures } from '../../../../../common/experimental_features'; export const threatMatchExecutor = async ({ - rule, + completeRule, tuple, listClient, exceptionItems, @@ -37,7 +36,7 @@ export const threatMatchExecutor = async ({ bulkCreate, wrapHits, }: { - rule: SavedObject>; + completeRule: CompleteRule; tuple: RuleRangeTuple; listClient: ListClient; exceptionItems: ExceptionListItemSchema[]; @@ -51,7 +50,7 @@ export const threatMatchExecutor = async ({ bulkCreate: BulkCreate; wrapHits: WrapHits; }) => { - const ruleParams = rule.attributes.params; + const ruleParams = completeRule.ruleParams; const inputIndex = await getInputIndex({ experimentalFeatures, services, @@ -59,32 +58,32 @@ export const threatMatchExecutor = async ({ index: ruleParams.index, }); return createThreatSignals({ - tuple, - threatMapping: ruleParams.threatMapping, - query: ruleParams.query, - inputIndex, - type: ruleParams.type, + alertId: completeRule.alertId, + buildRuleMessage, + bulkCreate, + completeRule, + concurrentSearches: ruleParams.concurrentSearches ?? 1, + eventsTelemetry, + exceptionItems, filters: ruleParams.filters ?? [], + inputIndex, + itemsPerSearch: ruleParams.itemsPerSearch ?? 9000, language: ruleParams.language, - savedId: ruleParams.savedId, - services, - exceptionItems, listClient, logger, - eventsTelemetry, - alertId: rule.id, outputIndex: ruleParams.outputIndex, - ruleSO: rule, + query: ruleParams.query, + savedId: ruleParams.savedId, searchAfterSize, + services, threatFilters: ruleParams.threatFilters ?? [], - threatQuery: ruleParams.threatQuery, - threatLanguage: ruleParams.threatLanguage, - buildRuleMessage, threatIndex: ruleParams.threatIndex, threatIndicatorPath: ruleParams.threatIndicatorPath, - concurrentSearches: ruleParams.concurrentSearches ?? 1, - itemsPerSearch: ruleParams.itemsPerSearch ?? 9000, - bulkCreate, + threatLanguage: ruleParams.threatLanguage, + threatMapping: ruleParams.threatMapping, + threatQuery: ruleParams.threatQuery, + tuple, + type: ruleParams.type, wrapHits, }); }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.test.ts index 11145405dcc99..e01e3498c2c7a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.test.ts @@ -13,48 +13,30 @@ import { alertsMock, AlertServicesMock } from '../../../../../../alerting/server import { thresholdExecutor } from './threshold'; import { getExceptionListItemSchemaMock } from '../../../../../../lists/common/schemas/response/exception_list_item_schema.mock'; import { getEntryListMock } from '../../../../../../lists/common/schemas/types/entry_list.mock'; -import { getThresholdRuleParams } from '../../schemas/rule_schemas.mock'; +import { getThresholdRuleParams, getCompleteRuleMock } from '../../schemas/rule_schemas.mock'; import { buildRuleMessageFactory } from '../rule_messages'; import { sampleEmptyDocSearchResults } from '../__mocks__/es_results'; import { allowedExperimentalValues } from '../../../../../common/experimental_features'; +import { ThresholdRuleParams } from '../../schemas/rule_schemas'; describe('threshold_executor', () => { const version = '8.0.0'; let logger: ReturnType; let alertServices: AlertServicesMock; const params = getThresholdRuleParams(); - const thresholdSO = { - id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', - type: 'alert', - version: '1', - updated_at: '2020-03-27T22:55:59.577Z', - attributes: { - actions: [], - alertTypeId: 'siem.signals', - enabled: true, - name: 'rule-name', - tags: ['some fake tag 1', 'some fake tag 2'], - createdBy: 'sample user', - createdAt: '2020-03-27T22:55:59.577Z', - updatedBy: 'sample user', - schedule: { - interval: '5m', - }, - throttle: 'no_actions', - params, - }, - references: [], - }; + + const thresholdCompleteRule = getCompleteRuleMock(params); + const tuple = { from: dateMath.parse(params.from)!, to: dateMath.parse(params.to)!, maxSignals: params.maxSignals, }; const buildRuleMessage = buildRuleMessageFactory({ - id: thresholdSO.id, - ruleId: thresholdSO.attributes.params.ruleId, - name: thresholdSO.attributes.name, - index: thresholdSO.attributes.params.outputIndex, + id: thresholdCompleteRule.alertId, + ruleId: thresholdCompleteRule.ruleParams.ruleId, + name: thresholdCompleteRule.ruleConfig.name, + index: thresholdCompleteRule.ruleParams.outputIndex, }); beforeEach(() => { @@ -69,7 +51,7 @@ describe('threshold_executor', () => { it('should set a warning when exception list for threshold rule contains value list exceptions', async () => { const exceptionItems = [getExceptionListItemSchemaMock({ entries: [getEntryListMock()] })]; const response = await thresholdExecutor({ - rule: thresholdSO, + completeRule: thresholdCompleteRule, tuple, exceptionItems, experimentalFeatures: allowedExperimentalValues, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.ts index 02cad1e8e508c..1550caba9434a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.ts @@ -9,7 +9,6 @@ import { SearchHit } from '@elastic/elasticsearch/api/types'; import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; import { Logger } from 'src/core/server'; -import { SavedObject } from 'src/core/types'; import { AlertInstanceContext, @@ -17,7 +16,7 @@ import { AlertServices, } from '../../../../../../alerting/server'; import { hasLargeValueItem } from '../../../../../common/detection_engine/utils'; -import { ThresholdRuleParams } from '../../schemas/rule_schemas'; +import { CompleteRule, ThresholdRuleParams } from '../../schemas/rule_schemas'; import { getFilter } from '../get_filter'; import { getInputIndex } from '../get_input_output_index'; import { @@ -27,7 +26,6 @@ import { getThresholdSignalHistory, } from '../threshold'; import { - AlertAttributes, BulkCreate, RuleRangeTuple, SearchAfterAndBulkCreateReturnType, @@ -44,7 +42,7 @@ import { ExperimentalFeatures } from '../../../../../common/experimental_feature import { buildThresholdSignalHistory } from '../threshold/build_signal_history'; export const thresholdExecutor = async ({ - rule, + completeRule, tuple, exceptionItems, experimentalFeatures, @@ -57,7 +55,7 @@ export const thresholdExecutor = async ({ bulkCreate, wrapHits, }: { - rule: SavedObject>; + completeRule: CompleteRule; tuple: RuleRangeTuple; exceptionItems: ExceptionListItemSchema[]; experimentalFeatures: ExperimentalFeatures; @@ -71,7 +69,7 @@ export const thresholdExecutor = async ({ wrapHits: WrapHits; }): Promise => { let result = createSearchAfterReturnType(); - const ruleParams = rule.attributes.params; + const ruleParams = completeRule.ruleParams; // Get state or build initial state (on upgrade) const { signalHistory, searchErrors: previousSearchErrors } = state.initialized @@ -150,7 +148,7 @@ export const thresholdExecutor = async ({ const { success, bulkCreateDuration, createdItemsCount, createdItems, errors } = await bulkCreateThresholdSignals({ someResult: thresholdResults, - ruleSO: rule, + completeRule, filter: esFilter, services, logger, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/preview/alert_instance_factory_stub.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/preview/alert_instance_factory_stub.ts new file mode 100644 index 0000000000000..d09314312c78d --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/preview/alert_instance_factory_stub.ts @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { RuleParams } from '../../schemas/rule_schemas'; +import { + AlertInstanceContext, + AlertInstanceState, + AlertTypeState, +} from '../../../../../../alerting/common'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { AlertInstance } from '../../../../../../alerting/server/alert_instance'; + +export const alertInstanceFactoryStub = < + TParams extends RuleParams, + TState extends AlertTypeState, + TInstanceState extends AlertInstanceState, + TInstanceContext extends AlertInstanceContext, + TActionGroupIds extends string = '' +>( + id: string +) => ({ + getState() { + return {} as unknown as TInstanceState; + }, + replaceState(state: TInstanceState) { + return new AlertInstance({ + state: {} as TInstanceState, + meta: { lastScheduledActions: { group: 'default', date: new Date() } }, + }); + }, + scheduleActions(actionGroup: TActionGroupIds, alertcontext: TInstanceContext) { + return new AlertInstance({ + state: {} as TInstanceState, + meta: { lastScheduledActions: { group: 'default', date: new Date() } }, + }); + }, + scheduleActionsWithSubGroup( + actionGroup: TActionGroupIds, + subgroup: string, + alertcontext: TInstanceContext + ) { + return new AlertInstance({ + state: {} as TInstanceState, + meta: { lastScheduledActions: { group: 'default', date: new Date() } }, + }); + }, +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/preview/preview_rule_execution_log_client.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/preview/preview_rule_execution_log_client.ts new file mode 100644 index 0000000000000..d3ccafddab6e4 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/preview/preview_rule_execution_log_client.ts @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { SavedObjectsFindResult } from 'kibana/server'; +import { + LogExecutionMetricsArgs, + IRuleExecutionLogClient, + FindBulkExecutionLogArgs, + FindBulkExecutionLogResponse, + FindExecutionLogArgs, + LogStatusChangeArgs, + UpdateExecutionLogArgs, +} from '../../rule_execution_log'; +import { IRuleStatusSOAttributes } from '../../rules/types'; + +export const createWarningsAndErrors = () => { + const warningsAndErrorsStore: LogStatusChangeArgs[] = []; + + const previewRuleExecutionLogClient: IRuleExecutionLogClient = { + async delete(id: string): Promise { + return Promise.resolve(undefined); + }, + async find( + args: FindExecutionLogArgs + ): Promise>> { + return Promise.resolve([]); + }, + async findBulk(args: FindBulkExecutionLogArgs): Promise { + return Promise.resolve({}); + }, + async logStatusChange(args: LogStatusChangeArgs): Promise { + warningsAndErrorsStore.push(args); + return Promise.resolve(undefined); + }, + async update(args: UpdateExecutionLogArgs): Promise { + return Promise.resolve(undefined); + }, + async logExecutionMetrics(args: LogExecutionMetricsArgs): Promise { + return Promise.resolve(undefined); + }, + }; + + return { previewRuleExecutionLogClient, warningsAndErrorsStore }; +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.test.ts index 1f46654e855b2..92b66873396ee 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.test.ts @@ -8,7 +8,6 @@ import { sampleEmptyDocSearchResults, sampleRuleGuid, - sampleRuleSO, mockLogger, repeatedSearchResultsWithSortId, repeatedSearchResultsWithNoSortId, @@ -27,12 +26,13 @@ import { getSearchListItemResponseMock } from '../../../../../lists/common/schem import { getRuleRangeTuples } from './utils'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { elasticsearchClientMock } from 'src/core/server/elasticsearch/client/mocks'; -import { getQueryRuleParams } from '../schemas/rule_schemas.mock'; +import { getCompleteRuleMock, getQueryRuleParams } from '../schemas/rule_schemas.mock'; import { bulkCreateFactory } from './bulk_create_factory'; import { wrapHitsFactory } from './wrap_hits_factory'; import { mockBuildRuleMessage } from './__mocks__/build_rule_message.mock'; import { ResponseError } from '@elastic/elasticsearch/lib/errors'; import { BuildReasonMessage } from './reason_formatters'; +import { QueryRuleParams } from '../schemas/rule_schemas'; const buildRuleMessage = mockBuildRuleMessage; @@ -45,7 +45,7 @@ describe('searchAfterAndBulkCreate', () => { let listClient = listMock.getListClient(); const someGuids = Array.from({ length: 13 }).map(() => uuid.v4()); const sampleParams = getQueryRuleParams(); - const ruleSO = sampleRuleSO(getQueryRuleParams()); + const queryCompleteRule = getCompleteRuleMock(sampleParams); sampleParams.maxSignals = 30; let tuple: RuleRangeTuple; beforeEach(() => { @@ -58,6 +58,7 @@ describe('searchAfterAndBulkCreate', () => { tuple = getRuleRangeTuples({ logger: mockLogger, previousStartedAt: new Date(), + startedAt: new Date(), from: sampleParams.from, to: sampleParams.to, interval: '5m', @@ -71,7 +72,7 @@ describe('searchAfterAndBulkCreate', () => { false ); wrapHits = wrapHitsFactory({ - ruleSO, + completeRule: queryCompleteRule, signalsIndex: DEFAULT_SIGNALS_INDEX, mergeStrategy: 'missingFields', ignoreFields: [], @@ -184,7 +185,7 @@ describe('searchAfterAndBulkCreate', () => { const { success, createdSignalsCount, lastLookBackDate } = await searchAfterAndBulkCreate({ tuple, - ruleSO, + completeRule: queryCompleteRule, listClient, exceptionsList: [exceptionItem], services: mockService, @@ -288,7 +289,7 @@ describe('searchAfterAndBulkCreate', () => { }, ]; const { success, createdSignalsCount, lastLookBackDate } = await searchAfterAndBulkCreate({ - ruleSO, + completeRule: queryCompleteRule, tuple, listClient, exceptionsList: [exceptionItem], @@ -367,7 +368,7 @@ describe('searchAfterAndBulkCreate', () => { }, ]; const { success, createdSignalsCount, lastLookBackDate } = await searchAfterAndBulkCreate({ - ruleSO, + completeRule: queryCompleteRule, tuple, listClient, exceptionsList: [exceptionItem], @@ -427,7 +428,7 @@ describe('searchAfterAndBulkCreate', () => { }, ]; const { success, createdSignalsCount, lastLookBackDate } = await searchAfterAndBulkCreate({ - ruleSO, + completeRule: queryCompleteRule, tuple, listClient, exceptionsList: [exceptionItem], @@ -507,7 +508,7 @@ describe('searchAfterAndBulkCreate', () => { ); const { success, createdSignalsCount, lastLookBackDate } = await searchAfterAndBulkCreate({ - ruleSO, + completeRule: queryCompleteRule, tuple, listClient, exceptionsList: [], @@ -563,7 +564,7 @@ describe('searchAfterAndBulkCreate', () => { }, ]; const { success, createdSignalsCount, lastLookBackDate } = await searchAfterAndBulkCreate({ - ruleSO, + completeRule: queryCompleteRule, tuple, listClient, exceptionsList: [exceptionItem], @@ -636,7 +637,7 @@ describe('searchAfterAndBulkCreate', () => { }, ]; const { success, createdSignalsCount, lastLookBackDate } = await searchAfterAndBulkCreate({ - ruleSO, + completeRule: queryCompleteRule, tuple, listClient, exceptionsList: [exceptionItem], @@ -711,7 +712,7 @@ describe('searchAfterAndBulkCreate', () => { ) ); const { success, createdSignalsCount, lastLookBackDate } = await searchAfterAndBulkCreate({ - ruleSO, + completeRule: queryCompleteRule, tuple, listClient, exceptionsList: [], @@ -766,7 +767,7 @@ describe('searchAfterAndBulkCreate', () => { listClient, exceptionsList: [exceptionItem], tuple, - ruleSO, + completeRule: queryCompleteRule, services: mockService, logger: mockLogger, eventsTelemetry: undefined, @@ -814,7 +815,7 @@ describe('searchAfterAndBulkCreate', () => { listClient, exceptionsList: [exceptionItem], tuple, - ruleSO, + completeRule: queryCompleteRule, services: mockService, logger: mockLogger, eventsTelemetry: undefined, @@ -876,7 +877,7 @@ describe('searchAfterAndBulkCreate', () => { listClient, exceptionsList: [exceptionItem], tuple, - ruleSO, + completeRule: queryCompleteRule, services: mockService, logger: mockLogger, eventsTelemetry: undefined, @@ -996,7 +997,7 @@ describe('searchAfterAndBulkCreate', () => { ); const { success, createdSignalsCount, lastLookBackDate, errors } = await searchAfterAndBulkCreate({ - ruleSO, + completeRule: queryCompleteRule, tuple, listClient, exceptionsList: [], @@ -1093,7 +1094,7 @@ describe('searchAfterAndBulkCreate', () => { const mockEnrichment = jest.fn((a) => a); const { success, createdSignalsCount, lastLookBackDate } = await searchAfterAndBulkCreate({ enrichment: mockEnrichment, - ruleSO, + completeRule: queryCompleteRule, tuple, listClient, exceptionsList: [], diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.ts index 52b0799f5fe33..09b64fc2b654c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.ts @@ -23,25 +23,25 @@ import { SearchAfterAndBulkCreateParams, SearchAfterAndBulkCreateReturnType } fr // search_after through documents and re-index using bulk endpoint. export const searchAfterAndBulkCreate = async ({ - tuple, - ruleSO, + buildReasonMessage, + buildRuleMessage, + bulkCreate, + completeRule, + enrichment = identity, + eventsTelemetry, exceptionsList, - services, + filter, + inputIndexPattern, listClient, logger, - eventsTelemetry, - inputIndexPattern, - filter, pageSize, - buildRuleMessage, - buildReasonMessage, - enrichment = identity, - bulkCreate, - wrapHits, + services, sortOrder, trackTotalHits, + tuple, + wrapHits, }: SearchAfterAndBulkCreateParams): Promise => { - const ruleParams = ruleSO.attributes.params; + const ruleParams = completeRule.ruleParams; let toReturn = createSearchAfterReturnType(); // sortId tells us where to start our next consecutive search_after query diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts index f9a2f5cfc0bfe..c55b3e2a297a3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts @@ -92,9 +92,9 @@ const getPayload = ( ruleTypeName: 'Name of rule', enabled: true, schedule: { - interval: '1h', + interval: '5m', }, - actions: [], + actions: ruleAlert.actions, createdBy: 'elastic', updatedBy: 'elastic', createdAt: new Date('2019-12-13T16:50:33.400Z'), @@ -216,7 +216,7 @@ describe('signal_rule_alert_type', () => { }); it('should warn about the gap between runs if gap is very large', async () => { - payload.previousStartedAt = moment().subtract(100, 'm').toDate(); + payload.previousStartedAt = moment(payload.startedAt).subtract(100, 'm').toDate(); await alert.executor(payload); expect(logger.warn).toHaveBeenCalled(); expect(mockRuleExecutionLogClient.logStatusChange).toHaveBeenNthCalledWith( @@ -357,20 +357,16 @@ describe('signal_rule_alert_type', () => { }, ]; - alertServices.savedObjectsClient.get.mockResolvedValue({ - id: 'rule-id', - type: 'type', - references: [], - attributes: ruleAlert, - }); - payload.params.meta = {}; + const modifiedPayload = getPayload( + ruleAlert, + alertServices + ) as jest.Mocked; - await alert.executor(payload); + await alert.executor(modifiedPayload); expect(scheduleNotificationActions).toHaveBeenCalledWith( expect.objectContaining({ - resultsLink: - '/app/security/detections/rules/id/rule-id?timerange=(global:(linkTo:!(timeline),timerange:(from:100,kind:absolute,to:100)),timeline:(linkTo:!(global),timerange:(from:100,kind:absolute,to:100)))', + resultsLink: `/app/security/detections/rules/id/${ruleAlert.id}?timerange=(global:(linkTo:!(timeline),timerange:(from:100,kind:absolute,to:100)),timeline:(linkTo:!(global),timerange:(from:100,kind:absolute,to:100)))`, }) ); }); @@ -390,20 +386,16 @@ describe('signal_rule_alert_type', () => { }, ]; - alertServices.savedObjectsClient.get.mockResolvedValue({ - id: 'rule-id', - type: 'type', - references: [], - attributes: ruleAlert, - }); - delete payload.params.meta; + const modifiedPayload = getPayload( + ruleAlert, + alertServices + ) as jest.Mocked; - await alert.executor(payload); + await alert.executor(modifiedPayload); expect(scheduleNotificationActions).toHaveBeenCalledWith( expect.objectContaining({ - resultsLink: - '/app/security/detections/rules/id/rule-id?timerange=(global:(linkTo:!(timeline),timerange:(from:100,kind:absolute,to:100)),timeline:(linkTo:!(global),timerange:(from:100,kind:absolute,to:100)))', + resultsLink: `/app/security/detections/rules/id/${ruleAlert.id}?timerange=(global:(linkTo:!(timeline),timerange:(from:100,kind:absolute,to:100)),timeline:(linkTo:!(global),timerange:(from:100,kind:absolute,to:100)))`, }) ); }); @@ -423,20 +415,16 @@ describe('signal_rule_alert_type', () => { }, ]; - alertServices.savedObjectsClient.get.mockResolvedValue({ - id: 'rule-id', - type: 'type', - references: [], - attributes: ruleAlert, - }); - payload.params.meta = { kibana_siem_app_url: 'http://localhost' }; + const modifiedPayload = getPayload( + ruleAlert, + alertServices + ) as jest.Mocked; - await alert.executor(payload); + await alert.executor(modifiedPayload); expect(scheduleNotificationActions).toHaveBeenCalledWith( expect.objectContaining({ - resultsLink: - 'http://localhost/detections/rules/id/rule-id?timerange=(global:(linkTo:!(timeline),timerange:(from:100,kind:absolute,to:100)),timeline:(linkTo:!(global),timerange:(from:100,kind:absolute,to:100)))', + resultsLink: `http://localhost/detections/rules/id/${ruleAlert.id}?timerange=(global:(linkTo:!(timeline),timerange:(from:100,kind:absolute,to:100)),timeline:(linkTo:!(global),timerange:(from:100,kind:absolute,to:100)))`, }) ); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts index 0b1524a5682ab..18cdef3048011 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts @@ -6,7 +6,7 @@ */ /* eslint-disable complexity */ -import { Logger, SavedObject } from 'src/core/server'; +import { Logger } from 'src/core/server'; import isEmpty from 'lodash/isEmpty'; import * as t from 'io-ts'; @@ -26,7 +26,7 @@ import { } from '../../../../common/detection_engine/utils'; import { SetupPlugins } from '../../../plugin'; import { getInputIndex } from './get_input_output_index'; -import { AlertAttributes, SignalRuleAlertTypeDefinition, ThresholdAlertState } from './types'; +import { SignalRuleAlertTypeDefinition, ThresholdAlertState } from './types'; import { getListsClient, getExceptions, @@ -59,6 +59,7 @@ import { ruleParams, RuleParams, savedQueryRuleParams, + CompleteRule, } from '../schemas/rule_schemas'; import { bulkCreateFactory } from './bulk_create_factory'; import { wrapHitsFactory } from './wrap_hits_factory'; @@ -66,7 +67,11 @@ import { wrapSequencesFactory } from './wrap_sequences_factory'; import { ConfigType } from '../../../config'; import { ExperimentalFeatures } from '../../../../common/experimental_features'; import { injectReferences, extractReferences } from './saved_object_references'; -import { RuleExecutionLogClient, truncateMessageList } from '../rule_execution_log'; +import { + IRuleExecutionLogClient, + RuleExecutionLogClient, + truncateMessageList, +} from '../rule_execution_log'; import { RuleExecutionStatus } from '../../../../common/detection_engine/schemas/common/schemas'; import { scheduleThrottledNotificationActions } from '../notifications/schedule_throttle_notification_actions'; import { IEventLogService } from '../../../../../event_log/server'; @@ -80,15 +85,19 @@ export const signalRulesAlertType = ({ lists, config, eventLogService, + indexNameOverride, + ruleExecutionLogClientOverride, }: { logger: Logger; eventsTelemetry: TelemetryEventsSender | undefined; experimentalFeatures: ExperimentalFeatures; version: string; - ml: SetupPlugins['ml']; + ml: SetupPlugins['ml'] | undefined; lists: SetupPlugins['lists'] | undefined; config: ConfigType; eventLogService: IEventLogService; + indexNameOverride?: string; + ruleExecutionLogClientOverride?: IRuleExecutionLogClient; }): SignalRuleAlertTypeDefinition => { const { alertMergeStrategy: mergeStrategy, alertIgnoreFields: ignoreFields } = config; return { @@ -127,31 +136,40 @@ export const signalRulesAlertType = ({ params, spaceId, updatedBy: updatedByUser, + rule, }) { const { ruleId, maxSignals, meta, outputIndex, timestampOverride, type } = params; const searchAfterSize = Math.min(maxSignals, DEFAULT_SEARCH_AFTER_PAGE_SIZE); let hasError: boolean = false; let result = createSearchAfterReturnType(); - const ruleStatusClient = new RuleExecutionLogClient({ - eventLogService, - savedObjectsClient: services.savedObjectsClient, - underlyingClient: config.ruleExecutionLog.underlyingClient, - }); + const ruleStatusClient = ruleExecutionLogClientOverride + ? ruleExecutionLogClientOverride + : new RuleExecutionLogClient({ + eventLogService, + savedObjectsClient: services.savedObjectsClient, + underlyingClient: config.ruleExecutionLog.underlyingClient, + }); + + const completeRule: CompleteRule = { + alertId, + ruleConfig: rule, + ruleParams: params, + }; - const savedObject = await services.savedObjectsClient.get('alert', alertId); const { actions, name, - alertTypeId, schedule: { interval }, - } = savedObject.attributes; + ruleTypeId, + } = completeRule.ruleConfig; + const refresh = actions.length ? 'wait_for' : false; const buildRuleMessage = buildRuleMessageFactory({ id: alertId, ruleId, name, - index: outputIndex, + index: indexNameOverride ?? outputIndex, }); logger.debug(buildRuleMessage('[+] Starting Signal Rule execution')); @@ -161,7 +179,7 @@ export const signalRulesAlertType = ({ spaceId, ruleId: alertId, ruleName: name, - ruleType: alertTypeId, + ruleType: ruleTypeId, }; await ruleStatusClient.logStatusChange({ @@ -172,7 +190,7 @@ export const signalRulesAlertType = ({ const notificationRuleParams: NotificationRuleTypeParams = { ...params, name, - id: savedObject.id, + id: alertId, }; // check if rule has permissions to access given index pattern @@ -235,7 +253,9 @@ export const signalRulesAlertType = ({ interval, maxSignals, buildRuleMessage, + startedAt, }); + if (remainingGap.asMilliseconds() > 0) { const gapString = remainingGap.humanize(); const gapMessage = buildRuleMessage( @@ -268,28 +288,32 @@ export const signalRulesAlertType = ({ logger, services.scopedClusterClient.asCurrentUser, buildRuleMessage, - refresh + refresh, + indexNameOverride ); const wrapHits = wrapHitsFactory({ - ruleSO: savedObject, - signalsIndex: params.outputIndex, + completeRule, + signalsIndex: indexNameOverride ?? params.outputIndex, mergeStrategy, ignoreFields, }); const wrapSequences = wrapSequencesFactory({ - ruleSO: savedObject, + completeRule, signalsIndex: params.outputIndex, mergeStrategy, ignoreFields, }); if (isMlRule(type)) { - const mlRuleSO = asTypeSpecificSO(savedObject, machineLearningRuleParams); + const mlRuleCompleteRule = asTypeSpecificCompleteRule( + completeRule, + machineLearningRuleParams + ); for (const tuple of tuples) { result = await mlExecutor({ - rule: mlRuleSO, + completeRule: mlRuleCompleteRule, tuple, ml, listClient, @@ -302,10 +326,13 @@ export const signalRulesAlertType = ({ }); } } else if (isThresholdRule(type)) { - const thresholdRuleSO = asTypeSpecificSO(savedObject, thresholdRuleParams); + const thresholdCompleteRule = asTypeSpecificCompleteRule( + completeRule, + thresholdRuleParams + ); for (const tuple of tuples) { result = await thresholdExecutor({ - rule: thresholdRuleSO, + completeRule: thresholdCompleteRule, tuple, exceptionItems, experimentalFeatures, @@ -320,10 +347,10 @@ export const signalRulesAlertType = ({ }); } } else if (isThreatMatchRule(type)) { - const threatRuleSO = asTypeSpecificSO(savedObject, threatRuleParams); + const threatCompleteRule = asTypeSpecificCompleteRule(completeRule, threatRuleParams); for (const tuple of tuples) { result = await threatMatchExecutor({ - rule: threatRuleSO, + completeRule: threatCompleteRule, tuple, listClient, exceptionItems, @@ -339,10 +366,10 @@ export const signalRulesAlertType = ({ }); } } else if (isQueryRule(type)) { - const queryRuleSO = validateQueryRuleTypes(savedObject); + const queryCompleteRule = validateQueryRuleTypes(completeRule); for (const tuple of tuples) { result = await queryExecutor({ - rule: queryRuleSO, + completeRule: queryCompleteRule, tuple, listClient, exceptionItems, @@ -358,10 +385,10 @@ export const signalRulesAlertType = ({ }); } } else if (isEqlRule(type)) { - const eqlRuleSO = asTypeSpecificSO(savedObject, eqlRuleParams); + const eqlCompleteRule = asTypeSpecificCompleteRule(completeRule, eqlRuleParams); for (const tuple of tuples) { result = await eqlExecutor({ - rule: eqlRuleSO, + completeRule: eqlCompleteRule, tuple, exceptionItems, experimentalFeatures, @@ -395,7 +422,7 @@ export const signalRulesAlertType = ({ const resultsLink = getNotificationResultsLink({ from: fromInMs, to: toInMs, - id: savedObject.id, + id: alertId, kibanaSiemAppUrl: (meta as { kibana_siem_app_url?: string } | undefined) ?.kibana_siem_app_url, }); @@ -404,15 +431,15 @@ export const signalRulesAlertType = ({ buildRuleMessage(`Found ${result.createdSignalsCount} signals for notification.`) ); - if (savedObject.attributes.throttle != null) { + if (completeRule.ruleConfig.throttle != null) { await scheduleThrottledNotificationActions({ alertInstance: services.alertInstanceFactory(alertId), - throttle: savedObject.attributes.throttle, + throttle: completeRule.ruleConfig.throttle, startedAt, - id: savedObject.id, + id: alertId, kibanaSiemAppUrl: (meta as { kibana_siem_app_url?: string } | undefined) ?.kibana_siem_app_url, - outputIndex, + outputIndex: indexNameOverride ?? outputIndex, ruleId, signals: result.createdSignals, esClient: services.scopedClusterClient.asCurrentUser, @@ -434,7 +461,9 @@ export const signalRulesAlertType = ({ logger.debug(buildRuleMessage('[+] Signal Rule execution completed.')); logger.debug( buildRuleMessage( - `[+] Finished indexing ${result.createdSignalsCount} signals into ${outputIndex}` + `[+] Finished indexing ${result.createdSignalsCount} signals into ${ + indexNameOverride ?? outputIndex + }` ) ); if (!hasError && !wroteWarningStatus && !result.warning) { @@ -462,12 +491,12 @@ export const signalRulesAlertType = ({ ); } else { // NOTE: Since this is throttled we have to call it even on an error condition, otherwise it will "reset" the throttle and fire early - if (savedObject.attributes.throttle != null) { + if (completeRule.ruleConfig.throttle != null) { await scheduleThrottledNotificationActions({ alertInstance: services.alertInstanceFactory(alertId), - throttle: savedObject.attributes.throttle, + throttle: completeRule.ruleConfig.throttle ?? '', startedAt, - id: savedObject.id, + id: completeRule.alertId, kibanaSiemAppUrl: (meta as { kibana_siem_app_url?: string } | undefined) ?.kibana_siem_app_url, outputIndex, @@ -496,12 +525,12 @@ export const signalRulesAlertType = ({ } } catch (error) { // NOTE: Since this is throttled we have to call it even on an error condition, otherwise it will "reset" the throttle and fire early - if (savedObject.attributes.throttle != null) { + if (completeRule.ruleConfig.throttle != null) { await scheduleThrottledNotificationActions({ alertInstance: services.alertInstanceFactory(alertId), - throttle: savedObject.attributes.throttle, + throttle: completeRule.ruleConfig.throttle ?? '', startedAt, - id: savedObject.id, + id: completeRule.alertId, kibanaSiemAppUrl: (meta as { kibana_siem_app_url?: string } | undefined) ?.kibana_siem_app_url, outputIndex, @@ -534,11 +563,11 @@ export const signalRulesAlertType = ({ }; }; -const validateQueryRuleTypes = (ruleSO: SavedObject) => { - if (ruleSO.attributes.params.type === 'query') { - return asTypeSpecificSO(ruleSO, queryRuleParams); +const validateQueryRuleTypes = (completeRule: CompleteRule) => { + if (completeRule.ruleParams.type === 'query') { + return asTypeSpecificCompleteRule(completeRule, queryRuleParams); } else { - return asTypeSpecificSO(ruleSO, savedQueryRuleParams); + return asTypeSpecificCompleteRule(completeRule, savedQueryRuleParams); } }; @@ -549,22 +578,19 @@ const validateQueryRuleTypes = (ruleSO: SavedObject) => { * checks if the required type specific fields actually exist on the SO and prevents rule executors from * accessing fields that only exist on other rule types. * - * @param ruleSO SavedObject typed as an object with all fields from all different rule types + * @param completeRule rule typed as an object with all fields from all different rule types * @param schema io-ts schema for the specific rule type the SavedObject claims to be */ -export const asTypeSpecificSO = ( - ruleSO: SavedObject, +export const asTypeSpecificCompleteRule = ( + completeRule: CompleteRule, schema: T ) => { - const [validated, errors] = validateNonExact(ruleSO.attributes.params, schema); + const [validated, errors] = validateNonExact(completeRule.ruleParams, schema); if (validated == null || errors != null) { throw new Error(`Rule attempted to execute with invalid params: ${errors}`); } return { - ...ruleSO, - attributes: { - ...ruleSO.attributes, - params: validated, - }, + ...completeRule, + ruleParams: validated, }; }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signal.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signal.ts index 33ba4723d82b2..bf72a13ba0450 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signal.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signal.ts @@ -14,28 +14,28 @@ import { CreateThreatSignalOptions } from './types'; import { SearchAfterAndBulkCreateReturnType } from '../types'; export const createThreatSignal = async ({ - tuple, - threatMapping, - threatEnrichment, - query, - inputIndex, - type, + alertId, + buildRuleMessage, + bulkCreate, + completeRule, + currentResult, + currentThreatList, + eventsTelemetry, + exceptionItems, filters, + inputIndex, language, - savedId, - services, - exceptionItems, listClient, logger, - eventsTelemetry, - alertId, outputIndex, - ruleSO, + query, + savedId, searchAfterSize, - buildRuleMessage, - currentThreatList, - currentResult, - bulkCreate, + services, + threatEnrichment, + threatMapping, + tuple, + type, wrapHits, }: CreateThreatSignalOptions): Promise => { const threatFilter = buildThreatMappingFilter({ @@ -71,25 +71,25 @@ export const createThreatSignal = async ({ ); const result = await searchAfterAndBulkCreate({ - tuple, - listClient, - exceptionsList: exceptionItems, - ruleSO, - services, - logger, + buildReasonMessage: buildReasonMessageForThreatMatchAlert, + buildRuleMessage, + bulkCreate, + completeRule, + enrichment: threatEnrichment, eventsTelemetry, + exceptionsList: exceptionItems, + filter: esFilter, id: alertId, inputIndexPattern: inputIndex, - signalsIndex: outputIndex, - filter: esFilter, + listClient, + logger, pageSize: searchAfterSize, - buildRuleMessage, - buildReasonMessage: buildReasonMessageForThreatMatchAlert, - enrichment: threatEnrichment, - bulkCreate, - wrapHits, + services, + signalsIndex: outputIndex, sortOrder: 'desc', trackTotalHits: false, + tuple, + wrapHits, }); logger.debug( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signals.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signals.ts index 677a2028acdf7..777445ca67ca8 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signals.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signals.ts @@ -15,39 +15,39 @@ import { buildExecutionIntervalValidator, combineConcurrentResults } from './uti import { buildThreatEnrichment } from './build_threat_enrichment'; export const createThreatSignals = async ({ - tuple, - threatMapping, - query, - inputIndex, - type, + alertId, + buildRuleMessage, + bulkCreate, + completeRule, + concurrentSearches, + eventsTelemetry, + exceptionItems, filters, + inputIndex, + itemsPerSearch, language, - savedId, - services, - exceptionItems, listClient, logger, - eventsTelemetry, - alertId, outputIndex, - ruleSO, + query, + savedId, searchAfterSize, + services, threatFilters, - threatQuery, - threatLanguage, - buildRuleMessage, threatIndex, threatIndicatorPath, - concurrentSearches, - itemsPerSearch, - bulkCreate, + threatLanguage, + threatMapping, + threatQuery, + tuple, + type, wrapHits, }: CreateThreatSignalsOptions): Promise => { - const params = ruleSO.attributes.params; + const params = completeRule.ruleParams; logger.debug(buildRuleMessage('Indicator matching rule starting')); const perPage = concurrentSearches * itemsPerSearch; const verifyExecutionCanProceed = buildExecutionIntervalValidator( - ruleSO.attributes.schedule.interval + completeRule.ruleConfig.schedule.interval ); let results: SearchAfterAndBulkCreateReturnType = { @@ -108,28 +108,28 @@ export const createThreatSignals = async ({ const concurrentSearchesPerformed = chunks.map>( (slicedChunk) => createThreatSignal({ - tuple, - threatEnrichment, - threatMapping, - query, - inputIndex, - type, + alertId, + buildRuleMessage, + bulkCreate, + completeRule, + currentResult: results, + currentThreatList: slicedChunk, + eventsTelemetry, + exceptionItems, filters, + inputIndex, language, - savedId, - services, - exceptionItems, listClient, logger, - eventsTelemetry, - alertId, outputIndex, - ruleSO, + query, + savedId, searchAfterSize, - buildRuleMessage, - currentThreatList: slicedChunk, - currentResult: results, - bulkCreate, + services, + threatEnrichment, + threatMapping, + tuple, + type, wrapHits, }) ); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/types.ts index 38d9d42a4602c..07baa353dddb7 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/types.ts @@ -24,107 +24,106 @@ import { AlertInstanceState, AlertServices, } from '../../../../../../alerting/server'; -import { ElasticsearchClient, Logger, SavedObject } from '../../../../../../../../src/core/server'; +import { ElasticsearchClient, Logger } from '../../../../../../../../src/core/server'; import { TelemetryEventsSender } from '../../../telemetry/sender'; import { BuildRuleMessage } from '../rule_messages'; import { - AlertAttributes, BulkCreate, RuleRangeTuple, SearchAfterAndBulkCreateReturnType, SignalsEnrichment, WrapHits, } from '../types'; -import { ThreatRuleParams } from '../../schemas/rule_schemas'; +import { CompleteRule, ThreatRuleParams } from '../../schemas/rule_schemas'; export type SortOrderOrUndefined = 'asc' | 'desc' | undefined; export interface CreateThreatSignalsOptions { - tuple: RuleRangeTuple; - threatMapping: ThreatMapping; - query: string; - inputIndex: string[]; - type: Type; + alertId: string; + buildRuleMessage: BuildRuleMessage; + bulkCreate: BulkCreate; + completeRule: CompleteRule; + concurrentSearches: ConcurrentSearches; + eventsTelemetry: TelemetryEventsSender | undefined; + exceptionItems: ExceptionListItemSchema[]; filters: unknown[]; + inputIndex: string[]; + itemsPerSearch: ItemsPerSearch; language: LanguageOrUndefined; - savedId: string | undefined; - services: AlertServices; - exceptionItems: ExceptionListItemSchema[]; listClient: ListClient; logger: Logger; - eventsTelemetry: TelemetryEventsSender | undefined; - alertId: string; outputIndex: string; - ruleSO: SavedObject>; + query: string; + savedId: string | undefined; searchAfterSize: number; + services: AlertServices; threatFilters: unknown[]; - threatQuery: ThreatQuery; - buildRuleMessage: BuildRuleMessage; threatIndex: ThreatIndex; threatIndicatorPath: ThreatIndicatorPathOrUndefined; threatLanguage: ThreatLanguageOrUndefined; - concurrentSearches: ConcurrentSearches; - itemsPerSearch: ItemsPerSearch; - bulkCreate: BulkCreate; + threatMapping: ThreatMapping; + threatQuery: ThreatQuery; + tuple: RuleRangeTuple; + type: Type; wrapHits: WrapHits; } export interface CreateThreatSignalOptions { - tuple: RuleRangeTuple; - threatMapping: ThreatMapping; - threatEnrichment: SignalsEnrichment; - query: string; - inputIndex: string[]; - type: Type; + alertId: string; + buildRuleMessage: BuildRuleMessage; + bulkCreate: BulkCreate; + completeRule: CompleteRule; + currentResult: SearchAfterAndBulkCreateReturnType; + currentThreatList: ThreatListItem[]; + eventsTelemetry: TelemetryEventsSender | undefined; + exceptionItems: ExceptionListItemSchema[]; filters: unknown[]; + inputIndex: string[]; language: LanguageOrUndefined; - savedId: string | undefined; - services: AlertServices; - exceptionItems: ExceptionListItemSchema[]; listClient: ListClient; logger: Logger; - eventsTelemetry: TelemetryEventsSender | undefined; - alertId: string; outputIndex: string; - ruleSO: SavedObject>; + query: string; + savedId: string | undefined; searchAfterSize: number; - buildRuleMessage: BuildRuleMessage; - currentThreatList: ThreatListItem[]; - currentResult: SearchAfterAndBulkCreateReturnType; - bulkCreate: BulkCreate; + services: AlertServices; + threatEnrichment: SignalsEnrichment; + threatMapping: ThreatMapping; + tuple: RuleRangeTuple; + type: Type; wrapHits: WrapHits; } export interface BuildThreatMappingFilterOptions { - threatMapping: ThreatMapping; - threatList: ThreatListItem[]; chunkSize?: number; + threatList: ThreatListItem[]; + threatMapping: ThreatMapping; } export interface FilterThreatMappingOptions { - threatMapping: ThreatMapping; threatListItem: ThreatListItem; + threatMapping: ThreatMapping; } export interface CreateInnerAndClausesOptions { - threatMappingEntries: ThreatMappingEntries; threatListItem: ThreatListItem; + threatMappingEntries: ThreatMappingEntries; } export interface CreateAndOrClausesOptions { - threatMapping: ThreatMapping; threatListItem: ThreatListItem; + threatMapping: ThreatMapping; } export interface BuildEntriesMappingFilterOptions { - threatMapping: ThreatMapping; - threatList: ThreatListItem[]; chunkSize: number; + threatList: ThreatListItem[]; + threatMapping: ThreatMapping; } export interface SplitShouldClausesOptions { - should: BooleanFilter[]; chunkSize: number; + should: BooleanFilter[]; } export interface BooleanFilter { @@ -132,35 +131,35 @@ export interface BooleanFilter { } export interface GetThreatListOptions { + buildRuleMessage: BuildRuleMessage; esClient: ElasticsearchClient; - query: string; - language: ThreatLanguageOrUndefined; + exceptionItems: ExceptionListItemSchema[]; index: string[]; + language: ThreatLanguageOrUndefined; + listClient: ListClient; + logger: Logger; perPage?: number; + query: string; searchAfter: string[] | undefined; sortField: string | undefined; sortOrder: SortOrderOrUndefined; threatFilters: unknown[]; - exceptionItems: ExceptionListItemSchema[]; - listClient: ListClient; - buildRuleMessage: BuildRuleMessage; - logger: Logger; } export interface ThreatListCountOptions { esClient: ElasticsearchClient; - query: string; + exceptionItems: ExceptionListItemSchema[]; + index: string[]; language: ThreatLanguageOrUndefined; + query: string; threatFilters: unknown[]; - index: string[]; - exceptionItems: ExceptionListItemSchema[]; } export interface GetSortWithTieBreakerOptions { - sortField: string | undefined; - sortOrder: SortOrderOrUndefined; index: string[]; listItemIndex: string; + sortField: string | undefined; + sortOrder: SortOrderOrUndefined; } export interface ThreatListDoc { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.ts index e2ef2cb6c841d..4dbe1577365d6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/bulk_create_threshold_signals.ts @@ -13,7 +13,7 @@ import { ThresholdNormalized, TimestampOverrideOrUndefined, } from '../../../../../common/detection_engine/schemas/common/schemas'; -import { Logger, SavedObject } from '../../../../../../../../src/core/server'; +import { Logger } from '../../../../../../../../src/core/server'; import { AlertInstanceContext, AlertInstanceState, @@ -33,15 +33,14 @@ import type { SignalSource, SignalSearchResponse, ThresholdSignalHistory, - AlertAttributes, BulkCreate, WrapHits, } from '../types'; -import { ThresholdRuleParams } from '../../schemas/rule_schemas'; +import { CompleteRule, ThresholdRuleParams } from '../../schemas/rule_schemas'; interface BulkCreateThresholdSignalsParams { someResult: SignalSearchResponse; - ruleSO: SavedObject>; + completeRule: CompleteRule; services: AlertServices; inputIndexPattern: string[]; logger: Logger; @@ -228,7 +227,7 @@ export const transformThresholdResultsToEcs = ( export const bulkCreateThresholdSignals = async ( params: BulkCreateThresholdSignalsParams ): Promise> => { - const ruleParams = params.ruleSO.attributes.params; + const ruleParams = params.completeRule.ruleParams; const thresholdResults = params.someResult; const ecsResults = transformThresholdResultsToEcs( thresholdResults, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts index 82b4a46f482b6..c831fb7f00cff 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts @@ -28,10 +28,10 @@ import { EqlSequence, } from '../../../../common/detection_engine/types'; import { ListClient } from '../../../../../lists/server'; -import { Logger, SavedObject } from '../../../../../../../src/core/server'; +import { Logger } from '../../../../../../../src/core/server'; import { BuildRuleMessage } from './rule_messages'; import { TelemetryEventsSender } from '../../telemetry/sender'; -import { RuleParams } from '../schemas/rule_schemas'; +import { CompleteRule, RuleParams } from '../schemas/rule_schemas'; import { GenericBulkCreateResponse } from './bulk_create_factory'; import { EcsFieldMap } from '../../../../../rule_registry/common/assets/field_maps/ecs_field_map'; import { TypeOfFieldMap } from '../../../../../rule_registry/common/field_map'; @@ -300,7 +300,7 @@ export interface SearchAfterAndBulkCreateParams { from: moment.Moment; maxSignals: number; }; - ruleSO: SavedObject; + completeRule: CompleteRule; services: AlertServices; listClient: ListClient; exceptionsList: ExceptionListItemSchema[]; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts index ce2b15a46ef6f..840b897997ddc 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts @@ -22,7 +22,6 @@ moment.suppressDeprecationWarnings = true; import { generateId, parseInterval, - getDriftTolerance, getGapBetweenRuns, getNumCatchupIntervals, errorAggregator, @@ -112,105 +111,13 @@ describe('utils', () => { }); }); - describe('getDriftTolerance', () => { - test('it returns a drift tolerance in milliseconds of 1 minute when "from" overlaps "to" by 1 minute and the interval is 5 minutes', () => { - const drift = getDriftTolerance({ - from: 'now-6m', - to: 'now', - intervalDuration: moment.duration(5, 'minutes'), - }); - expect(drift).not.toBeNull(); - expect(drift?.asMilliseconds()).toEqual(moment.duration(1, 'minute').asMilliseconds()); - }); - - test('it returns a drift tolerance of 0 when "from" equals the interval', () => { - const drift = getDriftTolerance({ - from: 'now-5m', - to: 'now', - intervalDuration: moment.duration(5, 'minutes'), - }); - expect(drift?.asMilliseconds()).toEqual(0); - }); - - test('it returns a drift tolerance of 5 minutes when "from" is 10 minutes but the interval is 5 minutes', () => { - const drift = getDriftTolerance({ - from: 'now-10m', - to: 'now', - intervalDuration: moment.duration(5, 'minutes'), - }); - expect(drift).not.toBeNull(); - expect(drift?.asMilliseconds()).toEqual(moment.duration(5, 'minutes').asMilliseconds()); - }); - - test('it returns a drift tolerance of 10 minutes when "from" is 10 minutes ago and the interval is 0', () => { - const drift = getDriftTolerance({ - from: 'now-10m', - to: 'now', - intervalDuration: moment.duration(0, 'milliseconds'), - }); - expect(drift).not.toBeNull(); - expect(drift?.asMilliseconds()).toEqual(moment.duration(10, 'minutes').asMilliseconds()); - }); - - test('returns a drift tolerance of 1 minute when "from" is invalid and defaults to "now-6m" and interval is 5 minutes', () => { - const drift = getDriftTolerance({ - from: 'invalid', - to: 'now', - intervalDuration: moment.duration(5, 'minutes'), - }); - expect(drift).not.toBeNull(); - expect(drift?.asMilliseconds()).toEqual(moment.duration(1, 'minute').asMilliseconds()); - }); - - test('returns a drift tolerance of 1 minute when "from" does not include `now` and defaults to "now-6m" and interval is 5 minutes', () => { - const drift = getDriftTolerance({ - from: '10m', - to: 'now', - intervalDuration: moment.duration(5, 'minutes'), - }); - expect(drift).not.toBeNull(); - expect(drift?.asMilliseconds()).toEqual(moment.duration(1, 'minute').asMilliseconds()); - }); - - test('returns a drift tolerance of 4 minutes when "to" is "now-x", from is a valid input and interval is 5 minute', () => { - const drift = getDriftTolerance({ - from: 'now-10m', - to: 'now-1m', - intervalDuration: moment.duration(5, 'minutes'), - }); - expect(drift).not.toBeNull(); - expect(drift?.asMilliseconds()).toEqual(moment.duration(4, 'minutes').asMilliseconds()); - }); - - test('it returns expected drift tolerance when "from" is an ISO string', () => { - const drift = getDriftTolerance({ - from: moment().subtract(10, 'minutes').toISOString(), - to: 'now', - intervalDuration: moment.duration(5, 'minutes'), - }); - expect(drift).not.toBeNull(); - expect(drift?.asMilliseconds()).toEqual(moment.duration(5, 'minutes').asMilliseconds()); - }); - - test('it returns expected drift tolerance when "to" is an ISO string', () => { - const drift = getDriftTolerance({ - from: 'now-6m', - to: moment().toISOString(), - intervalDuration: moment.duration(5, 'minutes'), - }); - expect(drift).not.toBeNull(); - expect(drift?.asMilliseconds()).toEqual(moment.duration(1, 'minute').asMilliseconds()); - }); - }); - describe('getGapBetweenRuns', () => { test('it returns a gap of 0 when "from" and interval match each other and the previous started was from the previous interval time', () => { const gap = getGapBetweenRuns({ previousStartedAt: nowDate.clone().subtract(5, 'minutes').toDate(), - intervalDuration: moment.duration(5, 'minutes'), - from: 'now-5m', - to: 'now', - now: nowDate.clone(), + startedAt: nowDate.clone().toDate(), + originalFrom: nowDate.clone().subtract(5, 'minutes'), + originalTo: nowDate.clone(), }); expect(gap).not.toBeNull(); expect(gap?.asMilliseconds()).toEqual(0); @@ -219,10 +126,9 @@ describe('utils', () => { test('it returns a negative gap of 1 minute when "from" overlaps to by 1 minute and the previousStartedAt was 5 minutes ago', () => { const gap = getGapBetweenRuns({ previousStartedAt: nowDate.clone().subtract(5, 'minutes').toDate(), - intervalDuration: moment.duration(5, 'minutes'), - from: 'now-6m', - to: 'now', - now: nowDate.clone(), + startedAt: nowDate.clone().toDate(), + originalFrom: nowDate.clone().subtract(6, 'minutes'), + originalTo: nowDate.clone(), }); expect(gap).not.toBeNull(); expect(gap?.asMilliseconds()).toEqual(moment.duration(-1, 'minute').asMilliseconds()); @@ -231,10 +137,9 @@ describe('utils', () => { test('it returns a negative gap of 5 minutes when "from" overlaps to by 1 minute and the previousStartedAt was 5 minutes ago', () => { const gap = getGapBetweenRuns({ previousStartedAt: nowDate.clone().subtract(5, 'minutes').toDate(), - intervalDuration: moment.duration(5, 'minutes'), - from: 'now-10m', - to: 'now', - now: nowDate.clone(), + startedAt: nowDate.clone().toDate(), + originalFrom: nowDate.clone().subtract(10, 'minutes'), + originalTo: nowDate.clone(), }); expect(gap).not.toBeNull(); expect(gap?.asMilliseconds()).toEqual(moment.duration(-5, 'minute').asMilliseconds()); @@ -243,10 +148,9 @@ describe('utils', () => { test('it returns a negative gap of 1 minute when "from" overlaps to by 1 minute and the previousStartedAt was 10 minutes ago and so was the interval', () => { const gap = getGapBetweenRuns({ previousStartedAt: nowDate.clone().subtract(10, 'minutes').toDate(), - intervalDuration: moment.duration(10, 'minutes'), - from: 'now-11m', - to: 'now', - now: nowDate.clone(), + startedAt: nowDate.clone().toDate(), + originalFrom: nowDate.clone().subtract(11, 'minutes'), + originalTo: nowDate.clone(), }); expect(gap).not.toBeNull(); expect(gap?.asMilliseconds()).toEqual(moment.duration(-1, 'minute').asMilliseconds()); @@ -255,10 +159,9 @@ describe('utils', () => { test('it returns a gap of only -30 seconds when the from overlaps with now by 1 minute, the interval is 5 minutes but the previous started is 30 seconds more', () => { const gap = getGapBetweenRuns({ previousStartedAt: nowDate.clone().subtract(5, 'minutes').subtract(30, 'seconds').toDate(), - intervalDuration: moment.duration(5, 'minutes'), - from: 'now-6m', - to: 'now', - now: nowDate.clone(), + startedAt: nowDate.clone().toDate(), + originalFrom: nowDate.clone().subtract(6, 'minutes'), + originalTo: nowDate.clone(), }); expect(gap).not.toBeNull(); expect(gap?.asMilliseconds()).toEqual(moment.duration(-30, 'seconds').asMilliseconds()); @@ -267,10 +170,9 @@ describe('utils', () => { test('it returns an exact 0 gap when the from overlaps with now by 1 minute, the interval is 5 minutes but the previous started is one minute late', () => { const gap = getGapBetweenRuns({ previousStartedAt: nowDate.clone().subtract(6, 'minutes').toDate(), - intervalDuration: moment.duration(5, 'minutes'), - from: 'now-6m', - to: 'now', - now: nowDate.clone(), + startedAt: nowDate.clone().toDate(), + originalFrom: nowDate.clone().subtract(6, 'minutes'), + originalTo: nowDate.clone(), }); expect(gap).not.toBeNull(); expect(gap?.asMilliseconds()).toEqual(moment.duration(0, 'minute').asMilliseconds()); @@ -279,10 +181,9 @@ describe('utils', () => { test('it returns a gap of 30 seconds when the from overlaps with now by 1 minute, the interval is 5 minutes but the previous started is one minute and 30 seconds late', () => { const gap = getGapBetweenRuns({ previousStartedAt: nowDate.clone().subtract(6, 'minutes').subtract(30, 'seconds').toDate(), - intervalDuration: moment.duration(5, 'minutes'), - from: 'now-6m', - to: 'now', - now: nowDate.clone(), + startedAt: nowDate.clone().toDate(), + originalFrom: nowDate.clone().subtract(6, 'minutes'), + originalTo: nowDate.clone(), }); expect(gap).not.toBeNull(); expect(gap?.asMilliseconds()).toEqual(moment.duration(30, 'seconds').asMilliseconds()); @@ -291,10 +192,9 @@ describe('utils', () => { test('it returns a gap of 1 minute when the from overlaps with now by 1 minute, the interval is 5 minutes but the previous started is two minutes late', () => { const gap = getGapBetweenRuns({ previousStartedAt: nowDate.clone().subtract(7, 'minutes').toDate(), - intervalDuration: moment.duration(5, 'minutes'), - from: 'now-6m', - to: 'now', - now: nowDate.clone(), + startedAt: nowDate.clone().toDate(), + originalFrom: nowDate.clone().subtract(6, 'minutes'), + originalTo: nowDate.clone(), }); expect(gap?.asMilliseconds()).not.toBeNull(); expect(gap?.asMilliseconds()).toEqual(moment.duration(1, 'minute').asMilliseconds()); @@ -303,37 +203,12 @@ describe('utils', () => { test('it returns 0 if given a previousStartedAt of null', () => { const gap = getGapBetweenRuns({ previousStartedAt: null, - intervalDuration: moment.duration(5, 'minutes'), - from: 'now-5m', - to: 'now', - now: nowDate.clone(), + startedAt: nowDate.clone().toDate(), + originalFrom: nowDate.clone().subtract(5, 'minutes'), + originalTo: nowDate.clone(), }); expect(gap.asMilliseconds()).toEqual(0); }); - - test('it returns the expected result when "from" is an invalid string such as "invalid"', () => { - const gap = getGapBetweenRuns({ - previousStartedAt: nowDate.clone().subtract(7, 'minutes').toDate(), - intervalDuration: moment.duration(5, 'minutes'), - from: 'invalid', - to: 'now', - now: nowDate.clone(), - }); - expect(gap?.asMilliseconds()).not.toBeNull(); - expect(gap?.asMilliseconds()).toEqual(moment.duration(1, 'minute').asMilliseconds()); - }); - - test('it returns the expected result when "to" is an invalid string such as "invalid"', () => { - const gap = getGapBetweenRuns({ - previousStartedAt: nowDate.clone().subtract(7, 'minutes').toDate(), - intervalDuration: moment.duration(5, 'minutes'), - from: 'now-6m', - to: 'invalid', - now: nowDate.clone(), - }); - expect(gap?.asMilliseconds()).not.toBeNull(); - expect(gap?.asMilliseconds()).toEqual(moment.duration(1, 'minute').asMilliseconds()); - }); }); describe('errorAggregator', () => { @@ -572,6 +447,7 @@ describe('utils', () => { const { tuples, remainingGap } = getRuleRangeTuples({ logger: mockLogger, previousStartedAt: moment().subtract(30, 's').toDate(), + startedAt: moment().subtract(30, 's').toDate(), interval: '30s', from: 'now-30s', to: 'now', @@ -588,6 +464,7 @@ describe('utils', () => { const { tuples, remainingGap } = getRuleRangeTuples({ logger: mockLogger, previousStartedAt: moment().subtract(30, 's').toDate(), + startedAt: moment().subtract(30, 's').toDate(), interval: 'invalid', from: 'now-30s', to: 'now', @@ -604,6 +481,7 @@ describe('utils', () => { const { tuples, remainingGap } = getRuleRangeTuples({ logger: mockLogger, previousStartedAt: moment().subtract(65, 's').toDate(), + startedAt: moment().toDate(), interval: '50s', from: 'now-55s', to: 'now', @@ -619,6 +497,7 @@ describe('utils', () => { const { tuples, remainingGap } = getRuleRangeTuples({ logger: mockLogger, previousStartedAt: moment().subtract(65, 's').toDate(), // 64 is 5 times the interval + lookback, which will trigger max lookback + startedAt: moment().toDate(), interval: '10s', from: 'now-13s', to: 'now', @@ -641,6 +520,7 @@ describe('utils', () => { const { tuples, remainingGap } = getRuleRangeTuples({ logger: mockLogger, previousStartedAt: moment().subtract(-15, 's').toDate(), + startedAt: moment().subtract(-15, 's').toDate(), interval: '10s', from: 'now-13s', to: 'now', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts index 0a3eda70bbd87..c7145ec27701b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts @@ -16,7 +16,6 @@ import { ALERT_INSTANCE_ID, ALERT_RULE_UUID } from '@kbn/rule-data-utils'; import type { ListArray, ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; import { MAX_EXCEPTION_LIST_SIZE } from '@kbn/securitysolution-list-constants'; import { hasLargeValueList } from '@kbn/securitysolution-list-utils'; -import { parseScheduleDates } from '@kbn/securitysolution-io-ts-utils'; import { TimestampOverrideOrUndefined, @@ -414,46 +413,23 @@ export const parseInterval = (intervalString: string): moment.Duration | null => } }; -export const getDriftTolerance = ({ - from, - to, - intervalDuration, - now = moment(), -}: { - from: string; - to: string; - intervalDuration: moment.Duration; - now?: moment.Moment; -}): moment.Duration => { - const toDate = parseScheduleDates(to) ?? now; - const fromDate = parseScheduleDates(from) ?? dateMath.parse('now-6m'); - const timeSegment = toDate.diff(fromDate); - const duration = moment.duration(timeSegment); - - return duration.subtract(intervalDuration); -}; - export const getGapBetweenRuns = ({ previousStartedAt, - intervalDuration, - from, - to, - now = moment(), + originalFrom, + originalTo, + startedAt, }: { previousStartedAt: Date | undefined | null; - intervalDuration: moment.Duration; - from: string; - to: string; - now?: moment.Moment; + originalFrom: moment.Moment; + originalTo: moment.Moment; + startedAt: Date; }): moment.Duration => { if (previousStartedAt == null) { return moment.duration(0); } - const driftTolerance = getDriftTolerance({ from, to, intervalDuration }); - - const diff = moment.duration(now.diff(previousStartedAt)); - const drift = diff.subtract(intervalDuration); - return drift.subtract(driftTolerance); + const driftTolerance = moment.duration(originalTo.diff(originalFrom)); + const currentDuration = moment.duration(moment(startedAt).diff(previousStartedAt)); + return currentDuration.subtract(driftTolerance); }; export const makeFloatString = (num: number): string => Number(num).toFixed(2); @@ -508,6 +484,7 @@ export const getRuleRangeTuples = ({ interval, maxSignals, buildRuleMessage, + startedAt, }: { logger: Logger; previousStartedAt: Date | null | undefined; @@ -516,9 +493,10 @@ export const getRuleRangeTuples = ({ interval: string; maxSignals: number; buildRuleMessage: BuildRuleMessage; + startedAt: Date; }) => { - const originalTo = dateMath.parse(to); - const originalFrom = dateMath.parse(from); + const originalTo = dateMath.parse(to, { forceNow: startedAt }); + const originalFrom = dateMath.parse(from, { forceNow: startedAt }); if (originalTo == null || originalFrom == null) { throw new Error(buildRuleMessage('dateMath parse failed')); } @@ -534,14 +512,19 @@ export const getRuleRangeTuples = ({ logger.error(`Failed to compute gap between rule runs: could not parse rule interval`); return { tuples, remainingGap: moment.duration(0) }; } - const gap = getGapBetweenRuns({ previousStartedAt, intervalDuration, from, to }); + const gap = getGapBetweenRuns({ + previousStartedAt, + originalTo, + originalFrom, + startedAt, + }); const catchup = getNumCatchupIntervals({ gap, intervalDuration, }); const catchupTuples = getCatchupTuples({ - to: originalTo, - from: originalFrom, + originalTo, + originalFrom, ruleParamsMaxSignals: maxSignals, catchup, intervalDuration, @@ -564,22 +547,22 @@ export const getRuleRangeTuples = ({ * @param intervalDuration moment.Duration the interval which the rule runs */ export const getCatchupTuples = ({ - to, - from, + originalTo, + originalFrom, ruleParamsMaxSignals, catchup, intervalDuration, }: { - to: moment.Moment; - from: moment.Moment; + originalTo: moment.Moment; + originalFrom: moment.Moment; ruleParamsMaxSignals: number; catchup: number; intervalDuration: moment.Duration; }): RuleRangeTuple[] => { const catchupTuples: RuleRangeTuple[] = []; const intervalInMilliseconds = intervalDuration.asMilliseconds(); - let currentTo = to; - let currentFrom = from; + let currentTo = originalTo; + let currentFrom = originalFrom; // This loop will create tuples with overlapping time ranges, the same way rule runs have overlapping time // ranges due to the additional lookback. We could choose to create tuples that don't overlap here by using the // "from" value from one tuple as "to" in the next one, however, the overlap matters for rule types like EQL and @@ -719,6 +702,20 @@ export const createSearchAfterReturnTypeFromResponse = ({ }); }; +export interface PreviewReturnType { + totalCount: number; + matrixHistogramData: unknown[]; + errors?: string[] | undefined; + warningMessages?: string[] | undefined; +} + +export const createPreviewReturnType = (): PreviewReturnType => ({ + matrixHistogramData: [], + totalCount: 0, + errors: [], + warningMessages: [], +}); + export const createSearchAfterReturnType = ({ success, warning, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/wrap_hits_factory.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/wrap_hits_factory.ts index 6f040465389fc..22af4dcdb9f4a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/wrap_hits_factory.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/wrap_hits_factory.ts @@ -5,20 +5,21 @@ * 2.0. */ -import { SearchAfterAndBulkCreateParams, WrapHits, WrappedSignalHit } from './types'; +import { WrapHits, WrappedSignalHit } from './types'; import { generateId } from './utils'; import { buildBulkBody } from './build_bulk_body'; import { filterDuplicateSignals } from './filter_duplicate_signals'; import type { ConfigType } from '../../../config'; +import { CompleteRule, RuleParams } from '../schemas/rule_schemas'; export const wrapHitsFactory = ({ - ruleSO, + completeRule, signalsIndex, mergeStrategy, ignoreFields, }: { - ruleSO: SearchAfterAndBulkCreateParams['ruleSO']; + completeRule: CompleteRule; signalsIndex: string; mergeStrategy: ConfigType['alertMergeStrategy']; ignoreFields: ConfigType['alertIgnoreFields']; @@ -27,15 +28,10 @@ export const wrapHitsFactory = const wrappedDocs: WrappedSignalHit[] = events.flatMap((doc) => [ { _index: signalsIndex, - _id: generateId( - doc._index, - doc._id, - String(doc._version), - ruleSO.attributes.params.ruleId ?? '' - ), - _source: buildBulkBody(ruleSO, doc, mergeStrategy, ignoreFields, buildReasonMessage), + _id: generateId(doc._index, doc._id, String(doc._version), completeRule.alertId ?? ''), + _source: buildBulkBody(completeRule, doc, mergeStrategy, ignoreFields, buildReasonMessage), }, ]); - return filterDuplicateSignals(ruleSO.id, wrappedDocs, false); + return filterDuplicateSignals(completeRule.alertId, wrappedDocs, false); }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/wrap_sequences_factory.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/wrap_sequences_factory.ts index f62992f550787..3b93ae824849a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/wrap_sequences_factory.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/wrap_sequences_factory.ts @@ -5,18 +5,19 @@ * 2.0. */ -import { SearchAfterAndBulkCreateParams, WrappedSignalHit, WrapSequences } from './types'; +import { WrappedSignalHit, WrapSequences } from './types'; import { buildSignalGroupFromSequence } from './build_bulk_body'; import { ConfigType } from '../../../config'; +import { CompleteRule, RuleParams } from '../schemas/rule_schemas'; export const wrapSequencesFactory = ({ - ruleSO, + completeRule, signalsIndex, mergeStrategy, ignoreFields, }: { - ruleSO: SearchAfterAndBulkCreateParams['ruleSO']; + completeRule: CompleteRule; signalsIndex: string; mergeStrategy: ConfigType['alertMergeStrategy']; ignoreFields: ConfigType['alertIgnoreFields']; @@ -27,7 +28,7 @@ export const wrapSequencesFactory = ...acc, ...buildSignalGroupFromSequence( sequence, - ruleSO, + completeRule, signalsIndex, mergeStrategy, ignoreFields, diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index f2aa4927a7688..14cf6f0a48799 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -176,6 +176,14 @@ export class Plugin implements ISecuritySolutionPlugin { const { ruleDataService } = plugins.ruleRegistry; let ruleDataClient: IRuleDataClient | null = null; + // rule options are used both to create and preview rules. + const ruleOptions: CreateRuleOptions = { + experimentalFeatures, + logger: this.logger, + ml: plugins.ml, + version: pluginContext.env.packageInfo.version, + }; + if (isRuleRegistryEnabled) { // NOTE: this is not used yet // TODO: convert the aliases to FieldMaps. Requires enhancing FieldMap to support alias path. @@ -203,14 +211,6 @@ export class Plugin implements ISecuritySolutionPlugin { secondaryAlias: config.signalsIndex, }); - // Register rule types via rule-registry - const createRuleOptions: CreateRuleOptions = { - experimentalFeatures, - logger, - ml: plugins.ml, - version: pluginContext.env.packageInfo.version, - }; - const securityRuleTypeWrapper = createSecurityRuleTypeWrapper({ lists: plugins.lists, logger: this.logger, @@ -219,17 +219,13 @@ export class Plugin implements ISecuritySolutionPlugin { eventLogService, }); - plugins.alerting.registerType(securityRuleTypeWrapper(createEqlAlertType(createRuleOptions))); - plugins.alerting.registerType( - securityRuleTypeWrapper(createIndicatorMatchAlertType(createRuleOptions)) - ); - plugins.alerting.registerType(securityRuleTypeWrapper(createMlAlertType(createRuleOptions))); - plugins.alerting.registerType( - securityRuleTypeWrapper(createQueryAlertType(createRuleOptions)) - ); + plugins.alerting.registerType(securityRuleTypeWrapper(createEqlAlertType(ruleOptions))); plugins.alerting.registerType( - securityRuleTypeWrapper(createThresholdAlertType(createRuleOptions)) + securityRuleTypeWrapper(createIndicatorMatchAlertType(ruleOptions)) ); + plugins.alerting.registerType(securityRuleTypeWrapper(createMlAlertType(ruleOptions))); + plugins.alerting.registerType(securityRuleTypeWrapper(createQueryAlertType(ruleOptions))); + plugins.alerting.registerType(securityRuleTypeWrapper(createThresholdAlertType(ruleOptions))); } // TODO We need to get the endpoint routes inside of initRoutes @@ -240,7 +236,8 @@ export class Plugin implements ISecuritySolutionPlugin { plugins.security, plugins.ml, logger, - isRuleRegistryEnabled + isRuleRegistryEnabled, + ruleOptions ); registerEndpointRoutes(router, endpointContext); registerLimitedConcurrencyRoutes(core); diff --git a/x-pack/plugins/security_solution/server/routes/index.ts b/x-pack/plugins/security_solution/server/routes/index.ts index 9d31684907f86..26dbd80a03db4 100644 --- a/x-pack/plugins/security_solution/server/routes/index.ts +++ b/x-pack/plugins/security_solution/server/routes/index.ts @@ -6,7 +6,6 @@ */ import { Logger } from 'src/core/server'; - import { SecuritySolutionPluginRouter } from '../types'; import { createRulesRoute } from '../lib/detection_engine/routes/rules/create_rules_route'; @@ -57,8 +56,11 @@ import { persistPinnedEventRoute } from '../lib/timeline/routes/pinned_events'; import { SetupPlugins } from '../plugin'; import { ConfigType } from '../config'; import { installPrepackedTimelinesRoute } from '../lib/timeline/routes/prepackaged_timelines/install_prepackaged_timelines'; +import { previewRulesRoute } from '../lib/detection_engine/routes/rules/preview_rules_route'; +import { CreateRuleOptions } from '../lib/detection_engine/rule_types/types'; // eslint-disable-next-line no-restricted-imports import { legacyCreateLegacyNotificationRoute } from '../lib/detection_engine/routes/rules/legacy_create_legacy_notification'; +import { createPreviewIndexRoute } from '../lib/detection_engine/routes/index/create_preview_index_route'; export const initRoutes = ( router: SecuritySolutionPluginRouter, @@ -67,7 +69,8 @@ export const initRoutes = ( security: SetupPlugins['security'], ml: SetupPlugins['ml'], logger: Logger, - isRuleRegistryEnabled: boolean + isRuleRegistryEnabled: boolean, + ruleOptions: CreateRuleOptions ) => { // Detection Engine Rule routes that have the REST endpoints of /api/detection_engine/rules // All REST rule creation, deletion, updating, etc...... @@ -77,6 +80,7 @@ export const initRoutes = ( patchRulesRoute(router, ml, isRuleRegistryEnabled); deleteRulesRoute(router, isRuleRegistryEnabled); findRulesRoute(router, logger, isRuleRegistryEnabled); + previewRulesRoute(router, config, ml, security, ruleOptions); // Once we no longer have the legacy notifications system/"side car actions" this should be removed. legacyCreateLegacyNotificationRoute(router, logger); @@ -129,6 +133,9 @@ export const initRoutes = ( readIndexRoute(router, config); deleteIndexRoute(router); + // Detection Engine Preview Index /api/detection_engine/preview/index + createPreviewIndexRoute(router); + // Detection Engine tags routes that have the REST endpoints of /api/detection_engine/tags readTagsRoute(router, isRuleRegistryEnabled); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_ml.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_ml.ts index c78ef18635de7..b495df7570d38 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_ml.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_ml.ts @@ -162,7 +162,6 @@ export default ({ getService }: FtrProviderContext) => { enabled: true, created_by: 'elastic', updated_by: 'elastic', - throttle: null, description: 'Test ML rule description', risk_score: 50, severity: 'critical', diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/generating_signals.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/generating_signals.ts index c954d8aa5721d..b3f89d206bd46 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/generating_signals.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/generating_signals.ts @@ -235,7 +235,7 @@ export default ({ getService }: FtrProviderContext) => { parents: [ { rule: signalNoRule.parents[0].rule, // rule id is always changing so skip testing it - id: '82421e2f4e96058baaa2ed87abbe565403b45edf36348c2b79a4f0e8cc1cd055', + id: signalNoRule.parents[0].id, // id is always changing so skip testing it type: 'signal', index: '.siem-signals-default-000001', depth: 1, @@ -250,7 +250,7 @@ export default ({ getService }: FtrProviderContext) => { }, { rule: signalNoRule.ancestors[1].rule, // rule id is always changing so skip testing it - id: '82421e2f4e96058baaa2ed87abbe565403b45edf36348c2b79a4f0e8cc1cd055', + id: signalNoRule.ancestors[1].id, // id is always changing so skip testing it type: 'signal', index: '.siem-signals-default-000001', depth: 1, @@ -260,7 +260,7 @@ export default ({ getService }: FtrProviderContext) => { depth: 2, parent: { rule: signalNoRule.parent?.rule, // parent.rule is always changing so skip testing it - id: '82421e2f4e96058baaa2ed87abbe565403b45edf36348c2b79a4f0e8cc1cd055', + id: signalNoRule.parent?.id, // parent.id is always changing so skip testing it type: 'signal', index: '.siem-signals-default-000001', depth: 1, @@ -1248,7 +1248,7 @@ export default ({ getService }: FtrProviderContext) => { parents: [ { rule: signalNoRule.parents[0].rule, // rule id is always changing so skip testing it - id: 'c4db4921f2d9152865fd6518c2a2ef3471738e49f607a21319048c69a303f83f', + id: signalNoRule.parents[0].id, // id is always changing so skip testing it type: 'signal', index: '.siem-signals-default-000001', depth: 1, @@ -1263,7 +1263,7 @@ export default ({ getService }: FtrProviderContext) => { }, { rule: signalNoRule.ancestors[1].rule, // rule id is always changing so skip testing it - id: 'c4db4921f2d9152865fd6518c2a2ef3471738e49f607a21319048c69a303f83f', + id: signalNoRule.ancestors[1].id, // id is always changing so skip testing it type: 'signal', index: '.siem-signals-default-000001', depth: 1, @@ -1273,7 +1273,7 @@ export default ({ getService }: FtrProviderContext) => { depth: 2, parent: { rule: signalNoRule.parent?.rule, // parent.rule is always changing so skip testing it - id: 'c4db4921f2d9152865fd6518c2a2ef3471738e49f607a21319048c69a303f83f', + id: signalNoRule.parent?.id, // parent.id is always changing so skip testing it type: 'signal', index: '.siem-signals-default-000001', depth: 1, @@ -1408,7 +1408,7 @@ export default ({ getService }: FtrProviderContext) => { parents: [ { rule: signalNoRule.parents[0].rule, // rule id is always changing so skip testing it - id: '0733d5d2eaed77410a65eec95cfb2df099abc97289b78e2b0b406130e2dbdb33', + id: signalNoRule.parents[0].id, // id is always changing so skip testing it type: 'signal', index: '.siem-signals-default-000001', depth: 1, @@ -1423,7 +1423,7 @@ export default ({ getService }: FtrProviderContext) => { }, { rule: signalNoRule.ancestors[1].rule, // rule id is always changing so skip testing it - id: '0733d5d2eaed77410a65eec95cfb2df099abc97289b78e2b0b406130e2dbdb33', + id: signalNoRule.ancestors[1].id, // id is always changing so skip testing it type: 'signal', index: '.siem-signals-default-000001', depth: 1, @@ -1433,7 +1433,7 @@ export default ({ getService }: FtrProviderContext) => { depth: 2, parent: { rule: signalNoRule.parent?.rule, // parent.rule is always changing so skip testing it - id: '0733d5d2eaed77410a65eec95cfb2df099abc97289b78e2b0b406130e2dbdb33', + id: signalNoRule.parent?.id, // parent.id is always changing so skip testing it type: 'signal', index: '.siem-signals-default-000001', depth: 1, From 31fa0cb13bd5bfb13c7f8e3fda8020a6cf0effbb Mon Sep 17 00:00:00 2001 From: Chris Cowan Date: Wed, 20 Oct 2021 10:19:44 -0600 Subject: [PATCH 136/204] [Metrics UI] Add track_total_hits to Metric Threshold query to support alerts with over 10K documents (#115465) * [Metrics UI] Add track_total_hits to Metric Threshold query * Adding tests * Making the esArchive smaller --- .../metric_threshold/lib/metric_query.ts | 1 + .../apis/metrics_ui/constants.ts | 4 + .../apis/metrics_ui/metric_threshold_alert.ts | 93 +- .../infra/ten_thousand_plus/data.json.gz | Bin 0 -> 410516 bytes .../infra/ten_thousand_plus/mappings.json | 21724 ++++++++++++++++ 5 files changed, 21819 insertions(+), 3 deletions(-) create mode 100644 x-pack/test/functional/es_archives/infra/ten_thousand_plus/data.json.gz create mode 100644 x-pack/test/functional/es_archives/infra/ten_thousand_plus/mappings.json diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/metric_query.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/metric_query.ts index f5ff4448ecb60..59dc398973f8c 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/metric_query.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/lib/metric_query.ts @@ -120,6 +120,7 @@ export const getElasticsearchMetricQuery = ( const parsedFilterQuery = getParsedFilterQuery(filterQuery); return { + track_total_hits: true, query: { bool: { filter: [ diff --git a/x-pack/test/api_integration/apis/metrics_ui/constants.ts b/x-pack/test/api_integration/apis/metrics_ui/constants.ts index 2ca89f2f9ab87..90db71ae08130 100644 --- a/x-pack/test/api_integration/apis/metrics_ui/constants.ts +++ b/x-pack/test/api_integration/apis/metrics_ui/constants.ts @@ -39,4 +39,8 @@ export const DATES = { max: 1609545900000, // '2021-01-02T00:05:00Z' }, }, + ten_thousand_plus: { + min: 1634604480001, // 2021-10-19T00:48:00.001Z + max: 1634604839997, // 2021-10-19T00:53:59.997Z + }, }; diff --git a/x-pack/test/api_integration/apis/metrics_ui/metric_threshold_alert.ts b/x-pack/test/api_integration/apis/metrics_ui/metric_threshold_alert.ts index 66c40e2e6e92d..880d73a236c3b 100644 --- a/x-pack/test/api_integration/apis/metrics_ui/metric_threshold_alert.ts +++ b/x-pack/test/api_integration/apis/metrics_ui/metric_threshold_alert.ts @@ -81,10 +81,95 @@ export default function ({ getService }: FtrProviderContext) { }; describe('Metric Threshold Alerts Executor', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/infra/alerts_test_data')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/infra/alerts_test_data')); - + describe('with 10K plus docs', () => { + before(() => esArchiver.load('x-pack/test/functional/es_archives/infra/ten_thousand_plus')); + after(() => esArchiver.unload('x-pack/test/functional/es_archives/infra/ten_thousand_plus')); + describe('without group by', () => { + it('should alert on document count', async () => { + const params = { + ...baseParams, + criteria: [ + { + timeSize: 5, + timeUnit: 'm', + threshold: [10000], + comparator: Comparator.LT_OR_EQ, + aggType: Aggregators.COUNT, + } as CountMetricExpressionParams, + ], + }; + const config = { + ...configuration, + metricAlias: 'filebeat-*', + }; + const timeFrame = { end: DATES.ten_thousand_plus.max }; + const results = await evaluateAlert(esClient, params, config, [], timeFrame); + expect(results).to.eql([ + { + '*': { + timeSize: 5, + timeUnit: 'm', + threshold: [10000], + comparator: '<=', + aggType: 'count', + metric: 'Document count', + currentValue: 20895, + timestamp: '2021-10-19T00:48:59.997Z', + shouldFire: [false], + shouldWarn: [false], + isNoData: [false], + isError: false, + }, + }, + ]); + }); + }); + describe('with group by', () => { + it('should alert on document count', async () => { + const params = { + ...baseParams, + groupBy: ['event.category'], + criteria: [ + { + timeSize: 5, + timeUnit: 'm', + threshold: [10000], + comparator: Comparator.LT_OR_EQ, + aggType: Aggregators.COUNT, + } as CountMetricExpressionParams, + ], + }; + const config = { + ...configuration, + metricAlias: 'filebeat-*', + }; + const timeFrame = { end: DATES.ten_thousand_plus.max }; + const results = await evaluateAlert(esClient, params, config, [], timeFrame); + expect(results).to.eql([ + { + web: { + timeSize: 5, + timeUnit: 'm', + threshold: [10000], + comparator: '<=', + aggType: 'count', + metric: 'Document count', + currentValue: 20895, + timestamp: '2021-10-19T00:48:59.997Z', + shouldFire: [false], + shouldWarn: [false], + isNoData: [false], + isError: false, + }, + }, + ]); + }); + }); + }); describe('with gauge data', () => { + before(() => esArchiver.load('x-pack/test/functional/es_archives/infra/alerts_test_data')); + after(() => esArchiver.unload('x-pack/test/functional/es_archives/infra/alerts_test_data')); + describe('without groupBy', () => { it('should alert on document count', async () => { const params = { @@ -285,6 +370,8 @@ export default function ({ getService }: FtrProviderContext) { }); describe('with rate data', () => { + before(() => esArchiver.load('x-pack/test/functional/es_archives/infra/alerts_test_data')); + after(() => esArchiver.unload('x-pack/test/functional/es_archives/infra/alerts_test_data')); describe('without groupBy', () => { it('should alert on rate', async () => { const params = { diff --git a/x-pack/test/functional/es_archives/infra/ten_thousand_plus/data.json.gz b/x-pack/test/functional/es_archives/infra/ten_thousand_plus/data.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..d407dbea7cdcbd856a0aca9c8b6c2a3f820914e4 GIT binary patch literal 410516 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
} href="/app/integrations/browse" image="/plugins/kibanaReact/assets/elastic_agent_card.svg" @@ -156,11 +174,15 @@ exports[`ElasticAgentCard renders 1`] = ` - Add Elastic Agent - + + Add Elastic Agent + +
} href="/app/integrations/browse" image="/plugins/kibanaReact/assets/elastic_agent_card.svg" diff --git a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/__snapshots__/no_data_card.test.tsx.snap b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/__snapshots__/no_data_card.test.tsx.snap index fccbbe3a9e8ee..6959e2e29095a 100644 --- a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/__snapshots__/no_data_card.test.tsx.snap +++ b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/__snapshots__/no_data_card.test.tsx.snap @@ -2,7 +2,7 @@ exports[`NoDataCard props button 1`] = `
- + +
`; exports[`NoDataCard props href 1`] = `
- + +
`; exports[`NoDataCard props recommended 1`] = `
+ `; exports[`NoDataCard renders 1`] = `
+ `; diff --git a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_agent_card.tsx b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_agent_card.tsx index b9d412fe4df89..d429f9d712081 100644 --- a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_agent_card.tsx +++ b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/elastic_agent_card.tsx @@ -44,6 +44,7 @@ export const ElasticAgentCard: FunctionComponent = ({ {i18n.translate('kibana-react.noDataPage.elasticAgentCard.noPermission.title', { @@ -92,7 +93,12 @@ export const ElasticAgentCard: FunctionComponent = ({ defaultMessage: `Use Elastic Agent for a simple, unified way to collect data from your machines.`, })} betaBadgeLabel={recommended ? NO_DATA_RECOMMENDED : undefined} - footer={footer} + footer={ +
+ {button} + {footer} +
+ } layout={layout as 'vertical' | undefined} {...cardRest} /> diff --git a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/no_data_card.tsx b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/no_data_card.tsx index 9cc38cc5f6038..ad40a4f8f5499 100644 --- a/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/no_data_card.tsx +++ b/src/plugins/kibana_react/public/page_template/no_data_page/no_data_card/no_data_card.tsx @@ -27,6 +27,7 @@ export const NoDataCard: FunctionComponent = ({ return ( = ({ defaultMessage: `Proceed without collecting data`, })} betaBadgeLabel={recommended ? NO_DATA_RECOMMENDED : undefined} - footer={footer} + footer={
{footer}
} layout={layout as 'vertical' | undefined} {...cardRest} /> diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/integration_preference.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/integration_preference.tsx index 4634996d6bc73..9c9027fb94ac5 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/integration_preference.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/integration_preference.tsx @@ -46,7 +46,7 @@ const link = ( const title = ( ); @@ -115,6 +115,7 @@ export const IntegrationPreference = ({ initialType, onChange }: Props) => { name="preference" /> + ); }; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid.tsx index d2d361cd212a6..91bd29d14d0b3 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid.tsx @@ -18,6 +18,7 @@ import { EuiText, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; + import { FormattedMessage } from '@kbn/i18n/react'; import { Loading } from '../../../components'; @@ -32,6 +33,7 @@ export interface Props { controls?: ReactNode | ReactNode[]; title?: string; list: IntegrationCardItem[]; + featuredList?: JSX.Element | null; initialSearch?: string; setSelectedCategory: (category: string) => void; onSearchChange: (search: string) => void; @@ -48,6 +50,7 @@ export const PackageListGrid: FunctionComponent = ({ onSearchChange, setSelectedCategory, showMissingIntegrationMessage = false, + featuredList = null, callout, }) => { const [searchTerm, setSearchTerm] = useState(initialSearch || ''); @@ -106,42 +109,45 @@ export const PackageListGrid: FunctionComponent = ({ } return ( -
- - - {controlsContent} - - - - {callout ? ( - <> - - {callout} - - ) : null} - - {gridContent} - {showMissingIntegrationMessage && ( - <> - - - - )} - - -
+ <> + {featuredList} +
+ + + {controlsContent} + + + + {callout ? ( + <> + + {callout} + + ) : null} + + {gridContent} + {showMissingIntegrationMessage && ( + <> + + + + )} + + +
+ ); }; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/available_packages.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/available_packages.tsx index 73de0e51bea65..4f13a874532f1 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/available_packages.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/available_packages.tsx @@ -8,7 +8,18 @@ import React, { memo, useMemo, useState } from 'react'; import { useLocation, useHistory, useParams } from 'react-router-dom'; import _ from 'lodash'; -import { EuiHorizontalRule, EuiFlexItem } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { + EuiHorizontalRule, + EuiFlexItem, + EuiFlexGrid, + EuiSpacer, + EuiCard, + EuiIcon, +} from '@elastic/eui'; + +import { useStartServices } from '../../../../hooks'; +import { TrackApplicationView } from '../../../../../../../../../../src/plugins/usage_collection/public'; import { pagePathGetters } from '../../../../constants'; import { @@ -98,6 +109,9 @@ export const AvailablePackages: React.FC = memo(() => { const [preference, setPreference] = useState('recommended'); useBreadcrumbs('integrations_all'); + const { http } = useStartServices(); + const addBasePath = http.basePath.prepend; + const { selectedCategory, searchParam } = getParams( useParams(), useLocation().search @@ -210,8 +224,62 @@ export const AvailablePackages: React.FC = memo(() => { return c.categories.includes(selectedCategory); }); + // TODO: Remove this hard coded list of integrations with a suggestion service + const featuredList = ( + <> + + + + } + href={addBasePath('/app/integrations/detail/endpoint/')} + title={i18n.translate('xpack.fleet.featuredSecurityTitle', { + defaultMessage: 'Endpoint Security', + })} + description={i18n.translate('xpack.fleet.featuredSecurityDesc', { + defaultMessage: + 'Protect your hosts with threat prevention, detection, and deep security data visibility.', + })} + /> + + + + + } + /> + + + + + } + href={addBasePath('/app/enterprise_search/app_search')} + title={i18n.translate('xpack.fleet.featuredSearchTitle', { + defaultMessage: 'Web site crawler', + })} + description={i18n.translate('xpack.fleet.featuredSearchDesc', { + defaultMessage: 'Add search to your website with the App Search web crawler.', + })} + /> + + + + + + ); + return ( { const { docLinks } = useStartServices(); @@ -56,10 +56,6 @@ const Callout = () => ( ); -const title = i18n.translate('xpack.fleet.epmList.installedTitle', { - defaultMessage: 'Installed integrations', -}); - // TODO: clintandrewhall - this component is hard to test due to the hooks, particularly those that use `http` // or `location` to load data. Ideally, we'll split this into "connected" and "pure" components. export const InstalledPackages: React.FC = memo(() => { @@ -115,7 +111,7 @@ export const InstalledPackages: React.FC = memo(() => { const categories: CategoryFacet[] = useMemo( () => [ { - ...ALL_CATEGORY, + ...INSTALLED_CATEGORY, count: allInstalledPackages.length, }, { @@ -153,7 +149,7 @@ export const InstalledPackages: React.FC = memo(() => { return ( Date: Wed, 20 Oct 2021 13:22:44 -0700 Subject: [PATCH 160/204] [Reporting] Upgrade Puppeteer dependency to 10.2.0 (#115682) * [Reporting] Upgrade Puppeteer dependency to 10.2.0 * Update x-pack/plugins/reporting/server/browsers/chromium/paths.ts * self-edit * Apply suggestions from code review Co-authored-by: Michael Dokolin * fix lint Co-authored-by: Michael Dokolin --- package.json | 2 +- x-pack/build_chromium/README.md | 10 +- .../chromium/driver/chromium_driver.ts | 3 +- .../browsers/chromium/driver_factory/index.ts | 12 --- .../server/browsers/chromium/paths.ts | 30 +++--- .../server/browsers/download/download.ts | 2 + .../download/ensure_downloaded.test.ts | 27 ++++-- .../browsers/download/ensure_downloaded.ts | 50 +++++++--- .../reporting/server/browsers/index.ts | 5 +- .../reporting/server/browsers/install.ts | 23 ++++- yarn.lock | 97 ++++++++++++------- 11 files changed, 165 insertions(+), 96 deletions(-) diff --git a/package.json b/package.json index 7c915f2f12228..bbb142ae63b89 100644 --- a/package.json +++ b/package.json @@ -309,7 +309,7 @@ "prop-types": "^15.7.2", "proxy-from-env": "1.0.0", "puid": "1.0.7", - "puppeteer": "^8.0.0", + "puppeteer": "^10.2.0", "query-string": "^6.13.2", "random-word-slugs": "^0.0.5", "raw-loader": "^3.1.0", diff --git a/x-pack/build_chromium/README.md b/x-pack/build_chromium/README.md index 267de376c68ff..c27a654abd671 100644 --- a/x-pack/build_chromium/README.md +++ b/x-pack/build_chromium/README.md @@ -99,7 +99,15 @@ are created in x64 using cross-compiling. CentOS is not supported for building C ## Artifacts After the build completes, there will be a .zip file and a .md5 file in `~/chromium/chromium/src/out/headless`. These are named like so: `chromium-{first_7_of_SHA}-{platform}-{arch}`, for example: `chromium-4747cc2-linux-x64`. -The zip files and md5 files are copied to a staging bucket in GCP storage. +The zip files and md5 files are copied to a **staging** bucket in GCP storage. + +To publish the built artifacts for bunding in Kibana, copy the files from the `headless_shell_staging` bucket to the `headless_shell` bucket. +``` +gsutil cp gs://headless_shell_staging/chromium-d163fd7-linux_arm64.md5 gs://headless_shell/ +gsutil cp gs://headless_shell_staging/chromium-d163fd7-linux_arm64.zip gs://headless_shell/ +``` + +IMPORTANT: Do not replace builds in the `headless_shell` bucket that are referenced in an active Kibana branch. CI tests on that branch will fail since the archive checksum no longer matches the original version. ## Testing Search the Puppeteer Github repo for known issues that could affect our use case, and make sure to test anywhere that is affected. diff --git a/x-pack/plugins/reporting/server/browsers/chromium/driver/chromium_driver.ts b/x-pack/plugins/reporting/server/browsers/chromium/driver/chromium_driver.ts index df91b6fe0ba47..e7c2b68ba2712 100644 --- a/x-pack/plugins/reporting/server/browsers/chromium/driver/chromium_driver.ts +++ b/x-pack/plugins/reporting/server/browsers/chromium/driver/chromium_driver.ts @@ -265,7 +265,7 @@ export class HeadlessChromiumDriver { } // @ts-ignore - // FIXME: use `await page.target().createCDPSession();` + // FIXME: retrieve the client in open() and pass in the client const client = this.page._client; // We have to reach into the Chrome Devtools Protocol to apply headers as using @@ -372,7 +372,6 @@ export class HeadlessChromiumDriver { await client.send('Debugger.enable'); await client.send('Debugger.pause'); - // @ts-ignore const targetId = target._targetId; const wsEndpoint = this.page.browser().wsEndpoint(); const { port } = parseUrl(wsEndpoint); diff --git a/x-pack/plugins/reporting/server/browsers/chromium/driver_factory/index.ts b/x-pack/plugins/reporting/server/browsers/chromium/driver_factory/index.ts index 688dd425fa8f3..264e673d2bf74 100644 --- a/x-pack/plugins/reporting/server/browsers/chromium/driver_factory/index.ts +++ b/x-pack/plugins/reporting/server/browsers/chromium/driver_factory/index.ts @@ -25,18 +25,6 @@ import { HeadlessChromiumDriver } from '../driver'; import { args } from './args'; import { getMetrics, Metrics } from './metrics'; -// Puppeteer type definitions do not match the documentation. -// See https://pptr.dev/#?product=Puppeteer&version=v8.0.0&show=api-puppeteerlaunchoptions -interface ReportingLaunchOptions extends puppeteer.LaunchOptions { - userDataDir?: string; - ignoreHTTPSErrors?: boolean; - args?: string[]; -} - -declare module 'puppeteer' { - function launch(options: ReportingLaunchOptions): Promise; -} - type BrowserConfig = CaptureConfig['browser']['chromium']; export class HeadlessChromiumDriverFactory { diff --git a/x-pack/plugins/reporting/server/browsers/chromium/paths.ts b/x-pack/plugins/reporting/server/browsers/chromium/paths.ts index ed06bba527c43..033ddce8d9ebd 100644 --- a/x-pack/plugins/reporting/server/browsers/chromium/paths.ts +++ b/x-pack/plugins/reporting/server/browsers/chromium/paths.ts @@ -14,6 +14,7 @@ interface PackageInfo { archiveChecksum: string; binaryChecksum: string; binaryRelativePath: string; + revision: number; } enum BaseUrl { @@ -32,8 +33,6 @@ interface CommonPackageInfo extends PackageInfo { } export class ChromiumArchivePaths { - public readonly revision = '856583'; - public readonly packages: Array = [ { platform: 'darwin', @@ -43,34 +42,38 @@ export class ChromiumArchivePaths { binaryChecksum: 'dfcd6e007214175997663c50c8d871ea', binaryRelativePath: 'headless_shell-darwin_x64/headless_shell', location: 'custom', + revision: 856583, }, { platform: 'linux', architecture: 'x64', - archiveFilename: 'chromium-d163fd7-linux_x64.zip', - archiveChecksum: 'fba0a240d409228a3494aef415c300fc', - binaryChecksum: '99cfab472d516038b94ef86649e52871', + archiveFilename: 'chromium-70f5d88-linux_x64.zip', + archiveChecksum: '7b1c9c2fb613444fbdf004a3b75a58df', + binaryChecksum: '82e80f9727a88ba3836ce230134bd126', binaryRelativePath: 'headless_shell-linux_x64/headless_shell', location: 'custom', + revision: 901912, }, { platform: 'linux', architecture: 'arm64', - archiveFilename: 'chromium-d163fd7-linux_arm64.zip', - archiveChecksum: '29834735bc2f0e0d9134c33bc0580fb6', - binaryChecksum: '13baccf2e5c8385cb9d9588db6a9e2c2', + archiveFilename: 'chromium-70f5d88-linux_arm64.zip', + archiveChecksum: '4a0217cfe7da86ad1e3d0e9e5895ddb5', + binaryChecksum: '29e943fbee6d87a217abd6cb6747058e', binaryRelativePath: 'headless_shell-linux_arm64/headless_shell', location: 'custom', + revision: 901912, }, { platform: 'win32', architecture: 'x64', archiveFilename: 'chrome-win.zip', - archiveChecksum: '64999a384bfb6c96c50c4cb6810dbc05', - binaryChecksum: '13b8bbb4a12f9036b8cc3b57b3a71fec', + archiveChecksum: '861bb8b7b8406a6934a87d3cbbce61d9', + binaryChecksum: 'ffa0949471e1b9a57bc8f8633fca9c7b', binaryRelativePath: 'chrome-win\\chrome.exe', location: 'common', archivePath: 'Win', + revision: 901912, }, ]; @@ -82,7 +85,8 @@ export class ChromiumArchivePaths { } public resolvePath(p: PackageInfo) { - return path.resolve(this.archivesPath, p.archiveFilename); + // adding architecture to the path allows it to download two binaries that have the same name, but are different architecture + return path.resolve(this.archivesPath, p.architecture, p.archiveFilename); } public getAllArchiveFilenames(): string[] { @@ -91,9 +95,9 @@ export class ChromiumArchivePaths { public getDownloadUrl(p: CustomPackageInfo | CommonPackageInfo) { if (p.location === 'common') { - return `${BaseUrl.common}/${p.archivePath}/${this.revision}/${p.archiveFilename}`; + return `${BaseUrl.common}/${p.archivePath}/${p.revision}/${p.archiveFilename}`; } - return BaseUrl.custom + '/' + p.archiveFilename; + return BaseUrl.custom + '/' + p.archiveFilename; // revision is not used for URL if package is a custom build } public getBinaryPath(p: PackageInfo) { diff --git a/x-pack/plugins/reporting/server/browsers/download/download.ts b/x-pack/plugins/reporting/server/browsers/download/download.ts index 77efc75ae1aaa..528395fe1afb2 100644 --- a/x-pack/plugins/reporting/server/browsers/download/download.ts +++ b/x-pack/plugins/reporting/server/browsers/download/download.ts @@ -49,6 +49,8 @@ export async function download( resolve(); }); }); + } catch (err) { + throw new Error(`Unable to download ${url}: ${err}`); } finally { closeSync(handle); } diff --git a/x-pack/plugins/reporting/server/browsers/download/ensure_downloaded.test.ts b/x-pack/plugins/reporting/server/browsers/download/ensure_downloaded.test.ts index 344735d6180b6..955e8214af8fa 100644 --- a/x-pack/plugins/reporting/server/browsers/download/ensure_downloaded.test.ts +++ b/x-pack/plugins/reporting/server/browsers/download/ensure_downloaded.test.ts @@ -5,6 +5,7 @@ * 2.0. */ +import path from 'path'; import mockFs from 'mock-fs'; import { existsSync, readdirSync } from 'fs'; import { chromium } from '../chromium'; @@ -27,16 +28,16 @@ describe('ensureBrowserDownloaded', () => { } as unknown as typeof logger; (md5 as jest.MockedFunction).mockImplementation( - async (path) => + async (packagePath) => chromium.paths.packages.find( - (packageInfo) => chromium.paths.resolvePath(packageInfo) === path + (packageInfo) => chromium.paths.resolvePath(packageInfo) === packagePath )?.archiveChecksum ?? 'some-md5' ); (download as jest.MockedFunction).mockImplementation( - async (_url, path) => + async (_url, packagePath) => chromium.paths.packages.find( - (packageInfo) => chromium.paths.resolvePath(packageInfo) === path + (packageInfo) => chromium.paths.resolvePath(packageInfo) === packagePath )?.archiveChecksum ?? 'some-md5' ); @@ -93,11 +94,19 @@ describe('ensureBrowserDownloaded', () => { await ensureBrowserDownloaded(logger); expect(download).not.toHaveBeenCalled(); - expect(readdirSync(chromium.paths.archivesPath)).toEqual( - expect.arrayContaining( - chromium.paths.packages.map(({ archiveFilename }) => archiveFilename) - ) - ); + const paths = [ + readdirSync(path.resolve(chromium.paths.archivesPath + '/x64')), + readdirSync(path.resolve(chromium.paths.archivesPath + '/arm64')), + ]; + + expect(paths).toEqual([ + expect.arrayContaining([ + 'chrome-win.zip', + 'chromium-70f5d88-linux_x64.zip', + 'chromium-d163fd7-darwin_x64.zip', + ]), + expect.arrayContaining(['chromium-70f5d88-linux_arm64.zip']), + ]); }); it('should download again if md5 hash different', async () => { diff --git a/x-pack/plugins/reporting/server/browsers/download/ensure_downloaded.ts b/x-pack/plugins/reporting/server/browsers/download/ensure_downloaded.ts index 55d6395b1bd3d..2766b404f1dd1 100644 --- a/x-pack/plugins/reporting/server/browsers/download/ensure_downloaded.ts +++ b/x-pack/plugins/reporting/server/browsers/download/ensure_downloaded.ts @@ -15,7 +15,6 @@ import { download } from './download'; /** * Check for the downloaded archive of each requested browser type and * download them if they are missing or their checksum is invalid - * @return {Promise} */ export async function ensureBrowserDownloaded(logger: GenericLevelLogger) { await ensureDownloaded([chromium], logger); @@ -25,18 +24,19 @@ export async function ensureBrowserDownloaded(logger: GenericLevelLogger) { * Clears the unexpected files in the browsers archivesPath * and ensures that all packages/archives are downloaded and * that their checksums match the declared value - * @param {BrowserSpec} browsers - * @return {Promise} */ async function ensureDownloaded(browsers: BrowserDownload[], logger: GenericLevelLogger) { await Promise.all( browsers.map(async ({ paths: pSet }) => { - ( - await del(`${pSet.archivesPath}/**/*`, { - force: true, - ignore: pSet.getAllArchiveFilenames(), - }) - ).forEach((path) => logger.warning(`Deleting unexpected file ${path}`)); + const removedFiles = await del(`${pSet.archivesPath}/**/*`, { + force: true, + onlyFiles: true, + ignore: pSet.getAllArchiveFilenames(), + }); + + removedFiles.forEach((path) => { + logger.warning(`Deleting unexpected file ${path}`); + }); const invalidChecksums: string[] = []; await Promise.all( @@ -44,22 +44,44 @@ async function ensureDownloaded(browsers: BrowserDownload[], logger: GenericLeve const { archiveFilename, archiveChecksum } = p; if (archiveFilename && archiveChecksum) { const path = pSet.resolvePath(p); + const pathExists = existsSync(path); + + let foundChecksum: string; + try { + foundChecksum = await md5(path).catch(); + } catch { + foundChecksum = 'MISSING'; + } - if (existsSync(path) && (await md5(path)) === archiveChecksum) { - logger.debug(`Browser archive exists in ${path}`); + if (pathExists && foundChecksum === archiveChecksum) { + logger.debug(`Browser archive for ${p.platform}/${p.architecture} found in ${path} `); return; } + if (!pathExists) { + logger.warning( + `Browser archive for ${p.platform}/${p.architecture} not found in ${path}.` + ); + } + if (foundChecksum !== archiveChecksum) { + logger.warning( + `Browser archive checksum for ${p.platform}/${p.architecture} ` + + `is ${foundChecksum} but ${archiveChecksum} was expected.` + ); + } + const url = pSet.getDownloadUrl(p); try { const downloadedChecksum = await download(url, path, logger); if (downloadedChecksum !== archiveChecksum) { + logger.warning( + `Invalid checksum for ${p.platform}/${p.architecture}: ` + + `expected ${archiveChecksum} got ${downloadedChecksum}` + ); invalidChecksums.push(`${url} => ${path}`); } } catch (err) { - const message = new Error(`Failed to download ${url}`); - logger.error(err); - throw message; + throw new Error(`Failed to download ${url}: ${err}`); } } }) diff --git a/x-pack/plugins/reporting/server/browsers/index.ts b/x-pack/plugins/reporting/server/browsers/index.ts index c47514960bb09..be5c85a6e9581 100644 --- a/x-pack/plugins/reporting/server/browsers/index.ts +++ b/x-pack/plugins/reporting/server/browsers/index.ts @@ -28,7 +28,8 @@ export interface BrowserDownload { } export const initializeBrowserDriverFactory = async (core: ReportingCore, logger: LevelLogger) => { - const { binaryPath$ } = installBrowser(logger); + const chromiumLogger = logger.clone(['chromium']); + const { binaryPath$ } = installBrowser(chromiumLogger); const binaryPath = await binaryPath$.pipe(first()).toPromise(); - return chromium.createDriverFactory(core, binaryPath, logger); + return chromium.createDriverFactory(core, binaryPath, chromiumLogger); }; diff --git a/x-pack/plugins/reporting/server/browsers/install.ts b/x-pack/plugins/reporting/server/browsers/install.ts index 51045c4ef038f..0441bbcfb5306 100644 --- a/x-pack/plugins/reporting/server/browsers/install.ts +++ b/x-pack/plugins/reporting/server/browsers/install.ts @@ -39,15 +39,28 @@ export function installBrowser( const binaryChecksum = await md5(binaryPath).catch(() => ''); if (binaryChecksum !== pkg.binaryChecksum) { - await ensureBrowserDownloaded(logger); - await del(chromiumPath); + logger.warning( + `Found browser binary checksum for ${pkg.platform}/${pkg.architecture} ` + + `is ${binaryChecksum} but ${pkg.binaryChecksum} was expected. Re-installing...` + ); + try { + await del(chromiumPath); + } catch (err) { + logger.error(err); + } - const archive = path.join(paths.archivesPath, pkg.archiveFilename); - logger.info(`Extracting [${archive}] to [${chromiumPath}]`); - await extract(archive, chromiumPath); + try { + await ensureBrowserDownloaded(logger); + const archive = path.join(paths.archivesPath, pkg.architecture, pkg.archiveFilename); + logger.info(`Extracting [${archive}] to [${chromiumPath}]`); + await extract(archive, chromiumPath); + } catch (err) { + logger.error(err); + } } logger.info(`Browser executable: ${binaryPath}`); + binaryPath$.next(binaryPath); // subscribers wait for download and extract to complete }; diff --git a/yarn.lock b/yarn.lock index 8d174464d0a92..23917871ee318 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12243,7 +12243,7 @@ debug@3.X, debug@^3.0.0, debug@^3.0.1, debug@^3.1.0, debug@^3.1.1, debug@^3.2.5, dependencies: ms "^2.1.1" -debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1: +debug@4, debug@4.3.1, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== @@ -12693,10 +12693,10 @@ devtools-protocol@0.0.818844: resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.818844.tgz#d1947278ec85b53e4c8ca598f607a28fa785ba9e" integrity sha512-AD1hi7iVJ8OD0aMLQU5VK0XH9LDlA1+BcPIgrAxPfaibx2DbWucuyOhc4oyQCbnvDDO68nN6/LcKfqTP343Jjg== -devtools-protocol@0.0.854822: - version "0.0.854822" - resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.854822.tgz#eac3a5260a6b3b4e729a09fdc0c77b0d322e777b" - integrity sha512-xd4D8kHQtB0KtWW0c9xBZD5LVtm9chkMOfs/3Yn01RhT/sFIsVtzTtypfKoFfWBaL+7xCYLxjOLkhwPXaX/Kcg== +devtools-protocol@0.0.901419: + version "0.0.901419" + resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.901419.tgz#79b5459c48fe7e1c5563c02bd72f8fec3e0cebcd" + integrity sha512-4INMPwNm9XRpBukhNbF7OB6fNTTCaI8pzy/fXg0xQzAy5h3zL1P8xT3QazgKqBrb/hAYwIBizqDBZ7GtJE74QQ== dezalgo@^1.0.0: version "1.0.3" @@ -20993,7 +20993,7 @@ node-emoji@^1.10.0: dependencies: lodash.toarray "^4.4.0" -node-fetch@^1.0.1, node-fetch@^2.3.0, node-fetch@^2.6.0, node-fetch@^2.6.1: +node-fetch@2.6.1, node-fetch@^1.0.1, node-fetch@^2.3.0, node-fetch@^2.6.0, node-fetch@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== @@ -22441,6 +22441,13 @@ pixelmatch@^5.1.0: dependencies: pngjs "^3.4.0" +pkg-dir@4.2.0, pkg-dir@^4.1.0, pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + pkg-dir@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" @@ -22455,13 +22462,6 @@ pkg-dir@^3.0.0: dependencies: find-up "^3.0.0" -pkg-dir@^4.1.0, pkg-dir@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" - integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== - dependencies: - find-up "^4.0.0" - pkg-up@3.1.0, pkg-up@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5" @@ -23186,6 +23186,11 @@ process@~0.5.1: resolved "https://registry.yarnpkg.com/process/-/process-0.5.2.tgz#1638d8a8e34c2f440a91db95ab9aeb677fc185cf" integrity sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8= +progress@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.1.tgz#c9242169342b1c29d275889c95734621b1952e31" + integrity sha512-OE+a6vzqazc+K6LxJrX5UPyKFvGnL5CYmq2jFGNIBWHpc4QyE49/YOumcrpQFJpfejmvRtbJzgO1zPmMCqlbBg== + progress@^1.1.8: version "1.1.8" resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" @@ -23330,7 +23335,7 @@ proxy-from-env@1.0.0: resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee" integrity sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4= -proxy-from-env@^1.0.0, proxy-from-env@^1.1.0: +proxy-from-env@1.1.0, proxy-from-env@^1.0.0, proxy-from-env@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== @@ -23425,6 +23430,24 @@ pupa@^2.1.1: dependencies: escape-goat "^2.0.0" +puppeteer@^10.2.0: + version "10.4.0" + resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-10.4.0.tgz#a6465ff97fda0576c4ac29601406f67e6fea3dc7" + integrity sha512-2cP8mBoqnu5gzAVpbZ0fRaobBWZM8GEUF4I1F6WbgHrKV/rz7SX8PG2wMymZgD0wo0UBlg2FBPNxlF/xlqW6+w== + dependencies: + debug "4.3.1" + devtools-protocol "0.0.901419" + extract-zip "2.0.1" + https-proxy-agent "5.0.0" + node-fetch "2.6.1" + pkg-dir "4.2.0" + progress "2.0.1" + proxy-from-env "1.1.0" + rimraf "3.0.2" + tar-fs "2.0.0" + unbzip2-stream "1.3.3" + ws "7.4.6" + puppeteer@^5.3.1: version "5.5.0" resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-5.5.0.tgz#331a7edd212ca06b4a556156435f58cbae08af00" @@ -23443,24 +23466,6 @@ puppeteer@^5.3.1: unbzip2-stream "^1.3.3" ws "^7.2.3" -puppeteer@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-8.0.0.tgz#a236669118aa795331c2d0ca19877159e7664705" - integrity sha512-D0RzSWlepeWkxPPdK3xhTcefj8rjah1791GE82Pdjsri49sy11ci/JQsAO8K2NRukqvwEtcI+ImP5F4ZiMvtIQ== - dependencies: - debug "^4.1.0" - devtools-protocol "0.0.854822" - extract-zip "^2.0.0" - https-proxy-agent "^5.0.0" - node-fetch "^2.6.1" - pkg-dir "^4.2.0" - progress "^2.0.1" - proxy-from-env "^1.1.0" - rimraf "^3.0.2" - tar-fs "^2.0.0" - unbzip2-stream "^1.3.3" - ws "^7.2.3" - q@^1.1.2, q@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" @@ -27468,6 +27473,16 @@ tape@^5.0.1: string.prototype.trim "^1.2.1" through "^2.3.8" +tar-fs@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.0.0.tgz#677700fc0c8b337a78bee3623fdc235f21d7afad" + integrity sha512-vaY0obB6Om/fso8a8vakQBzwholQ7v5+uy+tF3Ozvxv1KNezmVQAiWtcNmMHFSFPqL3dJA8ha6gdtFbfX9mcxA== + dependencies: + chownr "^1.1.1" + mkdirp "^0.5.1" + pump "^3.0.0" + tar-stream "^2.0.0" + tar-fs@^2.0.0, tar-fs@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" @@ -28433,6 +28448,14 @@ unbox-primitive@^1.0.1: has-symbols "^1.0.2" which-boxed-primitive "^1.0.2" +unbzip2-stream@1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.3.3.tgz#d156d205e670d8d8c393e1c02ebd506422873f6a" + integrity sha512-fUlAF7U9Ah1Q6EieQ4x4zLNejrRvDWUYmxXUpN3uziFYCHapjWFaCAnreY9bGgxzaMCFAPPpYNng57CypwJVhg== + dependencies: + buffer "^5.2.1" + through "^2.3.8" + unbzip2-stream@^1.3.3: version "1.4.3" resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7" @@ -30276,6 +30299,11 @@ write-pkg@^4.0.0: type-fest "^0.4.1" write-json-file "^3.2.0" +ws@7.4.6, ws@^7.2.3: + version "7.4.6" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" + integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== + ws@>=7.4.6, ws@^7.4.6: version "7.5.5" resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.5.tgz#8b4bc4af518cfabd0473ae4f99144287b33eb881" @@ -30288,11 +30316,6 @@ ws@^6.1.2, ws@^6.2.1: dependencies: async-limiter "~1.0.0" -ws@^7.2.3: - version "7.4.6" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" - integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== - x-is-function@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/x-is-function/-/x-is-function-1.0.4.tgz#5d294dc3d268cbdd062580e0c5df77a391d1fa1e" From abd5e9ffa8865199d632c47b12e294e7e828687b Mon Sep 17 00:00:00 2001 From: Byron Hulcher Date: Wed, 20 Oct 2021 16:56:14 -0400 Subject: [PATCH 161/204] [App Search] Automated Curations UX improvements (#115773) --- .../curation/automated_curation.test.tsx | 48 ++++++++---------- .../curations/curation/automated_curation.tsx | 24 ++++----- .../curations/curation/curation_logic.test.ts | 3 +- .../curations/curation/curation_logic.ts | 5 +- .../curation/manual_curation.test.tsx | 9 ++-- .../curations/curation/manual_curation.tsx | 8 +-- .../curations/curations_logic.test.ts | 3 +- .../components/curations/curations_logic.ts | 3 ++ .../curations/curations_router.test.tsx | 2 +- .../curation_suggestion_logic.ts | 8 +-- .../curations/views/curations.test.tsx | 49 +++++++++++++++++-- .../components/curations/views/curations.tsx | 24 +++++---- 12 files changed, 116 insertions(+), 70 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/automated_curation.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/automated_curation.test.tsx index 944d8315452b0..309924e99f600 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/automated_curation.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/automated_curation.test.tsx @@ -49,6 +49,7 @@ describe('AutomatedCuration', () => { const actions = { convertToManual: jest.fn(), + onSelectPageTab: jest.fn(), }; beforeEach(() => { @@ -62,48 +63,41 @@ describe('AutomatedCuration', () => { const wrapper = shallow(); expect(wrapper.is(AppSearchPageTemplate)); - expect(wrapper.find(PromotedDocuments)).toHaveLength(1); - expect(wrapper.find(OrganicDocuments)).toHaveLength(1); - expect(wrapper.find(History)).toHaveLength(0); }); - it('includes tabs', () => { + it('includes set of tabs in the page header', () => { const wrapper = shallow(); - let tabs = getPageHeaderTabs(wrapper).find(EuiTab); - expect(tabs).toHaveLength(3); + const tabs = getPageHeaderTabs(wrapper).find(EuiTab); - expect(tabs.at(0).prop('isSelected')).toBe(true); + tabs.at(0).simulate('click'); + expect(actions.onSelectPageTab).toHaveBeenNthCalledWith(1, 'promoted'); - expect(tabs.at(1).prop('onClick')).toBeUndefined(); - expect(tabs.at(1).prop('isSelected')).toBe(false); expect(tabs.at(1).prop('disabled')).toBe(true); - expect(tabs.at(2).prop('isSelected')).toBe(false); - - // Clicking on the History tab shows the history view tabs.at(2).simulate('click'); + expect(actions.onSelectPageTab).toHaveBeenNthCalledWith(2, 'history'); + }); - tabs = getPageHeaderTabs(wrapper).find(EuiTab); - - expect(tabs.at(0).prop('isSelected')).toBe(false); - expect(tabs.at(2).prop('isSelected')).toBe(true); + it('renders promoted and organic documents when the promoted tab is selected', () => { + setMockValues({ ...values, selectedPageTab: 'promoted' }); + const wrapper = shallow(); + const tabs = getPageHeaderTabs(wrapper).find(EuiTab); - expect(wrapper.find(PromotedDocuments)).toHaveLength(0); - expect(wrapper.find(OrganicDocuments)).toHaveLength(0); - expect(wrapper.find(History)).toHaveLength(1); + expect(tabs.at(0).prop('isSelected')).toEqual(true); - // Clicking back to the Promoted tab shows promoted documents - tabs.at(0).simulate('click'); + expect(wrapper.find(PromotedDocuments)).toHaveLength(1); + expect(wrapper.find(OrganicDocuments)).toHaveLength(1); + }); - tabs = getPageHeaderTabs(wrapper).find(EuiTab); + it('renders curation history when the history tab is selected', () => { + setMockValues({ ...values, selectedPageTab: 'history' }); + const wrapper = shallow(); + const tabs = getPageHeaderTabs(wrapper).find(EuiTab); - expect(tabs.at(0).prop('isSelected')).toBe(true); - expect(tabs.at(2).prop('isSelected')).toBe(false); + expect(tabs.at(2).prop('isSelected')).toEqual(true); - expect(wrapper.find(PromotedDocuments)).toHaveLength(1); - expect(wrapper.find(OrganicDocuments)).toHaveLength(1); - expect(wrapper.find(History)).toHaveLength(0); + expect(wrapper.find(History)).toHaveLength(1); }); it('initializes CurationLogic with a curationId prop from URL param', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/automated_curation.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/automated_curation.tsx index 276b40ba88677..eefe012cd8a28 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/automated_curation.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/automated_curation.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useState } from 'react'; +import React from 'react'; import { useParams } from 'react-router-dom'; import { useValues, useActions } from 'kea'; @@ -31,23 +31,19 @@ import { DeleteCurationButton } from './delete_curation_button'; import { PromotedDocuments, OrganicDocuments } from './documents'; import { History } from './history'; -const PROMOTED = 'promoted'; -const HISTORY = 'history'; - export const AutomatedCuration: React.FC = () => { const { curationId } = useParams<{ curationId: string }>(); const logic = CurationLogic({ curationId }); - const { convertToManual } = useActions(logic); - const { activeQuery, dataLoading, queries, curation } = useValues(logic); + const { convertToManual, onSelectPageTab } = useActions(logic); + const { activeQuery, dataLoading, queries, curation, selectedPageTab } = useValues(logic); const { engineName } = useValues(EngineLogic); - const [selectedPageTab, setSelectedPageTab] = useState(PROMOTED); const pageTabs = [ { label: PROMOTED_DOCUMENTS_TITLE, append: {curation.promoted.length}, - isSelected: selectedPageTab === PROMOTED, - onClick: () => setSelectedPageTab(PROMOTED), + isSelected: selectedPageTab === 'promoted', + onClick: () => onSelectPageTab('promoted'), }, { label: HIDDEN_DOCUMENTS_TITLE, @@ -62,8 +58,8 @@ export const AutomatedCuration: React.FC = () => { defaultMessage: 'History', } ), - isSelected: selectedPageTab === HISTORY, - onClick: () => setSelectedPageTab(HISTORY), + isSelected: selectedPageTab === 'history', + onClick: () => onSelectPageTab('history'), }, ]; @@ -102,9 +98,9 @@ export const AutomatedCuration: React.FC = () => { }} isLoading={dataLoading} > - {selectedPageTab === PROMOTED && } - {selectedPageTab === PROMOTED && } - {selectedPageTab === HISTORY && ( + {selectedPageTab === 'promoted' && } + {selectedPageTab === 'promoted' && } + {selectedPageTab === 'history' && ( )} diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/curation_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/curation_logic.test.ts index 5c3ac6d700de4..b1f16944c985b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/curation_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/curation_logic.test.ts @@ -250,7 +250,7 @@ describe('CurationLogic', () => { }); describe('onSelectPageTab', () => { - it('should set the selected page tab', () => { + it('should set the selected page tab and clears flash messages', () => { mount({ selectedPageTab: 'promoted', }); @@ -261,6 +261,7 @@ describe('CurationLogic', () => { ...DEFAULT_VALUES, selectedPageTab: 'hidden', }); + expect(clearFlashMessages).toHaveBeenCalled(); }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/curation_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/curation_logic.ts index 7b617dd89e962..6393ccf974225 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/curation_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/curation_logic.ts @@ -21,7 +21,7 @@ import { DELETE_SUCCESS_MESSAGE } from '../constants'; import { Curation } from '../types'; import { addDocument, removeDocument } from '../utils'; -type CurationPageTabs = 'promoted' | 'hidden'; +type CurationPageTabs = 'promoted' | 'history' | 'hidden'; interface CurationValues { dataLoading: boolean; @@ -271,6 +271,9 @@ export const CurationLogic = kea { + clearFlashMessages(); + }, setActiveQuery: () => actions.updateCuration(), setPromotedIds: () => actions.updateCuration(), addPromotedId: () => actions.updateCuration(), diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/manual_curation.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/manual_curation.test.tsx index 103d7be37535b..d739eae55040d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/manual_curation.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/manual_curation.test.tsx @@ -22,7 +22,7 @@ jest.mock('./curation_logic', () => ({ CurationLogic: jest.fn() })); import { CurationLogic } from './curation_logic'; import { DeleteCurationButton } from './delete_curation_button'; -import { PromotedDocuments, HiddenDocuments } from './documents'; +import { PromotedDocuments, HiddenDocuments, OrganicDocuments } from './documents'; import { ManualCuration } from './manual_curation'; import { ActiveQuerySelect, ManageQueriesModal } from './queries'; import { AddResultFlyout } from './results'; @@ -74,13 +74,13 @@ describe('ManualCuration', () => { expect(actions.onSelectPageTab).toHaveBeenNthCalledWith(2, 'hidden'); }); - it('contains a suggested documents callout when the selectedPageTab is ', () => { + it('contains a suggested documents callout', () => { const wrapper = shallow(); expect(wrapper.find(SuggestedDocumentsCallout)).toHaveLength(1); }); - it('renders promoted documents when that tab is selected', () => { + it('renders promoted and organic documents when the promoted tab is selected', () => { setMockValues({ ...values, selectedPageTab: 'promoted' }); const wrapper = shallow(); const tabs = getPageHeaderTabs(wrapper).find(EuiTab); @@ -88,9 +88,10 @@ describe('ManualCuration', () => { expect(tabs.at(0).prop('isSelected')).toEqual(true); expect(wrapper.find(PromotedDocuments)).toHaveLength(1); + expect(wrapper.find(OrganicDocuments)).toHaveLength(1); }); - it('renders hidden documents when that tab is selected', () => { + it('renders hidden documents when the hidden tab is selected', () => { setMockValues({ ...values, selectedPageTab: 'hidden' }); const wrapper = shallow(); const tabs = getPageHeaderTabs(wrapper).find(EuiTab); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/manual_curation.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/manual_curation.tsx index 3aee306e3d2ff..e78a80a5878b8 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/manual_curation.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curation/manual_curation.tsx @@ -26,10 +26,10 @@ import { SuggestedDocumentsCallout } from './suggested_documents_callout'; export const ManualCuration: React.FC = () => { const { curationId } = useParams() as { curationId: string }; - const { onSelectPageTab } = useActions(CurationLogic({ curationId })); - const { dataLoading, queries, selectedPageTab, curation } = useValues( - CurationLogic({ curationId }) - ); + const logic = CurationLogic({ curationId }); + const { onSelectPageTab } = useActions(logic); + const { dataLoading, queries, selectedPageTab, curation } = useValues(logic); + const { isFlyoutOpen } = useValues(AddResultLogic); const pageTabs = [ diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_logic.test.ts index 42c3985e4dcf1..44ff66e5f46eb 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_logic.test.ts @@ -92,7 +92,7 @@ describe('CurationsLogic', () => { }); describe('onSelectPageTab', () => { - it('should set the selected page tab', () => { + it('should set the selected page tab and clear flash messages', () => { mount(); CurationsLogic.actions.onSelectPageTab('settings'); @@ -101,6 +101,7 @@ describe('CurationsLogic', () => { ...DEFAULT_VALUES, selectedPageTab: 'settings', }); + expect(clearFlashMessages).toHaveBeenCalled(); }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_logic.ts index 4419603efddf0..487072584583f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_logic.ts @@ -126,5 +126,8 @@ export const CurationsLogic = kea { + clearFlashMessages(); + }, }), }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.test.tsx index a0fd778ac7dde..c0278c765e85e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.test.tsx @@ -94,7 +94,7 @@ describe('CurationsRouter', () => { expect(MOCK_ACTIONS.loadCurationsSettings).toHaveBeenCalledTimes(1); }); - it('skips loading curation settings when log retention is enabled', () => { + it('skips loading curation settings when log retention is disabled', () => { setMockValues({ ...MOCK_VALUES, logRetention: { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_suggestion_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_suggestion_logic.ts index b206c0c79ed26..8e6c3a9c6a6ae 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_suggestion_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curation_suggestion/curation_suggestion_logic.ts @@ -137,7 +137,7 @@ export const CurationSuggestionLogic = kea< setQueuedSuccessMessage( i18n.translate( 'xpack.enterpriseSearch.appSearch.engine.curations.suggestedCuration.successfullyAppliedMessage', - { defaultMessage: 'Suggestion was succefully applied.' } + { defaultMessage: 'Suggestion was successfully applied.' } ) ); if (suggestion!.operation === 'delete') { @@ -177,7 +177,7 @@ export const CurationSuggestionLogic = kea< 'xpack.enterpriseSearch.appSearch.engine.curations.suggestedCuration.successfullyAutomatedMessage', { defaultMessage: - 'Suggestion was succefully applied and all future suggestions for the query "{query}" will be automatically applied.', + 'Suggestion was successfully applied and all future suggestions for the query "{query}" will be automatically applied.', values: { query: suggestion!.query }, } ) @@ -208,7 +208,7 @@ export const CurationSuggestionLogic = kea< i18n.translate( 'xpack.enterpriseSearch.appSearch.engine.curations.suggestedCuration.successfullyRejectedMessage', { - defaultMessage: 'Suggestion was succefully rejected.', + defaultMessage: 'Suggestion was successfully rejected.', } ) ); @@ -230,7 +230,7 @@ export const CurationSuggestionLogic = kea< 'xpack.enterpriseSearch.appSearch.engine.curations.suggestedCuration.successfullyDisabledMessage', { defaultMessage: - 'Suggestion was succefully rejected and you will no longer receive suggestions for the query "{query}".', + 'Suggestion was successfully rejected and you will no longer receive suggestions for the query "{query}".', values: { query: suggestion!.query }, } ) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.test.tsx index aacabf0ac7303..f446438d83d94 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.test.tsx @@ -47,6 +47,10 @@ describe('Curations', () => { }, }, selectedPageTab: 'overview', + // CurationsSettingsLogic + curationsSettings: { + enabled: true, + }, // LicensingLogic hasPlatinumLicense: true, }; @@ -78,8 +82,6 @@ describe('Curations', () => { tabs.at(2).simulate('click'); expect(actions.onSelectPageTab).toHaveBeenNthCalledWith(3, 'settings'); - // The settings tab should NOT have an icon next to it - expect(tabs.at(2).prop('prepend')).toBeUndefined(); }); it('renders less tabs when less than platinum license', () => { @@ -90,8 +92,47 @@ describe('Curations', () => { const tabs = getPageHeaderTabs(wrapper).find(EuiTab); expect(tabs.length).toBe(2); - // The settings tab should have an icon next to it - expect(tabs.at(1).prop('prepend')).not.toBeUndefined(); + }); + + it('renders a New! badge when less than platinum license', () => { + setMockValues({ ...values, hasPlatinumLicense: false }); + const wrapper = shallow(); + + expect(getPageTitle(wrapper)).toEqual('Curated results'); + + const tabs = getPageHeaderTabs(wrapper).find(EuiTab); + expect(tabs.at(1).prop('append')).not.toBeUndefined(); + }); + + it('renders a New! badge when suggestions are disabled', () => { + setMockValues({ + ...values, + curationsSettings: { + enabled: false, + }, + }); + const wrapper = shallow(); + + expect(getPageTitle(wrapper)).toEqual('Curated results'); + + const tabs = getPageHeaderTabs(wrapper).find(EuiTab); + expect(tabs.at(2).prop('append')).not.toBeUndefined(); + }); + + it('hides the badge when suggestions are enabled and the user has a platinum license', () => { + setMockValues({ + ...values, + hasPlatinumLicense: true, + curationsSettings: { + enabled: true, + }, + }); + const wrapper = shallow(); + + expect(getPageTitle(wrapper)).toEqual('Curated results'); + + const tabs = getPageHeaderTabs(wrapper).find(EuiTab); + expect(tabs.at(2).prop('append')).toBeUndefined(); }); it('renders an overview view', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.tsx index 3d4751fcb343f..e5b064e649af0 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.tsx @@ -9,7 +9,7 @@ import React, { useEffect } from 'react'; import { useValues, useActions } from 'kea'; -import { EuiIcon } from '@elastic/eui'; +import { EuiBadge } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { LicensingLogic } from '../../../../shared/licensing'; @@ -31,7 +31,12 @@ export const Curations: React.FC = () => { const { dataLoading: curationsDataLoading, meta, selectedPageTab } = useValues(CurationsLogic); const { loadCurations, onSelectPageTab } = useActions(CurationsLogic); const { hasPlatinumLicense } = useValues(LicensingLogic); - const { dataLoading: curationsSettingsDataLoading } = useValues(CurationsSettingsLogic); + const { + dataLoading: curationsSettingsDataLoading, + curationsSettings: { enabled: curationsSettingsEnabled }, + } = useValues(CurationsSettingsLogic); + + const suggestionsEnabled = hasPlatinumLicense && curationsSettingsEnabled; const OVERVIEW_TAB = { label: i18n.translate( @@ -61,17 +66,18 @@ export const Curations: React.FC = () => { ), isSelected: selectedPageTab === 'settings', onClick: () => onSelectPageTab('settings'), + append: suggestionsEnabled ? undefined : ( + + {i18n.translate('xpack.enterpriseSearch.appSearch.engine.curations.newBadgeLabel', { + defaultMessage: 'New!', + })} + + ), }; const pageTabs = hasPlatinumLicense ? [OVERVIEW_TAB, HISTORY_TAB, SETTINGS_TAB] - : [ - OVERVIEW_TAB, - { - ...SETTINGS_TAB, - prepend: , - }, - ]; + : [OVERVIEW_TAB, SETTINGS_TAB]; useEffect(() => { loadCurations(); From b879a9a497d5c94ab4b544c240fc8547e6722ec7 Mon Sep 17 00:00:00 2001 From: Thom Heymann <190132+thomheymann@users.noreply.github.com> Date: Wed, 20 Oct 2021 22:17:45 +0100 Subject: [PATCH 162/204] Add interactive setup CLI (#114493) * Add interactive setup CLI * Added tsconfig * ignore all CLI dev.js files when building * add cli_init to the root TS project and setup necessary ref * Fix type errors * Added suggestions from code review * ts fix * fixed build dependencies * Added suggestions from code review * fix type definitions * fix types * upgraded commander to fix ts issues * Revert "upgraded commander to fix ts issues" This reverts commit 52b8943222c52f3f59550ec8e462071e95e969e9. * upgraded commander Co-authored-by: spalger Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- package.json | 6 +- scripts/kibana_setup.js | 9 ++ src/cli_plugin/lib/logger.d.ts | 20 +++ src/cli_setup/cli_setup.ts | 118 ++++++++++++++++++ src/cli_setup/dev.js | 10 ++ src/cli_setup/dist.js | 10 ++ src/cli_setup/jest.config.js | 13 ++ src/cli_setup/utils.test.ts | 76 +++++++++++ src/cli_setup/utils.ts | 91 ++++++++++++++ src/dev/build/tasks/bin/scripts/kibana-setup | 29 +++++ .../build/tasks/bin/scripts/kibana-setup.bat | 35 ++++++ src/dev/build/tasks/copy_source_task.ts | 2 +- .../package_json/find_used_dependencies.ts | 6 +- .../public/enrollment_token_form.tsx | 2 +- .../server/elasticsearch_service.ts | 24 +++- .../interactive_setup/server/routes/enroll.ts | 11 +- tsconfig.json | 5 +- yarn.lock | 5 - 18 files changed, 442 insertions(+), 30 deletions(-) create mode 100644 scripts/kibana_setup.js create mode 100644 src/cli_plugin/lib/logger.d.ts create mode 100644 src/cli_setup/cli_setup.ts create mode 100644 src/cli_setup/dev.js create mode 100644 src/cli_setup/dist.js create mode 100644 src/cli_setup/jest.config.js create mode 100644 src/cli_setup/utils.test.ts create mode 100644 src/cli_setup/utils.ts create mode 100755 src/dev/build/tasks/bin/scripts/kibana-setup create mode 100755 src/dev/build/tasks/bin/scripts/kibana-setup.bat diff --git a/package.json b/package.json index bbb142ae63b89..ddcb701c8456f 100644 --- a/package.json +++ b/package.json @@ -197,7 +197,7 @@ "chroma-js": "^1.4.1", "classnames": "2.2.6", "color": "1.0.3", - "commander": "^3.0.2", + "commander": "^4.1.1", "compare-versions": "3.5.1", "concat-stream": "1.6.2", "constate": "^1.3.2", @@ -248,6 +248,7 @@ "idx": "^2.5.6", "immer": "^9.0.6", "inline-style": "^2.0.0", + "inquirer": "^7.3.3", "intl": "^1.2.5", "intl-format-cache": "^2.1.0", "intl-messageformat": "^2.2.0", @@ -297,6 +298,7 @@ "object-hash": "^1.3.1", "object-path-immutable": "^3.1.1", "opn": "^5.5.0", + "ora": "^4.0.4", "p-limit": "^3.0.1", "p-map": "^4.0.0", "p-retry": "^4.2.0", @@ -720,7 +722,6 @@ "html": "1.0.0", "html-loader": "^0.5.5", "http-proxy": "^1.18.1", - "inquirer": "^7.3.3", "is-glob": "^4.0.1", "is-path-inside": "^3.0.2", "istanbul-instrumenter-loader": "^3.0.1", @@ -762,7 +763,6 @@ "null-loader": "^3.0.0", "nyc": "^15.0.1", "oboe": "^2.1.4", - "ora": "^4.0.4", "parse-link-header": "^1.0.1", "pbf": "3.2.1", "pirates": "^4.0.1", diff --git a/scripts/kibana_setup.js b/scripts/kibana_setup.js new file mode 100644 index 0000000000000..115e4c8073fa0 --- /dev/null +++ b/scripts/kibana_setup.js @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +require('../src/cli_setup/dev'); diff --git a/src/cli_plugin/lib/logger.d.ts b/src/cli_plugin/lib/logger.d.ts new file mode 100644 index 0000000000000..dae3255839677 --- /dev/null +++ b/src/cli_plugin/lib/logger.d.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +interface LoggerOptions { + silent?: boolean; + quiet?: boolean; +} + +export declare class Logger { + constructor(settings?: LoggerOptions); + + log(data: string, sameLine?: boolean): void; + + error(data: string): void; +} diff --git a/src/cli_setup/cli_setup.ts b/src/cli_setup/cli_setup.ts new file mode 100644 index 0000000000000..a23a0a9f25c1e --- /dev/null +++ b/src/cli_setup/cli_setup.ts @@ -0,0 +1,118 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { kibanaPackageJson } from '@kbn/utils'; +import chalk from 'chalk'; +import ora from 'ora'; +import { Command } from 'commander'; +import { getConfigPath } from '@kbn/utils'; + +import { + ElasticsearchService, + EnrollResult, +} from '../plugins/interactive_setup/server/elasticsearch_service'; +import { getDetailedErrorMessage } from '../plugins/interactive_setup/server/errors'; +import { + promptToken, + getCommand, + decodeEnrollmentToken, + kibanaConfigWriter, + elasticsearch, +} from './utils'; +import { Logger } from '../cli_plugin/lib/logger'; + +const program = new Command('bin/kibana-setup'); + +program + .version(kibanaPackageJson.version) + .description( + 'This command walks you through all required steps to securely connect Kibana with Elasticsearch' + ) + .option('-t, --token ', 'Elasticsearch enrollment token') + .option('-s, --silent', 'Prevent all logging'); + +program.parse(process.argv); + +interface SetupOptions { + token?: string; + silent?: boolean; +} + +const options = program.opts() as SetupOptions; +const spinner = ora(); +const logger = new Logger(options); + +async function initCommand() { + const token = decodeEnrollmentToken( + options.token ?? (options.silent ? undefined : await promptToken()) + ); + if (!token) { + logger.error(chalk.red('Invalid enrollment token provided.')); + logger.error(''); + logger.error('To generate a new enrollment token run:'); + logger.error(` ${getCommand('elasticsearch-create-enrollment-token', '-s kibana')}`); + process.exit(1); + } + + if (!(await kibanaConfigWriter.isConfigWritable())) { + logger.error(chalk.red('Kibana does not have enough permissions to write to the config file.')); + logger.error(''); + logger.error('To grant write access run:'); + logger.error(` chmod +w ${getConfigPath()}`); + process.exit(1); + } + + logger.log(''); + if (!options.silent) { + spinner.start(chalk.dim('Configuring Kibana...')); + } + + let configToWrite: EnrollResult; + try { + configToWrite = await elasticsearch.enroll({ + hosts: token.adr, + apiKey: token.key, + caFingerprint: ElasticsearchService.formatFingerprint(token.fgr), + }); + } catch (error) { + if (!options.silent) { + spinner.fail( + `${chalk.bold('Unable to enroll with Elasticsearch:')} ${chalk.red( + `${getDetailedErrorMessage(error)}` + )}` + ); + } + logger.error(''); + logger.error('To generate a new enrollment token run:'); + logger.error(` ${getCommand('elasticsearch-create-enrollment-token', '-s kibana')}`); + process.exit(1); + } + + try { + await kibanaConfigWriter.writeConfig(configToWrite); + } catch (error) { + if (!options.silent) { + spinner.fail( + `${chalk.bold('Unable to configure Kibana:')} ${chalk.red( + `${getDetailedErrorMessage(error)}` + )}` + ); + } + logger.error(chalk.red(`${getDetailedErrorMessage(error)}`)); + process.exit(1); + } + + if (!options.silent) { + spinner.succeed(chalk.bold('Kibana configured successfully.')); + } + logger.log(''); + logger.log('To start Kibana run:'); + logger.log(` ${getCommand('kibana')}`); +} + +initCommand(); diff --git a/src/cli_setup/dev.js b/src/cli_setup/dev.js new file mode 100644 index 0000000000000..cf61672f2ed0a --- /dev/null +++ b/src/cli_setup/dev.js @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +require('../setup_node_env'); +require('./cli_setup'); diff --git a/src/cli_setup/dist.js b/src/cli_setup/dist.js new file mode 100644 index 0000000000000..cbfba1cf45f7f --- /dev/null +++ b/src/cli_setup/dist.js @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +require('../setup_node_env/dist'); +require('./cli_setup'); diff --git a/src/cli_setup/jest.config.js b/src/cli_setup/jest.config.js new file mode 100644 index 0000000000000..53de66e818823 --- /dev/null +++ b/src/cli_setup/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/src/cli_setup'], +}; diff --git a/src/cli_setup/utils.test.ts b/src/cli_setup/utils.test.ts new file mode 100644 index 0000000000000..c9242d4eaeba4 --- /dev/null +++ b/src/cli_setup/utils.test.ts @@ -0,0 +1,76 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { decodeEnrollmentToken, getCommand } from './utils'; +import type { EnrollmentToken } from '../plugins/interactive_setup/common'; + +describe('kibana setup cli', () => { + describe('getCommand', () => { + const originalPlatform = process.platform; + + it('should format windows correctly', () => { + Object.defineProperty(process, 'platform', { + value: 'win32', + }); + expect(getCommand('kibana')).toEqual('bin\\kibana.bat'); + expect(getCommand('kibana', '--silent')).toEqual('bin\\kibana.bat --silent'); + }); + + it('should format unix correctly', () => { + Object.defineProperty(process, 'platform', { + value: 'linux', + }); + expect(getCommand('kibana')).toEqual('bin/kibana'); + expect(getCommand('kibana', '--silent')).toEqual('bin/kibana --silent'); + }); + + afterAll(function () { + Object.defineProperty(process, 'platform', { + value: originalPlatform, + }); + }); + }); + + describe('decodeEnrollmentToken', () => { + const token: EnrollmentToken = { + ver: '8.0.0', + adr: ['localhost:9200'], + fgr: 'AA:C8:2C:2E:09:58:F4:FE:A1:D2:AB:7F:13:70:C2:7D:EB:FD:A2:23:88:13:E4:DA:3A:D0:59:D0:09:00:07:36', + key: 'JH-36HoBo4EYIoVhHh2F:uEo4dksARMq_BSHaAHUr8Q', + }; + + it('should decode a valid token', () => { + expect(decodeEnrollmentToken(btoa(JSON.stringify(token)))).toEqual({ + adr: ['https://localhost:9200'], + fgr: 'AA:C8:2C:2E:09:58:F4:FE:A1:D2:AB:7F:13:70:C2:7D:EB:FD:A2:23:88:13:E4:DA:3A:D0:59:D0:09:00:07:36', + key: 'SkgtMzZIb0JvNEVZSW9WaEhoMkY6dUVvNGRrc0FSTXFfQlNIYUFIVXI4UQ==', + ver: '8.0.0', + }); + }); + + it('should not decode an invalid token', () => { + expect(decodeEnrollmentToken(JSON.stringify(token))).toBeUndefined(); + expect( + decodeEnrollmentToken( + btoa( + JSON.stringify({ + ver: [''], + adr: null, + fgr: false, + key: undefined, + }) + ) + ) + ).toBeUndefined(); + expect(decodeEnrollmentToken(btoa(JSON.stringify({})))).toBeUndefined(); + expect(decodeEnrollmentToken(btoa(JSON.stringify([])))).toBeUndefined(); + expect(decodeEnrollmentToken(btoa(JSON.stringify(null)))).toBeUndefined(); + expect(decodeEnrollmentToken(btoa(JSON.stringify('')))).toBeUndefined(); + }); + }); +}); diff --git a/src/cli_setup/utils.ts b/src/cli_setup/utils.ts new file mode 100644 index 0000000000000..65a46b8f5b278 --- /dev/null +++ b/src/cli_setup/utils.ts @@ -0,0 +1,91 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { getConfigPath } from '@kbn/utils'; +import inquirer from 'inquirer'; +import { duration } from 'moment'; +import { merge } from 'lodash'; + +import { Logger } from '../core/server'; +import { ClusterClient } from '../core/server/elasticsearch/client'; +import { configSchema } from '../core/server/elasticsearch'; +import { ElasticsearchService } from '../plugins/interactive_setup/server/elasticsearch_service'; +import { KibanaConfigWriter } from '../plugins/interactive_setup/server/kibana_config_writer'; +import type { EnrollmentToken } from '../plugins/interactive_setup/common'; + +const noop = () => {}; +const logger: Logger = { + debug: noop, + error: noop, + warn: noop, + trace: noop, + info: noop, + fatal: noop, + log: noop, + get: () => logger, +}; + +export const kibanaConfigWriter = new KibanaConfigWriter(getConfigPath(), logger); +export const elasticsearch = new ElasticsearchService(logger).setup({ + connectionCheckInterval: duration(Infinity), + elasticsearch: { + createClient: (type, config) => { + const defaults = configSchema.validate({}); + return new ClusterClient( + merge( + defaults, + { + hosts: Array.isArray(defaults.hosts) ? defaults.hosts : [defaults.hosts], + }, + config + ), + logger, + type + ); + }, + }, +}); + +export async function promptToken() { + const answers = await inquirer.prompt({ + type: 'input', + name: 'token', + message: 'Enter enrollment token:', + validate: (value = '') => (decodeEnrollmentToken(value) ? true : 'Invalid enrollment token'), + }); + return answers.token; +} + +export function decodeEnrollmentToken(enrollmentToken: string): EnrollmentToken | undefined { + try { + const json = JSON.parse(atob(enrollmentToken)) as EnrollmentToken; + if ( + !Array.isArray(json.adr) || + json.adr.some((adr) => typeof adr !== 'string') || + typeof json.fgr !== 'string' || + typeof json.key !== 'string' || + typeof json.ver !== 'string' + ) { + return; + } + return { ...json, adr: json.adr.map((adr) => `https://${adr}`), key: btoa(json.key) }; + } catch (error) {} // eslint-disable-line no-empty +} + +function btoa(str: string) { + return Buffer.from(str, 'binary').toString('base64'); +} + +function atob(str: string) { + return Buffer.from(str, 'base64').toString('binary'); +} + +export function getCommand(command: string, args?: string) { + const isWindows = process.platform === 'win32'; + return `${isWindows ? `bin\\${command}.bat` : `bin/${command}`}${args ? ` ${args}` : ''}`; +} diff --git a/src/dev/build/tasks/bin/scripts/kibana-setup b/src/dev/build/tasks/bin/scripts/kibana-setup new file mode 100755 index 0000000000000..71f722f4af63f --- /dev/null +++ b/src/dev/build/tasks/bin/scripts/kibana-setup @@ -0,0 +1,29 @@ +#!/bin/sh +SCRIPT=$0 + +# SCRIPT may be an arbitrarily deep series of symlinks. Loop until we have the concrete path. +while [ -h "$SCRIPT" ] ; do + ls=$(ls -ld "$SCRIPT") + # Drop everything prior to -> + link=$(expr "$ls" : '.*-> \(.*\)$') + if expr "$link" : '/.*' > /dev/null; then + SCRIPT="$link" + else + SCRIPT=$(dirname "$SCRIPT")/"$link" + fi +done + +DIR="$(dirname "${SCRIPT}")/.." +CONFIG_DIR=${KBN_PATH_CONF:-"$DIR/config"} +NODE="${DIR}/node/bin/node" +test -x "$NODE" +if [ ! -x "$NODE" ]; then + echo "unable to find usable node.js executable." + exit 1 +fi + +if [ -f "${CONFIG_DIR}/node.options" ]; then + KBN_NODE_OPTS="$(grep -v ^# < ${CONFIG_DIR}/node.options | xargs)" +fi + +NODE_OPTIONS="$KBN_NODE_OPTS $NODE_OPTIONS" "${NODE}" "${DIR}/src/cli_setup/dist" "$@" diff --git a/src/dev/build/tasks/bin/scripts/kibana-setup.bat b/src/dev/build/tasks/bin/scripts/kibana-setup.bat new file mode 100755 index 0000000000000..df463a03ba050 --- /dev/null +++ b/src/dev/build/tasks/bin/scripts/kibana-setup.bat @@ -0,0 +1,35 @@ +@echo off + +SETLOCAL ENABLEDELAYEDEXPANSION + +set SCRIPT_DIR=%~dp0 +for %%I in ("%SCRIPT_DIR%..") do set DIR=%%~dpfI + +set NODE=%DIR%\node\node.exe + +If Not Exist "%NODE%" ( + Echo unable to find usable node.js executable. + Exit /B 1 +) + +set CONFIG_DIR=%KBN_PATH_CONF% +If ["%KBN_PATH_CONF%"] == [] ( + set "CONFIG_DIR=%DIR%\config" +) + +IF EXIST "%CONFIG_DIR%\node.options" ( + for /F "usebackq eol=# tokens=*" %%i in ("%CONFIG_DIR%\node.options") do ( + If [!NODE_OPTIONS!] == [] ( + set "NODE_OPTIONS=%%i" + ) Else ( + set "NODE_OPTIONS=!NODE_OPTIONS! %%i" + ) + ) +) + +TITLE Kibana Setup +"%NODE%" "%DIR%\src\cli_setup\dist" %* + +:finally + +ENDLOCAL diff --git a/src/dev/build/tasks/copy_source_task.ts b/src/dev/build/tasks/copy_source_task.ts index 1e9c0b443eb8b..5c6ac93852533 100644 --- a/src/dev/build/tasks/copy_source_task.ts +++ b/src/dev/build/tasks/copy_source_task.ts @@ -26,7 +26,7 @@ export const CopySource: Task = { '!src/test_utils/**', '!src/fixtures/**', '!src/cli/repl/**', - '!src/cli/dev.js', + '!src/cli*/dev.js', '!src/functional_test_runner/**', '!src/dev/**', '!**/jest.config.js', diff --git a/src/dev/build/tasks/package_json/find_used_dependencies.ts b/src/dev/build/tasks/package_json/find_used_dependencies.ts index 8072287996368..6fb0c0060ecc7 100644 --- a/src/dev/build/tasks/package_json/find_used_dependencies.ts +++ b/src/dev/build/tasks/package_json/find_used_dependencies.ts @@ -22,11 +22,7 @@ async function getDependencies(cwd: string, entries: string[]) { export async function findUsedDependencies(listedPkgDependencies: any, baseDir: any) { // Define the entry points for the server code in order to // start here later looking for the server side dependencies - const mainCodeEntries = [ - Path.resolve(baseDir, `src/cli/dist.js`), - Path.resolve(baseDir, `src/cli_keystore/dist.js`), - Path.resolve(baseDir, `src/cli_plugin/dist.js`), - ]; + const mainCodeEntries = await globby(normalize(Path.resolve(baseDir, `src/cli*/dist.js`))); const discoveredPluginEntries = await globby([ normalize(Path.resolve(baseDir, `src/plugins/**/server/index.js`)), diff --git a/src/plugins/interactive_setup/public/enrollment_token_form.tsx b/src/plugins/interactive_setup/public/enrollment_token_form.tsx index 5a844489433d6..4b692ed4efcc8 100644 --- a/src/plugins/interactive_setup/public/enrollment_token_form.tsx +++ b/src/plugins/interactive_setup/public/enrollment_token_form.tsx @@ -193,7 +193,7 @@ const EnrollmentTokenDetails: FunctionComponent = ( ); -export function decodeEnrollmentToken(enrollmentToken: string) { +export function decodeEnrollmentToken(enrollmentToken: string): EnrollmentToken | undefined { try { const json = JSON.parse(atob(enrollmentToken)) as EnrollmentToken; if ( diff --git a/src/plugins/interactive_setup/server/elasticsearch_service.ts b/src/plugins/interactive_setup/server/elasticsearch_service.ts index 99cf0950eec72..b3b25b13c5a9b 100644 --- a/src/plugins/interactive_setup/server/elasticsearch_service.ts +++ b/src/plugins/interactive_setup/server/elasticsearch_service.ts @@ -34,7 +34,7 @@ import { getDetailedErrorMessage, getErrorStatusCode } from './errors'; export interface EnrollParameters { apiKey: string; - hosts: string[]; + hosts: readonly string[]; caFingerprint: string; } @@ -49,7 +49,7 @@ export interface ElasticsearchServiceSetupDeps { /** * Core Elasticsearch service preboot contract; */ - elasticsearch: ElasticsearchServicePreboot; + elasticsearch: Pick; /** * Interval for the Elasticsearch connection check (whether it's configured or not). @@ -169,7 +169,7 @@ export class ElasticsearchService { * the Elasticsearch node we're enrolling with. Should be in a form of a hex colon-delimited string in upper case. */ private async enroll( - elasticsearch: ElasticsearchServicePreboot, + elasticsearch: Pick, { apiKey, hosts, caFingerprint }: EnrollParameters ) { const scopeableRequest: ScopeableRequest = { headers: { authorization: `ApiKey ${apiKey}` } }; @@ -257,7 +257,7 @@ export class ElasticsearchService { } private async authenticate( - elasticsearch: ElasticsearchServicePreboot, + elasticsearch: Pick, { host, username, password, caCert }: AuthenticateParameters ) { const client = elasticsearch.createClient('authenticate', { @@ -281,7 +281,10 @@ export class ElasticsearchService { } } - private async ping(elasticsearch: ElasticsearchServicePreboot, host: string) { + private async ping( + elasticsearch: Pick, + host: string + ) { const client = elasticsearch.createClient('ping', { hosts: [host], username: '', @@ -393,4 +396,15 @@ export class ElasticsearchService { .replace(/([^\n]{1,65})/g, '$1\n') .replace(/\n$/g, '')}\n-----END CERTIFICATE-----\n`; } + + public static formatFingerprint(caFingerprint: string) { + // Convert a plain hex string returned in the enrollment token to a format that ES client + // expects, i.e. to a colon delimited hex string in upper case: deadbeef -> DE:AD:BE:EF. + return ( + caFingerprint + .toUpperCase() + .match(/.{1,2}/g) + ?.join(':') ?? '' + ); + } } diff --git a/src/plugins/interactive_setup/server/routes/enroll.ts b/src/plugins/interactive_setup/server/routes/enroll.ts index ce5b33d2a83b6..904865d7e5564 100644 --- a/src/plugins/interactive_setup/server/routes/enroll.ts +++ b/src/plugins/interactive_setup/server/routes/enroll.ts @@ -18,6 +18,7 @@ import { ERROR_KIBANA_CONFIG_NOT_WRITABLE, ERROR_OUTSIDE_PREBOOT_STAGE, } from '../../common'; +import { ElasticsearchService } from '../elasticsearch_service'; import type { EnrollResult } from '../elasticsearch_service'; import type { WriteConfigParameters } from '../kibana_config_writer'; import type { RouteDefinitionParams } from './'; @@ -92,20 +93,12 @@ export function defineEnrollRoutes({ }); } - // Convert a plain hex string returned in the enrollment token to a format that ES client - // expects, i.e. to a colon delimited hex string in upper case: deadbeef -> DE:AD:BE:EF. - const colonFormattedCaFingerprint = - request.body.caFingerprint - .toUpperCase() - .match(/.{1,2}/g) - ?.join(':') ?? ''; - let configToWrite: WriteConfigParameters & EnrollResult; try { configToWrite = await elasticsearch.enroll({ apiKey: request.body.apiKey, hosts: request.body.hosts, - caFingerprint: colonFormattedCaFingerprint, + caFingerprint: ElasticsearchService.formatFingerprint(request.body.caFingerprint), }); } catch { // For security reasons, we shouldn't leak to the user whether Elasticsearch node couldn't process enrollment diff --git a/tsconfig.json b/tsconfig.json index 0307525de0156..ba72ca7a3a856 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,6 +8,8 @@ "typings/**/*", "src/cli/**/*", + "src/cli_setup/**/*", + "src/cli_plugin/**/*", "src/dev/**/*", "src/fixtures/**/*", @@ -17,6 +19,7 @@ "references": [ { "path": "./src/core/tsconfig.json" }, { "path": "./src/plugins/usage_collection/tsconfig.json" }, + { "path": "./src/plugins/interactive_setup/tsconfig.json" }, { "path": "./x-pack/plugins/reporting/tsconfig.json" }, ] -} \ No newline at end of file +} diff --git a/yarn.lock b/yarn.lock index 23917871ee318..44bc48206b2b4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10806,11 +10806,6 @@ commander@2.17.x, commander@~2.17.1: resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg== -commander@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.2.tgz#6837c3fb677ad9933d1cfba42dd14d5117d6b39e" - integrity sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow== - commander@^4.0.1, commander@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" From e37c25991ef982f07b532514c88870f2b0dd8f86 Mon Sep 17 00:00:00 2001 From: Rashmi Kulkarni Date: Wed, 20 Oct 2021 14:20:23 -0700 Subject: [PATCH 163/204] un-skip maps/feature_controls/maps_spaces.ts functional test (#113656) --- .../apps/maps/feature_controls/maps_spaces.ts | 31 +++++++++++++------ .../test/functional/page_objects/gis_page.ts | 1 + 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/x-pack/test/functional/apps/maps/feature_controls/maps_spaces.ts b/x-pack/test/functional/apps/maps/feature_controls/maps_spaces.ts index 5e3e3d7142c10..0398bacd1e664 100644 --- a/x-pack/test/functional/apps/maps/feature_controls/maps_spaces.ts +++ b/x-pack/test/functional/apps/maps/feature_controls/maps_spaces.ts @@ -7,14 +7,15 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../ftr_provider_context'; +import { APP_ID } from '../../../../../plugins/maps/common/constants'; export default function ({ getPageObjects, getService }: FtrProviderContext) { const spacesService = getService('spaces'); - const PageObjects = getPageObjects(['common', 'maps', 'security']); + const PageObjects = getPageObjects(['common', 'maps', 'security', 'header']); + const listingTable = getService('listingTable'); const appsMenu = getService('appsMenu'); - // FLAKY: https://github.com/elastic/kibana/issues/38414 - describe.skip('spaces feature controls', () => { + describe('spaces feature controls', () => { before(async () => { PageObjects.maps.setBasePath('/s/custom_space'); }); @@ -46,21 +47,31 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); it(`allows a map to be created`, async () => { - await PageObjects.common.navigateToActualUrl('maps', '', { + await PageObjects.common.navigateToActualUrl(APP_ID, '/map', { basePath: `/s/custom_space`, - ensureCurrentUrl: false, + ensureCurrentUrl: true, shouldLoginIfPrompted: false, }); + await PageObjects.maps.waitForLayersToLoad(); await PageObjects.maps.saveMap('my test map'); }); it(`allows a map to be deleted`, async () => { - await PageObjects.common.navigateToActualUrl('maps', '', { + await PageObjects.common.navigateToActualUrl(APP_ID, '/', { basePath: `/s/custom_space`, - ensureCurrentUrl: false, + ensureCurrentUrl: true, shouldLoginIfPrompted: false, }); - await PageObjects.maps.deleteSavedMaps('my test map'); + + // Can not use maps.deleteSavedMaps because maps.deleteSavedMaps will + // navigate to default space if on list page check fails + await listingTable.searchForItemWithName('my test map'); + await PageObjects.header.waitUntilLoadingHasFinished(); + await listingTable.checkListingSelectAllCheckbox(); + await listingTable.clickDeleteSelected(); + await PageObjects.common.clickConfirmOnModal(); + await PageObjects.header.waitUntilLoadingHasFinished(); + await listingTable.expectItemsCount('map', 0); }); }); @@ -78,9 +89,9 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); it(`returns a 404`, async () => { - await PageObjects.common.navigateToActualUrl('maps', '', { + await PageObjects.common.navigateToActualUrl(APP_ID, '/', { basePath: '/s/custom_space', - ensureCurrentUrl: false, + ensureCurrentUrl: true, shouldLoginIfPrompted: false, }); const messageText = await PageObjects.common.getJsonBodyText(); diff --git a/x-pack/test/functional/page_objects/gis_page.ts b/x-pack/test/functional/page_objects/gis_page.ts index cd20863065688..629402a0a4768 100644 --- a/x-pack/test/functional/page_objects/gis_page.ts +++ b/x-pack/test/functional/page_objects/gis_page.ts @@ -164,6 +164,7 @@ export class GisPageObject extends FtrService { await this.testSubjects.click('savedObjectTitle'); } await this.testSubjects.clickWhenNotDisabled('confirmSaveSavedObjectButton'); + await this.header.waitUntilLoadingHasFinished(); } async clickSaveAndReturnButton() { From 8a0a96e422e13f9dbb04bb671581bfa37a48f4b9 Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Wed, 20 Oct 2021 17:48:15 -0400 Subject: [PATCH 164/204] fix ueba store (#115842) --- x-pack/plugins/security_solution/public/plugin.tsx | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/security_solution/public/plugin.tsx b/x-pack/plugins/security_solution/public/plugin.tsx index 4885538269264..f016c9712c650 100644 --- a/x-pack/plugins/security_solution/public/plugin.tsx +++ b/x-pack/plugins/security_solution/public/plugin.tsx @@ -384,10 +384,8 @@ export class Plugin implements IPlugin Date: Wed, 20 Oct 2021 14:54:45 -0700 Subject: [PATCH 165/204] skip flaky suite (#115859) --- x-pack/test/accessibility/apps/upgrade_assistant.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/accessibility/apps/upgrade_assistant.ts b/x-pack/test/accessibility/apps/upgrade_assistant.ts index c96b21ba21820..0ea2fd8ce4b4e 100644 --- a/x-pack/test/accessibility/apps/upgrade_assistant.ts +++ b/x-pack/test/accessibility/apps/upgrade_assistant.ts @@ -13,7 +13,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const testSubjects = getService('testSubjects'); const retry = getService('retry'); - describe('Upgrade Assistant', () => { + // Failing: See https://github.com/elastic/kibana/issues/115859 + describe.skip('Upgrade Assistant', () => { before(async () => { await PageObjects.upgradeAssistant.navigateToPage(); }); From 35114175b67ba19c15d1f5ee32dc7259feb3b651 Mon Sep 17 00:00:00 2001 From: Brian Seeders Date: Wed, 20 Oct 2021 18:09:49 -0400 Subject: [PATCH 166/204] [buildkite] FTSR / limit concurrency and total jobs (#115857) --- .buildkite/pipelines/flaky_tests/pipeline.js | 8 +------ .buildkite/pipelines/flaky_tests/runner.js | 25 ++++++++++++++++---- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/.buildkite/pipelines/flaky_tests/pipeline.js b/.buildkite/pipelines/flaky_tests/pipeline.js index 1d390ed0d1b63..5f3633860dfe3 100644 --- a/.buildkite/pipelines/flaky_tests/pipeline.js +++ b/.buildkite/pipelines/flaky_tests/pipeline.js @@ -17,12 +17,6 @@ const inputs = [ default: 0, required: true, }, - { - key: 'ftsr-concurrency', - text: 'Max concurrency per step', - default: 20, - required: true, - }, ]; for (let i = 1; i <= OSS_CI_GROUPS; i++) { @@ -36,7 +30,7 @@ for (let i = 1; i <= XPACK_CI_GROUPS; i++) { const pipeline = { steps: [ { - input: 'Number of Runs', + input: 'Number of Runs - Click Me', fields: inputs, }, { diff --git a/.buildkite/pipelines/flaky_tests/runner.js b/.buildkite/pipelines/flaky_tests/runner.js index 46c390ce455ca..bdb163504f46c 100644 --- a/.buildkite/pipelines/flaky_tests/runner.js +++ b/.buildkite/pipelines/flaky_tests/runner.js @@ -9,8 +9,10 @@ const overrideCount = parseInt( execSync(`buildkite-agent meta-data get 'ftsr-override-count'`).toString().trim() ); -const concurrency = - parseInt(execSync(`buildkite-agent meta-data get 'ftsr-concurrency'`).toString().trim()) || 20; +const concurrency = 25; +const initialJobs = 3; + +let totalJobs = initialJobs; const testSuites = []; for (const key of keys) { @@ -21,12 +23,25 @@ for (const key of keys) { const value = overrideCount || execSync(`buildkite-agent meta-data get '${key}'`).toString().trim(); + const count = value === '' ? defaultCount : parseInt(value); + totalJobs += count; + testSuites.push({ key: key.replace('ftsr-suite/', ''), - count: value === '' ? defaultCount : parseInt(value), + count: count, }); } +if (totalJobs > 500) { + console.error('+++ Too many tests'); + console.error( + `Buildkite builds can only contain 500 steps in total. Found ${totalJobs} in total. Make sure your test runs are less than ${ + 500 - initialJobs + }` + ); + process.exit(1); +} + const steps = []; const pipeline = { env: { @@ -46,7 +61,7 @@ steps.push({ for (const testSuite of testSuites) { const TEST_SUITE = testSuite.key; const RUN_COUNT = testSuite.count; - const UUID = TEST_SUITE + process.env.UUID; + const UUID = process.env.UUID; const JOB_PARTS = TEST_SUITE.split('/'); const IS_XPACK = JOB_PARTS[0] === 'xpack'; @@ -65,6 +80,7 @@ for (const testSuite of testSuites) { parallelism: RUN_COUNT, concurrency: concurrency, concurrency_group: UUID, + concurrency_method: 'eager', }); } else { steps.push({ @@ -75,6 +91,7 @@ for (const testSuite of testSuites) { parallelism: RUN_COUNT, concurrency: concurrency, concurrency_group: UUID, + concurrency_method: 'eager', }); } } From 9ca48d05f34d3404703d8301cb824ed74421e54f Mon Sep 17 00:00:00 2001 From: Frank Hassanabad Date: Wed, 20 Oct 2021 16:25:33 -0600 Subject: [PATCH 167/204] Adds one time conflict retry and cleans up the exception lists to use the REST API (#115848) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Improves FTR/e2e conflict retries with exception lists and security rules. Fixes: https://github.com/elastic/kibana/issues/115734 https://github.com/elastic/kibana/issues/115769 https://github.com/elastic/kibana/issues/115715 https://github.com/elastic/kibana/issues/115702 https://github.com/elastic/kibana/issues/115701 This past week we have been seeing increasing flake across tests involving `exception_lists` involving a `409 conflict` on our tests. Looking at each of the tests above and the flake it looks like we were calling Elasticsearch directly within the `.kibana` index to delete the exception list and list items as a shortcut: ``` export const deleteAllExceptions = async (es: KibanaClient): Promise => { return countDownES(async () => { return es.deleteByQuery({ index: '.kibana', q: 'type:exception-list or type:exception-list-agnostic', wait_for_completion: true, refresh: true, body: {}, }); }, 'deleteAllExceptions'); }; ``` Although I think we did everything correctly `wait_for_completion: true` and `refresh: true` within the tests there might be a slight race condition where the delete by query does not immediately happen for us. Since we should prefer to use direct REST API's where we can instead of calling into `.kibana` I changed this to using the exception list API: ``` export const deleteAllExceptions = async ( supertest: SuperTest.SuperTest ): Promise => { await countDownTest( async () => { const { body } = await supertest .get(`${EXCEPTION_LIST_URL}/_find?per_page=9999`) .set('kbn-xsrf', 'true') .send(); const ids: string[] = body.data.map((exception: ExceptionList) => exception.id); for await (const id of ids) { await supertest.delete(`${EXCEPTION_LIST_URL}?id=${id}`).set('kbn-xsrf', 'true').send(); } const { body: finalCheck } = await supertest .get(`${EXCEPTION_LIST_URL}/_find`) .set('kbn-xsrf', 'true') .send(); return finalCheck.data.length === 0; }, 'deleteAllExceptions', 50, 1000 ); }; ``` The additional final check above should ensure it sees that the data has been deleted before returning. Otherwise it will loop around again and keep trying. I also improve both the `createRules` and `createExceptionList` by introducing a one-time, "detect if in conflict" and then "remove if in conflict" within those tests. This should help safe guard against flake if the above does not fix it. I also added more logging statements in case we do encounter this again on the CI system we can further trouble shoot it and add additional retry logic/fix logic. A good side effect is if now you kill your tests half way through and restart them, the additional "detect if conflict" will recover your test for you as a developer. So 👍 that is an added benefit. Example error message you would get (but not test failure) if you remove one of the cleanup sections in the `afterEach` or if you kill a test half way through and then restart it as an engineer: ``` └-: "is" operator └-> "before all" hook for "should find all the text from the data set when no exceptions are set on the rule" └-> should find all the text from the data set when no exceptions are set on the rule └-> "before each" hook: global before each for "should find all the text from the data set when no exceptions are set on the rule" └-> "before each" hook for "should find all the text from the data set when no exceptions are set on the rule" When creating a rule found an unexpected conflict (409), will attempt a cleanup and one time re-try. This usually indicates a bad cleanup or race condition within the tests: {"message":"rule_id: \"rule-1\" already exists","status_code":409} └- ✓ pass (7.9s) ``` ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --- .../tests/create_endpoint_exceptions.ts | 3 +- .../tests/create_exceptions.ts | 4 +- .../security_and_spaces/tests/create_ml.ts | 5 +- .../exception_operators_data_types/date.ts | 3 +- .../exception_operators_data_types/double.ts | 3 +- .../exception_operators_data_types/float.ts | 3 +- .../exception_operators_data_types/integer.ts | 3 +- .../exception_operators_data_types/ip.ts | 3 +- .../ip_array.ts | 3 +- .../exception_operators_data_types/keyword.ts | 3 +- .../keyword_array.ts | 3 +- .../exception_operators_data_types/long.ts | 3 +- .../exception_operators_data_types/text.ts | 3 +- .../text_array.ts | 3 +- .../detection_engine_api_integration/utils.ts | 149 +++++++++++++++--- .../tests/create_exception_list_items.ts | 3 +- .../tests/create_exception_lists.ts | 3 +- .../tests/delete_exception_list_items.ts | 3 +- .../tests/delete_exception_lists.ts | 3 +- .../security_and_spaces/tests/delete_lists.ts | 3 +- .../tests/export_exception_list.ts | 3 +- .../tests/find_exception_list_items.ts | 3 +- .../tests/find_exception_lists.ts | 3 +- .../tests/read_exception_list_items.ts | 3 +- .../tests/read_exception_lists.ts | 3 +- .../tests/summary_exception_lists.ts | 3 +- .../tests/update_exception_list_items.ts | 3 +- .../tests/update_exception_lists.ts | 3 +- x-pack/test/lists_api_integration/utils.ts | 50 ++++-- 29 files changed, 189 insertions(+), 94 deletions(-) diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_endpoint_exceptions.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_endpoint_exceptions.ts index 6d04ffc67c573..6c6fcc366782a 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_endpoint_exceptions.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_endpoint_exceptions.ts @@ -69,7 +69,6 @@ export const getHostHits = async ( export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); - const es = getService('es'); describe('Rule exception operators for endpoints', () => { before(async () => { @@ -94,7 +93,7 @@ export default ({ getService }: FtrProviderContext) => { afterEach(async () => { await deleteSignalsIndex(supertest); await deleteAllAlerts(supertest); - await deleteAllExceptions(es); + await deleteAllExceptions(supertest); await deleteListsIndex(supertest); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_exceptions.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_exceptions.ts index 0d1e353447e99..1882f488e5a17 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_exceptions.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_exceptions.ts @@ -79,7 +79,7 @@ export default ({ getService }: FtrProviderContext) => { afterEach(async () => { await deleteSignalsIndex(supertest); await deleteAllAlerts(supertest); - await deleteAllExceptions(es); + await deleteAllExceptions(supertest); }); describe('elastic admin', () => { @@ -550,7 +550,7 @@ export default ({ getService }: FtrProviderContext) => { afterEach(async () => { await deleteSignalsIndex(supertest); await deleteAllAlerts(supertest); - await deleteAllExceptions(es); + await deleteAllExceptions(supertest); }); it('should be able to execute against an exception list that does not include valid entries and get back 10 signals', async () => { diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_ml.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_ml.ts index b495df7570d38..2210cf8efd355 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_ml.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_ml.ts @@ -211,9 +211,10 @@ export default ({ getService }: FtrProviderContext) => { const signalsOpen = await getOpenSignals(supertest, es, createdRule); expect(signalsOpen.hits.hits.length).eql(7); }); + describe('with non-value list exception', () => { afterEach(async () => { - await deleteAllExceptions(es); + await deleteAllExceptions(supertest); }); it('generates no signals when an exception is added for an ML rule', async () => { const createdRule = await createRuleWithExceptionEntries(supertest, testRule, [ @@ -238,7 +239,7 @@ export default ({ getService }: FtrProviderContext) => { afterEach(async () => { await deleteListsIndex(supertest); - await deleteAllExceptions(es); + await deleteAllExceptions(supertest); }); it('generates no signals when a value list exception is added for an ML rule', async () => { diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/date.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/date.ts index c2c7313762ae7..742bbf7285e95 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/date.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/date.ts @@ -30,7 +30,6 @@ import { export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); - const es = getService('es'); describe('Rule exception operators for data type date', () => { before(async () => { @@ -49,7 +48,7 @@ export default ({ getService }: FtrProviderContext) => { afterEach(async () => { await deleteSignalsIndex(supertest); await deleteAllAlerts(supertest); - await deleteAllExceptions(es); + await deleteAllExceptions(supertest); await deleteListsIndex(supertest); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/double.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/double.ts index 7f659d6795f9a..a2639ea522447 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/double.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/double.ts @@ -30,7 +30,6 @@ import { export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); - const es = getService('es'); describe('Rule exception operators for data type double', () => { before(async () => { @@ -53,7 +52,7 @@ export default ({ getService }: FtrProviderContext) => { afterEach(async () => { await deleteSignalsIndex(supertest); await deleteAllAlerts(supertest); - await deleteAllExceptions(es); + await deleteAllExceptions(supertest); await deleteListsIndex(supertest); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/float.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/float.ts index 912596ed7ca00..288e2353166ee 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/float.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/float.ts @@ -30,7 +30,6 @@ import { export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); - const es = getService('es'); describe('Rule exception operators for data type float', () => { before(async () => { @@ -51,7 +50,7 @@ export default ({ getService }: FtrProviderContext) => { afterEach(async () => { await deleteSignalsIndex(supertest); await deleteAllAlerts(supertest); - await deleteAllExceptions(es); + await deleteAllExceptions(supertest); await deleteListsIndex(supertest); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/integer.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/integer.ts index da9219e4b52f6..459034cd06569 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/integer.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/integer.ts @@ -30,7 +30,6 @@ import { export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); - const es = getService('es'); describe('Rule exception operators for data type integer', () => { before(async () => { @@ -53,7 +52,7 @@ export default ({ getService }: FtrProviderContext) => { afterEach(async () => { await deleteSignalsIndex(supertest); await deleteAllAlerts(supertest); - await deleteAllExceptions(es); + await deleteAllExceptions(supertest); await deleteListsIndex(supertest); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip.ts index 3a65a0f0a67e5..2497bf096550a 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip.ts @@ -30,7 +30,6 @@ import { export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); - const es = getService('es'); describe('Rule exception operators for data type ip', () => { before(async () => { @@ -49,7 +48,7 @@ export default ({ getService }: FtrProviderContext) => { afterEach(async () => { await deleteSignalsIndex(supertest); await deleteAllAlerts(supertest); - await deleteAllExceptions(es); + await deleteAllExceptions(supertest); await deleteListsIndex(supertest); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip_array.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip_array.ts index 526c6d1c988ce..5df01ff80d67b 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip_array.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip_array.ts @@ -30,7 +30,6 @@ import { export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); - const es = getService('es'); describe('Rule exception operators for data type ip', () => { before(async () => { @@ -49,7 +48,7 @@ export default ({ getService }: FtrProviderContext) => { afterEach(async () => { await deleteSignalsIndex(supertest); await deleteAllAlerts(supertest); - await deleteAllExceptions(es); + await deleteAllExceptions(supertest); await deleteListsIndex(supertest); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/keyword.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/keyword.ts index 4c70dbdf170c7..8a184025b00e2 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/keyword.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/keyword.ts @@ -30,7 +30,6 @@ import { export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); - const es = getService('es'); describe('Rule exception operators for data type keyword', () => { before(async () => { @@ -49,7 +48,7 @@ export default ({ getService }: FtrProviderContext) => { afterEach(async () => { await deleteSignalsIndex(supertest); await deleteAllAlerts(supertest); - await deleteAllExceptions(es); + await deleteAllExceptions(supertest); await deleteListsIndex(supertest); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/keyword_array.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/keyword_array.ts index 8571aa8eeaa60..092b81bf446b8 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/keyword_array.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/keyword_array.ts @@ -30,7 +30,6 @@ import { export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); - const es = getService('es'); describe('Rule exception operators for data type keyword', () => { before(async () => { @@ -51,7 +50,7 @@ export default ({ getService }: FtrProviderContext) => { afterEach(async () => { await deleteSignalsIndex(supertest); await deleteAllAlerts(supertest); - await deleteAllExceptions(es); + await deleteAllExceptions(supertest); await deleteListsIndex(supertest); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/long.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/long.ts index 8d5f1515e4ab6..f5bf3b627bc2f 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/long.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/long.ts @@ -30,7 +30,6 @@ import { export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); - const es = getService('es'); describe('Rule exception operators for data type long', () => { before(async () => { @@ -51,7 +50,7 @@ export default ({ getService }: FtrProviderContext) => { afterEach(async () => { await deleteSignalsIndex(supertest); await deleteAllAlerts(supertest); - await deleteAllExceptions(es); + await deleteAllExceptions(supertest); await deleteListsIndex(supertest); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text.ts index 367e68f7f9ed1..ff2f680654047 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text.ts @@ -31,7 +31,6 @@ import { export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); - const es = getService('es'); describe('Rule exception operators for data type text', () => { before(async () => { @@ -52,7 +51,7 @@ export default ({ getService }: FtrProviderContext) => { afterEach(async () => { await deleteSignalsIndex(supertest); await deleteAllAlerts(supertest); - await deleteAllExceptions(es); + await deleteAllExceptions(supertest); await deleteListsIndex(supertest); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text_array.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text_array.ts index 3eedabd41d663..3bcf8692d58f9 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text_array.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text_array.ts @@ -30,7 +30,6 @@ import { export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); - const es = getService('es'); describe('Rule exception operators for data type text', () => { before(async () => { @@ -49,7 +48,7 @@ export default ({ getService }: FtrProviderContext) => { afterEach(async () => { await deleteSignalsIndex(supertest); await deleteAllAlerts(supertest); - await deleteAllExceptions(es); + await deleteAllExceptions(supertest); await deleteListsIndex(supertest); }); diff --git a/x-pack/test/detection_engine_api_integration/utils.ts b/x-pack/test/detection_engine_api_integration/utils.ts index dd1b1ba966175..edb2db3e9a261 100644 --- a/x-pack/test/detection_engine_api_integration/utils.ts +++ b/x-pack/test/detection_engine_api_integration/utils.ts @@ -413,17 +413,12 @@ export const getSimpleMlRuleOutput = (ruleId = 'rule-1'): Partial = * @param supertest The supertest agent. */ export const deleteAllAlerts = async ( - supertest: SuperTest.SuperTest, - space?: string + supertest: SuperTest.SuperTest ): Promise => { await countDownTest( async () => { const { body } = await supertest - .get( - space - ? `/s/${space}${DETECTION_ENGINE_RULES_URL}/_find?per_page=9999` - : `${DETECTION_ENGINE_RULES_URL}/_find?per_page=9999` - ) + .get(`${DETECTION_ENGINE_RULES_URL}/_find?per_page=9999`) .set('kbn-xsrf', 'true') .send(); @@ -432,11 +427,7 @@ export const deleteAllAlerts = async ( })); await supertest - .post( - space - ? `/s/${space}${DETECTION_ENGINE_RULES_URL}/_bulk_delete` - : `${DETECTION_ENGINE_RULES_URL}/_bulk_delete` - ) + .post(`${DETECTION_ENGINE_RULES_URL}/_bulk_delete`) .send(ids) .set('kbn-xsrf', 'true'); @@ -899,8 +890,10 @@ export const countDownTest = async ( }; /** - * Helper to cut down on the noise in some of the tests. This checks for - * an expected 200 still and does not try to any retries. + * Helper to cut down on the noise in some of the tests. If this detects + * a conflict it will try to manually remove the rule before re-adding the rule one time and log + * and error about the race condition. + * rule a second attempt. It only re-tries adding the rule if it encounters a conflict once. * @param supertest The supertest deps * @param rule The rule to create */ @@ -908,17 +901,69 @@ export const createRule = async ( supertest: SuperTest.SuperTest, rule: CreateRulesSchema ): Promise => { - const { body } = await supertest + const response = await supertest .post(DETECTION_ENGINE_RULES_URL) .set('kbn-xsrf', 'true') - .send(rule) - .expect(200); - return body; + .send(rule); + if (response.status === 409) { + if (rule.rule_id != null) { + // eslint-disable-next-line no-console + console.log( + `When creating a rule found an unexpected conflict (409), will attempt a cleanup and one time re-try. This usually indicates a bad cleanup or race condition within the tests: ${JSON.stringify( + response.body + )}` + ); + await deleteRule(supertest, rule.rule_id); + const secondResponseTry = await supertest + .post(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .send(rule); + if (secondResponseTry.status !== 200) { + throw new Error( + `Unexpected non 200 ok when attempting to create a rule (second try): ${JSON.stringify( + response.body + )}` + ); + } else { + return secondResponseTry.body; + } + } else { + throw new Error('When creating a rule found an unexpected conflict (404)'); + } + } else if (response.status !== 200) { + throw new Error( + `Unexpected non 200 ok when attempting to create a rule: ${JSON.stringify(response.status)}` + ); + } else { + return response.body; + } }; /** - * Helper to cut down on the noise in some of the tests. This checks for - * an expected 200 still and does not try to any retries. + * Helper to cut down on the noise in some of the tests. Does a delete of a rule. + * It does not check for a 200 "ok" on this. + * @param supertest The supertest deps + * @param id The rule id to delete + */ +export const deleteRule = async ( + supertest: SuperTest.SuperTest, + ruleId: string +): Promise => { + const response = await supertest + .delete(`${DETECTION_ENGINE_RULES_URL}?rule_id=${ruleId}`) + .set('kbn-xsrf', 'true'); + if (response.status !== 200) { + // eslint-disable-next-line no-console + console.log( + 'Did not get an expected 200 "ok" when deleting the rule. CI issues could happen. Suspect this line if you are seeing CI issues.' + ); + } + + return response.body; +}; + +/** + * Helper to cut down on the noise in some of the tests. * @param supertest The supertest deps * @param rule The rule to create */ @@ -1017,12 +1062,68 @@ export const createExceptionList = async ( supertest: SuperTest.SuperTest, exceptionList: CreateExceptionListSchema ): Promise => { - const { body } = await supertest + const response = await supertest .post(EXCEPTION_LIST_URL) .set('kbn-xsrf', 'true') - .send(exceptionList) - .expect(200); - return body; + .send(exceptionList); + + if (response.status === 409) { + if (exceptionList.list_id != null) { + // eslint-disable-next-line no-console + console.log( + `When creating an exception list found an unexpected conflict (409), will attempt a cleanup and one time re-try. This usually indicates a bad cleanup or race condition within the tests: ${JSON.stringify( + response.body + )}` + ); + await deleteExceptionList(supertest, exceptionList.list_id); + const secondResponseTry = await supertest + .post(EXCEPTION_LIST_URL) + .set('kbn-xsrf', 'true') + .send(exceptionList); + if (secondResponseTry.status !== 200) { + throw new Error( + `Unexpected non 200 ok when attempting to create an exception list (second try): ${JSON.stringify( + response.body + )}` + ); + } else { + return secondResponseTry.body; + } + } else { + throw new Error('When creating an exception list found an unexpected conflict (404)'); + } + } else if (response.status !== 200) { + throw new Error( + `Unexpected non 200 ok when attempting to create an exception list: ${JSON.stringify( + response.status + )}` + ); + } else { + return response.body; + } +}; + +/** + * Helper to cut down on the noise in some of the tests. Does a delete of a rule. + * It does not check for a 200 "ok" on this. + * @param supertest The supertest deps + * @param id The rule id to delete + */ +export const deleteExceptionList = async ( + supertest: SuperTest.SuperTest, + listId: string +): Promise => { + const response = await supertest + .delete(`${EXCEPTION_LIST_URL}?list_id=${listId}`) + .set('kbn-xsrf', 'true'); + if (response.status !== 200) { + // eslint-disable-next-line no-console + console.log( + 'Did not get an expected 200 "ok" when deleting an exception list. CI issues could happen. Suspect this line if you are seeing CI issues.' + ); + } + + return response.body; }; /** diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/create_exception_list_items.ts b/x-pack/test/lists_api_integration/security_and_spaces/tests/create_exception_list_items.ts index 4541a758a02f1..cb858a4791b71 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/create_exception_list_items.ts +++ b/x-pack/test/lists_api_integration/security_and_spaces/tests/create_exception_list_items.ts @@ -27,7 +27,6 @@ import { deleteAllExceptions } from '../../utils'; // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); - const es = getService('es'); describe('create_exception_list_items', () => { describe('validation errors', () => { @@ -47,7 +46,7 @@ export default ({ getService }: FtrProviderContext) => { describe('creating exception list items', () => { afterEach(async () => { - await deleteAllExceptions(es); + await deleteAllExceptions(supertest); }); it('should create a simple exception list item with a list item id', async () => { diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/create_exception_lists.ts b/x-pack/test/lists_api_integration/security_and_spaces/tests/create_exception_lists.ts index 7e34c80806cc4..e7f5d96bfb76a 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/create_exception_lists.ts +++ b/x-pack/test/lists_api_integration/security_and_spaces/tests/create_exception_lists.ts @@ -21,12 +21,11 @@ import { deleteAllExceptions, removeExceptionListServerGeneratedProperties } fro // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); - const es = getService('es'); describe('create_exception_lists', () => { describe('creating exception lists', () => { afterEach(async () => { - await deleteAllExceptions(es); + await deleteAllExceptions(supertest); }); it('should create a simple exception list', async () => { diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/delete_exception_list_items.ts b/x-pack/test/lists_api_integration/security_and_spaces/tests/delete_exception_list_items.ts index 229d5737a99bb..acf968c8b78af 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/delete_exception_list_items.ts +++ b/x-pack/test/lists_api_integration/security_and_spaces/tests/delete_exception_list_items.ts @@ -22,12 +22,11 @@ import { deleteAllExceptions, removeExceptionListItemServerGeneratedProperties } // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); - const es = getService('es'); describe('delete_exception_list_items', () => { describe('delete exception list items', () => { afterEach(async () => { - await deleteAllExceptions(es); + await deleteAllExceptions(supertest); }); it('should delete a single exception list item by its item_id', async () => { diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/delete_exception_lists.ts b/x-pack/test/lists_api_integration/security_and_spaces/tests/delete_exception_lists.ts index d83c4bdc2f1a9..0f8ca96e5383a 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/delete_exception_lists.ts +++ b/x-pack/test/lists_api_integration/security_and_spaces/tests/delete_exception_lists.ts @@ -21,12 +21,11 @@ import { deleteAllExceptions, removeExceptionListServerGeneratedProperties } fro // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); - const es = getService('es'); describe('delete_exception_lists', () => { describe('delete exception lists', () => { afterEach(async () => { - await deleteAllExceptions(es); + await deleteAllExceptions(supertest); }); it('should delete a single exception list by its list_id', async () => { diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/delete_lists.ts b/x-pack/test/lists_api_integration/security_and_spaces/tests/delete_lists.ts index 5d1abf6f74f7e..3c01d93380736 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/delete_lists.ts +++ b/x-pack/test/lists_api_integration/security_and_spaces/tests/delete_lists.ts @@ -33,7 +33,6 @@ import { DETECTION_TYPE, LIST_ID } from '../../../../plugins/lists/common/consta // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); - const es = getService('es'); describe('delete_lists', () => { describe('deleting lists', () => { @@ -117,7 +116,7 @@ export default ({ getService }: FtrProviderContext) => { describe('deleting lists referenced in exceptions', () => { afterEach(async () => { - await deleteAllExceptions(es); + await deleteAllExceptions(supertest); }); it('should return an error when deleting a list referenced within an exception list item', async () => { diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/export_exception_list.ts b/x-pack/test/lists_api_integration/security_and_spaces/tests/export_exception_list.ts index c21026d5df3d2..c61f4a2b1d02f 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/export_exception_list.ts +++ b/x-pack/test/lists_api_integration/security_and_spaces/tests/export_exception_list.ts @@ -22,12 +22,11 @@ import { getCreateExceptionListItemMinimalSchemaMock } from '../../../../plugins // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext): void => { const supertest = getService('supertest'); - const es = getService('es'); describe('export_exception_list_route', () => { describe('exporting exception lists', () => { afterEach(async () => { - await deleteAllExceptions(es); + await deleteAllExceptions(supertest); }); it('should set the response content types to be expected', async () => { diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/find_exception_list_items.ts b/x-pack/test/lists_api_integration/security_and_spaces/tests/find_exception_list_items.ts index 7b23ab7d6f866..bdacd674d7519 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/find_exception_list_items.ts +++ b/x-pack/test/lists_api_integration/security_and_spaces/tests/find_exception_list_items.ts @@ -18,12 +18,11 @@ import { deleteAllExceptions, removeExceptionListItemServerGeneratedProperties } // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext): void => { const supertest = getService('supertest'); - const es = getService('es'); describe('find_exception_list_items', () => { describe('find exception list items', () => { afterEach(async () => { - await deleteAllExceptions(es); + await deleteAllExceptions(supertest); }); it('should return an empty find body correctly if no exception list items are loaded', async () => { diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/find_exception_lists.ts b/x-pack/test/lists_api_integration/security_and_spaces/tests/find_exception_lists.ts index 9972ed6a89171..158d951b2bd68 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/find_exception_lists.ts +++ b/x-pack/test/lists_api_integration/security_and_spaces/tests/find_exception_lists.ts @@ -17,12 +17,11 @@ import { deleteAllExceptions, removeExceptionListServerGeneratedProperties } fro // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext): void => { const supertest = getService('supertest'); - const es = getService('es'); describe('find_exception_lists', () => { describe('find exception lists', () => { afterEach(async () => { - await deleteAllExceptions(es); + await deleteAllExceptions(supertest); }); it('should return an empty find body correctly if no exception lists are loaded', async () => { diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/read_exception_list_items.ts b/x-pack/test/lists_api_integration/security_and_spaces/tests/read_exception_list_items.ts index 0a7caed8a5e14..9345306e0c3e1 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/read_exception_list_items.ts +++ b/x-pack/test/lists_api_integration/security_and_spaces/tests/read_exception_list_items.ts @@ -22,12 +22,11 @@ import { deleteAllExceptions, removeExceptionListItemServerGeneratedProperties } // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); - const es = getService('es'); describe('read_exception_list_items', () => { describe('reading exception list items', () => { afterEach(async () => { - await deleteAllExceptions(es); + await deleteAllExceptions(supertest); }); it('should be able to read a single exception list items using item_id', async () => { diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/read_exception_lists.ts b/x-pack/test/lists_api_integration/security_and_spaces/tests/read_exception_lists.ts index db53a0ed18a0c..b9f2b89a3e0ee 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/read_exception_lists.ts +++ b/x-pack/test/lists_api_integration/security_and_spaces/tests/read_exception_lists.ts @@ -21,12 +21,11 @@ import { deleteAllExceptions, removeExceptionListServerGeneratedProperties } fro // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); - const es = getService('es'); describe('read_exception_lists', () => { describe('reading exception lists', () => { afterEach(async () => { - await deleteAllExceptions(es); + await deleteAllExceptions(supertest); }); it('should be able to read a single exception list using list_id', async () => { diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/summary_exception_lists.ts b/x-pack/test/lists_api_integration/security_and_spaces/tests/summary_exception_lists.ts index 13ba7da4c7aaa..b71a7dbe768d2 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/summary_exception_lists.ts +++ b/x-pack/test/lists_api_integration/security_and_spaces/tests/summary_exception_lists.ts @@ -21,7 +21,6 @@ interface SummaryResponseType { // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); - const es = getService('es'); describe('summary_exception_lists', () => { describe('summary exception lists', () => { @@ -30,7 +29,7 @@ export default ({ getService }: FtrProviderContext) => { }); afterEach(async () => { await deleteListsIndex(supertest); - await deleteAllExceptions(es); + await deleteAllExceptions(supertest); }); it('should give a validation error if the list_id and the id are not supplied', async () => { diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/update_exception_list_items.ts b/x-pack/test/lists_api_integration/security_and_spaces/tests/update_exception_list_items.ts index b611d5c31de67..fa0466f14db28 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/update_exception_list_items.ts +++ b/x-pack/test/lists_api_integration/security_and_spaces/tests/update_exception_list_items.ts @@ -24,12 +24,11 @@ import { getUpdateMinimalExceptionListItemSchemaMock } from '../../../../plugins // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); - const es = getService('es'); describe('update_exception_list_items', () => { describe('update exception list items', () => { afterEach(async () => { - await deleteAllExceptions(es); + await deleteAllExceptions(supertest); }); it('should update a single exception list item property of name using an id', async () => { diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/update_exception_lists.ts b/x-pack/test/lists_api_integration/security_and_spaces/tests/update_exception_lists.ts index 75064860da1c2..2eeaca7f0521b 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/update_exception_lists.ts +++ b/x-pack/test/lists_api_integration/security_and_spaces/tests/update_exception_lists.ts @@ -23,12 +23,11 @@ import { getUpdateMinimalExceptionListSchemaMock } from '../../../../plugins/lis // eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); - const es = getService('es'); describe('update_exception_lists', () => { describe('update exception lists', () => { afterEach(async () => { - await deleteAllExceptions(es); + await deleteAllExceptions(supertest); }); it('should update a single exception list property of name using an id', async () => { diff --git a/x-pack/test/lists_api_integration/utils.ts b/x-pack/test/lists_api_integration/utils.ts index b3816ad7563b8..c8c1acb9f0e87 100644 --- a/x-pack/test/lists_api_integration/utils.ts +++ b/x-pack/test/lists_api_integration/utils.ts @@ -6,7 +6,6 @@ */ import type SuperTest from 'supertest'; -import type { KibanaClient } from '@elastic/elasticsearch/api/kibana'; import type { Type, @@ -14,10 +13,15 @@ import type { ListItemSchema, ExceptionListSchema, ExceptionListItemSchema, + ExceptionList, } from '@kbn/securitysolution-io-ts-list-types'; -import { LIST_INDEX, LIST_ITEM_URL } from '@kbn/securitysolution-list-constants'; +import { + EXCEPTION_LIST_URL, + LIST_INDEX, + LIST_ITEM_URL, +} from '@kbn/securitysolution-list-constants'; import { getImportListItemAsBuffer } from '../../plugins/lists/common/schemas/request/import_list_item_schema.mock'; -import { countDownES, countDownTest } from '../detection_engine_api_integration/utils'; +import { countDownTest } from '../detection_engine_api_integration/utils'; /** * Creates the lists and lists items index for use inside of beforeEach blocks of tests @@ -160,20 +164,34 @@ export const binaryToString = (res: any, callback: any): void => { }; /** - * Remove all exceptions from the .kibana index - * This will retry 20 times before giving up and hopefully still not interfere with other tests - * @param es The ElasticSearch handle + * Remove all exceptions + * This will retry 50 times before giving up and hopefully still not interfere with other tests + * @param supertest The supertest handle */ -export const deleteAllExceptions = async (es: KibanaClient): Promise => { - return countDownES(async () => { - return es.deleteByQuery({ - index: '.kibana', - q: 'type:exception-list or type:exception-list-agnostic', - wait_for_completion: true, - refresh: true, - body: {}, - }); - }, 'deleteAllExceptions'); +export const deleteAllExceptions = async ( + supertest: SuperTest.SuperTest +): Promise => { + await countDownTest( + async () => { + const { body } = await supertest + .get(`${EXCEPTION_LIST_URL}/_find?per_page=9999`) + .set('kbn-xsrf', 'true') + .send(); + + const ids: string[] = body.data.map((exception: ExceptionList) => exception.id); + for await (const id of ids) { + await supertest.delete(`${EXCEPTION_LIST_URL}?id=${id}`).set('kbn-xsrf', 'true').send(); + } + const { body: finalCheck } = await supertest + .get(`${EXCEPTION_LIST_URL}/_find`) + .set('kbn-xsrf', 'true') + .send(); + return finalCheck.data.length === 0; + }, + 'deleteAllExceptions', + 50, + 1000 + ); }; /** From 491ccc0d55a8a5daace449242e27cd08a93b1995 Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Wed, 20 Oct 2021 17:53:37 -0500 Subject: [PATCH 168/204] [yarn.lock] Resolve micromatch to 4.0.4 (#115132) --- packages/kbn-pm/dist/index.js | 104698 ++++++++++++++++--------------- yarn.lock | 12 +- 2 files changed, 54456 insertions(+), 50254 deletions(-) diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index 721072d9e899b..f70c5145516f8 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -94,21 +94,21 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _cli__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "run", function() { return _cli__WEBPACK_IMPORTED_MODULE_0__["run"]; }); -/* harmony import */ var _production__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(554); +/* harmony import */ var _production__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(560); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildBazelProductionProjects", function() { return _production__WEBPACK_IMPORTED_MODULE_1__["buildBazelProductionProjects"]; }); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildNonBazelProductionProjects", function() { return _production__WEBPACK_IMPORTED_MODULE_1__["buildNonBazelProductionProjects"]; }); -/* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(340); +/* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(346); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "getProjects", function() { return _utils_projects__WEBPACK_IMPORTED_MODULE_2__["getProjects"]; }); -/* harmony import */ var _utils_project__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(342); +/* harmony import */ var _utils_project__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(348); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Project", function() { return _utils_project__WEBPACK_IMPORTED_MODULE_3__["Project"]; }); -/* harmony import */ var _utils_package_json__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(343); +/* harmony import */ var _utils_package_json__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(349); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "transformDependencies", function() { return _utils_package_json__WEBPACK_IMPORTED_MODULE_4__["transformDependencies"]; }); -/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(553); +/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(559); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "getProjectPaths", function() { return _config__WEBPACK_IMPORTED_MODULE_5__["getProjectPaths"]; }); /* @@ -141,7 +141,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _kbn_dev_utils_tooling_log__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(5); /* harmony import */ var _kbn_dev_utils_tooling_log__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_kbn_dev_utils_tooling_log__WEBPACK_IMPORTED_MODULE_3__); /* harmony import */ var _commands__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(129); -/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(548); +/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(554); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(220); /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one @@ -8829,11 +8829,11 @@ exports.ToolingLogCollectingWriter = ToolingLogCollectingWriter; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "commands", function() { return commands; }); /* harmony import */ var _bootstrap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(130); -/* harmony import */ var _build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(519); -/* harmony import */ var _clean__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(520); -/* harmony import */ var _reset__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(544); -/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(545); -/* harmony import */ var _watch__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(547); +/* harmony import */ var _build__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(525); +/* harmony import */ var _clean__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(526); +/* harmony import */ var _reset__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(550); +/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(551); +/* harmony import */ var _watch__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(553); /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License @@ -8870,10 +8870,10 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(220); /* harmony import */ var _utils_child_process__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(221); /* harmony import */ var _utils_link_project_executables__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(230); -/* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(340); -/* harmony import */ var _utils_yarn_lock__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(408); -/* harmony import */ var _utils_validate_dependencies__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(411); -/* harmony import */ var _utils_bazel__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(413); +/* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(346); +/* harmony import */ var _utils_yarn_lock__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(414); +/* harmony import */ var _utils_validate_dependencies__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(417); +/* harmony import */ var _utils_bazel__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(419); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } @@ -16566,7 +16566,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(del__WEBPACK_IMPORTED_MODULE_1__); /* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(132); /* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(fs__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var ncp__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(339); +/* harmony import */ var ncp__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(345); /* harmony import */ var ncp__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(ncp__WEBPACK_IMPORTED_MODULE_3__); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(4); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_4__); @@ -17980,12 +17980,12 @@ const {promisify} = __webpack_require__(113); const path = __webpack_require__(4); const globby = __webpack_require__(241); const isGlob = __webpack_require__(266); -const slash = __webpack_require__(330); +const slash = __webpack_require__(336); const gracefulFs = __webpack_require__(233); -const isPathCwd = __webpack_require__(332); -const isPathInside = __webpack_require__(333); -const rimraf = __webpack_require__(334); -const pMap = __webpack_require__(335); +const isPathCwd = __webpack_require__(338); +const isPathInside = __webpack_require__(339); +const rimraf = __webpack_require__(340); +const pMap = __webpack_require__(341); const rimrafP = promisify(rimraf); @@ -18109,9 +18109,9 @@ const arrayUnion = __webpack_require__(242); const merge2 = __webpack_require__(243); const glob = __webpack_require__(244); const fastGlob = __webpack_require__(257); -const dirGlob = __webpack_require__(326); -const gitignore = __webpack_require__(328); -const {FilterStream, UniqueStream} = __webpack_require__(331); +const dirGlob = __webpack_require__(332); +const gitignore = __webpack_require__(334); +const {FilterStream, UniqueStream} = __webpack_require__(337); const DEFAULT_FILTER = () => false; @@ -21724,10 +21724,10 @@ function slice (args) { "use strict"; const taskManager = __webpack_require__(258); -const async_1 = __webpack_require__(287); -const stream_1 = __webpack_require__(322); -const sync_1 = __webpack_require__(323); -const settings_1 = __webpack_require__(325); +const async_1 = __webpack_require__(293); +const stream_1 = __webpack_require__(328); +const sync_1 = __webpack_require__(329); +const settings_1 = __webpack_require__(331); const utils = __webpack_require__(259); async function FastGlob(source, options) { assertPatternsInput(source); @@ -21881,9 +21881,9 @@ const path = __webpack_require__(263); exports.path = path; const pattern = __webpack_require__(264); exports.pattern = pattern; -const stream = __webpack_require__(285); +const stream = __webpack_require__(291); exports.stream = stream; -const string = __webpack_require__(286); +const string = __webpack_require__(292); exports.string = string; @@ -22007,7 +22007,7 @@ exports.matchAny = exports.convertPatternsToRe = exports.makeRe = exports.getPat const path = __webpack_require__(4); const globParent = __webpack_require__(265); const micromatch = __webpack_require__(268); -const picomatch = __webpack_require__(279); +const picomatch = __webpack_require__(285); const GLOBSTAR = '**'; const ESCAPE_SYMBOL = '\\'; const COMMON_GLOB_SYMBOLS_RE = /[*?]|^!/; @@ -22275,7 +22275,7 @@ const util = __webpack_require__(113); const braces = __webpack_require__(269); const picomatch = __webpack_require__(279); const utils = __webpack_require__(282); -const isEmptyString = val => typeof val === 'string' && (val === '' || val === './'); +const isEmptyString = val => val === '' || val === './'; /** * Returns an array of strings that match one or more glob patterns. @@ -22287,9 +22287,9 @@ const isEmptyString = val => typeof val === 'string' && (val === '' || val === ' * console.log(mm(['a.js', 'a.txt'], ['*.js'])); * //=> [ 'a.js' ] * ``` - * @param {String|Array} list List of strings to match. - * @param {String|Array} patterns One or more glob patterns to use for matching. - * @param {Object} options See available [options](#options) + * @param {String|Array} `list` List of strings to match. + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `options` See available [options](#options) * @return {Array} Returns an array of matches * @summary false * @api public @@ -22384,9 +22384,9 @@ micromatch.matcher = (pattern, options) => picomatch(pattern, options); * console.log(mm.isMatch('a.a', ['b.*', '*.a'])); //=> true * console.log(mm.isMatch('a.a', 'b.*')); //=> false * ``` - * @param {String} str The string to test. - * @param {String|Array} patterns One or more glob patterns to use for matching. - * @param {Object} [options] See available [options](#options). + * @param {String} `str` The string to test. + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `[options]` See available [options](#options). * @return {Boolean} Returns true if any patterns match `str` * @api public */ @@ -22452,7 +22452,7 @@ micromatch.not = (list, patterns, options = {}) => { * @param {String} `str` The string to match. * @param {String|Array} `patterns` Glob pattern to use for matching. * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Boolean} Returns true if the patter matches any part of `str`. + * @return {Boolean} Returns true if any of the patterns matches any part of `str`. * @api public */ @@ -22523,7 +22523,7 @@ micromatch.matchKeys = (obj, patterns, options) => { * @param {String|Array} `list` The string or array of strings to test. Returns as soon as the first match is found. * @param {String|Array} `patterns` One or more glob patterns to use for matching. * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Boolean} Returns true if any patterns match `str` + * @return {Boolean} Returns true if any `patterns` matches any of the strings in `list` * @api public */ @@ -22559,7 +22559,7 @@ micromatch.some = (list, patterns, options) => { * @param {String|Array} `list` The string or array of strings to test. * @param {String|Array} `patterns` One or more glob patterns to use for matching. * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Boolean} Returns true if any patterns match `str` + * @return {Boolean} Returns true if all `patterns` matches all of the strings in `list` * @api public */ @@ -22625,7 +22625,7 @@ micromatch.all = (str, patterns, options) => { * @param {String} `glob` Glob pattern to use for matching. * @param {String} `input` String to match * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Boolean} Returns an array of captures if the input matches the glob pattern, otherwise `null`. + * @return {Array|null} Returns an array of captures if the input matches the glob pattern, otherwise `null`. * @api public */ @@ -24485,68 +24485,71 @@ picomatch.parse = (pattern, options) => { picomatch.scan = (input, options) => scan(input, options); /** - * Create a regular expression from a parsed glob pattern. - * - * ```js - * const picomatch = require('picomatch'); - * const state = picomatch.parse('*.js'); - * // picomatch.compileRe(state[, options]); + * Compile a regular expression from the `state` object returned by the + * [parse()](#parse) method. * - * console.log(picomatch.compileRe(state)); - * //=> /^(?:(?!\.)(?=.)[^/]*?\.js)$/ - * ``` - * @param {String} `state` The object returned from the `.parse` method. + * @param {Object} `state` * @param {Object} `options` - * @return {RegExp} Returns a regex created from the given pattern. + * @param {Boolean} `returnOutput` Intended for implementors, this argument allows you to return the raw output from the parser. + * @param {Boolean} `returnState` Adds the state to a `state` property on the returned regex. Useful for implementors and debugging. + * @return {RegExp} * @api public */ -picomatch.compileRe = (parsed, options, returnOutput = false, returnState = false) => { +picomatch.compileRe = (state, options, returnOutput = false, returnState = false) => { if (returnOutput === true) { - return parsed.output; + return state.output; } const opts = options || {}; const prepend = opts.contains ? '' : '^'; const append = opts.contains ? '' : '$'; - let source = `${prepend}(?:${parsed.output})${append}`; - if (parsed && parsed.negated === true) { + let source = `${prepend}(?:${state.output})${append}`; + if (state && state.negated === true) { source = `^(?!${source}).*$`; } const regex = picomatch.toRegex(source, options); if (returnState === true) { - regex.state = parsed; + regex.state = state; } return regex; }; -picomatch.makeRe = (input, options, returnOutput = false, returnState = false) => { +/** + * Create a regular expression from a parsed glob pattern. + * + * ```js + * const picomatch = require('picomatch'); + * const state = picomatch.parse('*.js'); + * // picomatch.compileRe(state[, options]); + * + * console.log(picomatch.compileRe(state)); + * //=> /^(?:(?!\.)(?=.)[^/]*?\.js)$/ + * ``` + * @param {String} `state` The object returned from the `.parse` method. + * @param {Object} `options` + * @param {Boolean} `returnOutput` Implementors may use this argument to return the compiled output, instead of a regular expression. This is not exposed on the options to prevent end-users from mutating the result. + * @param {Boolean} `returnState` Implementors may use this argument to return the state from the parsed glob with the returned regular expression. + * @return {RegExp} Returns a regex created from the given pattern. + * @api public + */ + +picomatch.makeRe = (input, options = {}, returnOutput = false, returnState = false) => { if (!input || typeof input !== 'string') { throw new TypeError('Expected a non-empty string'); } - const opts = options || {}; let parsed = { negated: false, fastpaths: true }; - let prefix = ''; - let output; - - if (input.startsWith('./')) { - input = input.slice(2); - prefix = parsed.prefix = './'; - } - if (opts.fastpaths !== false && (input[0] === '.' || input[0] === '*')) { - output = parse.fastpaths(input, options); + if (options.fastpaths !== false && (input[0] === '.' || input[0] === '*')) { + parsed.output = parse.fastpaths(input, options); } - if (output === undefined) { + if (!parsed.output) { parsed = parse(input, options); - parsed.prefix = prefix + (parsed.prefix || ''); - } else { - parsed.output = output; } return picomatch.compileRe(parsed, options, returnOutput, returnState); @@ -24632,7 +24635,8 @@ const depth = token => { /** * Quickly scans a glob pattern and returns an object with a handful of * useful properties, like `isGlob`, `path` (the leading non-glob, if it exists), - * `glob` (the actual pattern), and `negated` (true if the path starts with `!`). + * `glob` (the actual pattern), `negated` (true if the path starts with `!` but not + * with `!(`) and `negatedExtglob` (true if the path starts with `!(`). * * ```js * const pm = require('picomatch'); @@ -24666,6 +24670,7 @@ const scan = (input, options) => { let braceEscaped = false; let backslashes = false; let negated = false; + let negatedExtglob = false; let finished = false; let braces = 0; let prev; @@ -24777,6 +24782,9 @@ const scan = (input, options) => { isGlob = token.isGlob = true; isExtglob = token.isExtglob = true; finished = true; + if (code === CHAR_EXCLAMATION_MARK && index === start) { + negatedExtglob = true; + } if (scanToEnd === true) { while (eos() !== true && (code = advance())) { @@ -24831,13 +24839,15 @@ const scan = (input, options) => { isBracket = token.isBracket = true; isGlob = token.isGlob = true; finished = true; - - if (scanToEnd === true) { - continue; - } break; } } + + if (scanToEnd === true) { + continue; + } + + break; } if (opts.nonegate !== true && code === CHAR_EXCLAMATION_MARK && index === start) { @@ -24928,7 +24938,8 @@ const scan = (input, options) => { isGlob, isExtglob, isGlobstar, - negated + negated, + negatedExtglob }; if (opts.tokens === true) { @@ -25339,7 +25350,7 @@ const parse = (input, options) => { START_ANCHOR } = PLATFORM_CHARS; - const globstar = (opts) => { + const globstar = opts => { return `(${capture}(?:(?!${START_ANCHOR}${opts.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`; }; @@ -25389,12 +25400,13 @@ const parse = (input, options) => { const eos = () => state.index === len - 1; const peek = state.peek = (n = 1) => input[state.index + n]; - const advance = state.advance = () => input[++state.index]; + const advance = state.advance = () => input[++state.index] || ''; const remaining = () => input.slice(state.index + 1); const consume = (value = '', num = 0) => { state.consumed += value; state.index += num; }; + const append = token => { state.output += token.output != null ? token.output : token.value; consume(token.value); @@ -25450,7 +25462,7 @@ const parse = (input, options) => { } } - if (extglobs.length && tok.type !== 'paren' && !EXTGLOB_CHARS[tok.value]) { + if (extglobs.length && tok.type !== 'paren') { extglobs[extglobs.length - 1].inner += tok.value; } @@ -25482,6 +25494,7 @@ const parse = (input, options) => { const extglobClose = token => { let output = token.close + (opts.capture ? ')' : ''); + let rest; if (token.type === 'negate') { let extglobStar = star; @@ -25494,7 +25507,11 @@ const parse = (input, options) => { output = token.close = `)$))${extglobStar}`; } - if (token.prev.type === 'bos' && eos()) { + if (token.inner.includes('*') && (rest = remaining()) && /^\.[^\\/.]+$/.test(rest)) { + output = token.close = `)${rest})${extglobStar})`; + } + + if (token.prev.type === 'bos') { state.negatedExtglob = true; } } @@ -25603,9 +25620,9 @@ const parse = (input, options) => { } if (opts.unescape === true) { - value = advance() || ''; + value = advance(); } else { - value += advance() || ''; + value += advance(); } if (state.brackets === 0) { @@ -26269,7 +26286,7 @@ parse.fastpaths = (input, options) => { star = `(${star})`; } - const globstar = (opts) => { + const globstar = opts => { if (opts.noglobstar === true) return star; return `(${capture}(?:(?!${START_ANCHOR}${opts.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`; }; @@ -26330,23 +26347,9 @@ module.exports = parse; /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.merge = void 0; -const merge2 = __webpack_require__(243); -function merge(streams) { - const mergedStream = merge2(streams); - streams.forEach((stream) => { - stream.once('error', (error) => mergedStream.emit('error', error)); - }); - mergedStream.once('close', () => propagateCloseEventToSources(streams)); - mergedStream.once('end', () => propagateCloseEventToSources(streams)); - return mergedStream; -} -exports.merge = merge; -function propagateCloseEventToSources(streams) { - streams.forEach((stream) => stream.emit('close')); -} + + +module.exports = __webpack_require__(286); /***/ }), @@ -26354,10036 +26357,12138 @@ function propagateCloseEventToSources(streams) { /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.isEmpty = exports.isString = void 0; -function isString(input) { - return typeof input === 'string'; -} -exports.isString = isString; -function isEmpty(input) { - return input === ''; -} -exports.isEmpty = isEmpty; -/***/ }), -/* 287 */ -/***/ (function(module, exports, __webpack_require__) { +const path = __webpack_require__(4); +const scan = __webpack_require__(287); +const parse = __webpack_require__(290); +const utils = __webpack_require__(288); +const constants = __webpack_require__(289); +const isObject = val => val && typeof val === 'object' && !Array.isArray(val); -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const stream_1 = __webpack_require__(288); -const provider_1 = __webpack_require__(315); -class ProviderAsync extends provider_1.default { - constructor() { - super(...arguments); - this._reader = new stream_1.default(this._settings); - } - read(task) { - const root = this._getRootDirectory(task); - const options = this._getReaderOptions(task); - const entries = []; - return new Promise((resolve, reject) => { - const stream = this.api(root, task, options); - stream.once('error', reject); - stream.on('data', (entry) => entries.push(options.transform(entry))); - stream.once('end', () => resolve(entries)); - }); - } - api(root, task, options) { - if (task.dynamic) { - return this._reader.dynamic(root, options); - } - return this._reader.static(task.patterns, options); - } -} -exports.default = ProviderAsync; +/** + * Creates a matcher function from one or more glob patterns. The + * returned function takes a string to match as its first argument, + * and returns true if the string is a match. The returned matcher + * function also takes a boolean as the second argument that, when true, + * returns an object with additional information. + * + * ```js + * const picomatch = require('picomatch'); + * // picomatch(glob[, options]); + * + * const isMatch = picomatch('*.!(*a)'); + * console.log(isMatch('a.a')); //=> false + * console.log(isMatch('a.b')); //=> true + * ``` + * @name picomatch + * @param {String|Array} `globs` One or more glob patterns. + * @param {Object=} `options` + * @return {Function=} Returns a matcher function. + * @api public + */ +const picomatch = (glob, options, returnState = false) => { + if (Array.isArray(glob)) { + const fns = glob.map(input => picomatch(input, options, returnState)); + const arrayMatcher = str => { + for (const isMatch of fns) { + const state = isMatch(str); + if (state) return state; + } + return false; + }; + return arrayMatcher; + } -/***/ }), -/* 288 */ -/***/ (function(module, exports, __webpack_require__) { + const isState = isObject(glob) && glob.tokens && glob.input; -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const stream_1 = __webpack_require__(173); -const fsStat = __webpack_require__(289); -const fsWalk = __webpack_require__(294); -const reader_1 = __webpack_require__(314); -class ReaderStream extends reader_1.default { - constructor() { - super(...arguments); - this._walkStream = fsWalk.walkStream; - this._stat = fsStat.stat; - } - dynamic(root, options) { - return this._walkStream(root, options); - } - static(patterns, options) { - const filepaths = patterns.map(this._getFullEntryPath, this); - const stream = new stream_1.PassThrough({ objectMode: true }); - stream._write = (index, _enc, done) => { - return this._getEntry(filepaths[index], patterns[index], options) - .then((entry) => { - if (entry !== null && options.entryFilter(entry)) { - stream.push(entry); - } - if (index === filepaths.length - 1) { - stream.end(); - } - done(); - }) - .catch(done); - }; - for (let i = 0; i < filepaths.length; i++) { - stream.write(i); - } - return stream; - } - _getEntry(filepath, pattern, options) { - return this._getStat(filepath) - .then((stats) => this._makeEntry(stats, pattern)) - .catch((error) => { - if (options.errorFilter(error)) { - return null; - } - throw error; - }); - } - _getStat(filepath) { - return new Promise((resolve, reject) => { - this._stat(filepath, this._fsStatSettings, (error, stats) => { - return error === null ? resolve(stats) : reject(error); - }); - }); - } -} -exports.default = ReaderStream; + if (glob === '' || (typeof glob !== 'string' && !isState)) { + throw new TypeError('Expected pattern to be a non-empty string'); + } + const opts = options || {}; + const posix = utils.isWindows(options); + const regex = isState + ? picomatch.compileRe(glob, options) + : picomatch.makeRe(glob, options, false, true); -/***/ }), -/* 289 */ -/***/ (function(module, exports, __webpack_require__) { + const state = regex.state; + delete regex.state; -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const async = __webpack_require__(290); -const sync = __webpack_require__(291); -const settings_1 = __webpack_require__(292); -exports.Settings = settings_1.default; -function stat(path, optionsOrSettingsOrCallback, callback) { - if (typeof optionsOrSettingsOrCallback === 'function') { - return async.read(path, getSettings(), optionsOrSettingsOrCallback); - } - async.read(path, getSettings(optionsOrSettingsOrCallback), callback); -} -exports.stat = stat; -function statSync(path, optionsOrSettings) { - const settings = getSettings(optionsOrSettings); - return sync.read(path, settings); -} -exports.statSync = statSync; -function getSettings(settingsOrOptions = {}) { - if (settingsOrOptions instanceof settings_1.default) { - return settingsOrOptions; - } - return new settings_1.default(settingsOrOptions); -} + let isIgnored = () => false; + if (opts.ignore) { + const ignoreOpts = { ...options, ignore: null, onMatch: null, onResult: null }; + isIgnored = picomatch(opts.ignore, ignoreOpts, returnState); + } + const matcher = (input, returnObject = false) => { + const { isMatch, match, output } = picomatch.test(input, regex, options, { glob, posix }); + const result = { glob, state, regex, posix, input, output, match, isMatch }; -/***/ }), -/* 290 */ -/***/ (function(module, exports, __webpack_require__) { + if (typeof opts.onResult === 'function') { + opts.onResult(result); + } -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -function read(path, settings, callback) { - settings.fs.lstat(path, (lstatError, lstat) => { - if (lstatError !== null) { - return callFailureCallback(callback, lstatError); - } - if (!lstat.isSymbolicLink() || !settings.followSymbolicLink) { - return callSuccessCallback(callback, lstat); - } - settings.fs.stat(path, (statError, stat) => { - if (statError !== null) { - if (settings.throwErrorOnBrokenSymbolicLink) { - return callFailureCallback(callback, statError); - } - return callSuccessCallback(callback, lstat); - } - if (settings.markSymbolicLink) { - stat.isSymbolicLink = () => true; - } - callSuccessCallback(callback, stat); - }); - }); -} -exports.read = read; -function callFailureCallback(callback, error) { - callback(error); -} -function callSuccessCallback(callback, result) { - callback(null, result); -} + if (isMatch === false) { + result.isMatch = false; + return returnObject ? result : false; + } + if (isIgnored(input)) { + if (typeof opts.onIgnore === 'function') { + opts.onIgnore(result); + } + result.isMatch = false; + return returnObject ? result : false; + } -/***/ }), -/* 291 */ -/***/ (function(module, exports, __webpack_require__) { + if (typeof opts.onMatch === 'function') { + opts.onMatch(result); + } + return returnObject ? result : true; + }; -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -function read(path, settings) { - const lstat = settings.fs.lstatSync(path); - if (!lstat.isSymbolicLink() || !settings.followSymbolicLink) { - return lstat; - } - try { - const stat = settings.fs.statSync(path); - if (settings.markSymbolicLink) { - stat.isSymbolicLink = () => true; - } - return stat; - } - catch (error) { - if (!settings.throwErrorOnBrokenSymbolicLink) { - return lstat; - } - throw error; - } -} -exports.read = read; + if (returnState) { + matcher.state = state; + } + return matcher; +}; -/***/ }), -/* 292 */ -/***/ (function(module, exports, __webpack_require__) { +/** + * Test `input` with the given `regex`. This is used by the main + * `picomatch()` function to test the input string. + * + * ```js + * const picomatch = require('picomatch'); + * // picomatch.test(input, regex[, options]); + * + * console.log(picomatch.test('foo/bar', /^(?:([^/]*?)\/([^/]*?))$/)); + * // { isMatch: true, match: [ 'foo/', 'foo', 'bar' ], output: 'foo/bar' } + * ``` + * @param {String} `input` String to test. + * @param {RegExp} `regex` + * @return {Object} Returns an object with matching info. + * @api public + */ -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const fs = __webpack_require__(293); -class Settings { - constructor(_options = {}) { - this._options = _options; - this.followSymbolicLink = this._getValue(this._options.followSymbolicLink, true); - this.fs = fs.createFileSystemAdapter(this._options.fs); - this.markSymbolicLink = this._getValue(this._options.markSymbolicLink, false); - this.throwErrorOnBrokenSymbolicLink = this._getValue(this._options.throwErrorOnBrokenSymbolicLink, true); - } - _getValue(option, value) { - return option === undefined ? value : option; - } -} -exports.default = Settings; +picomatch.test = (input, regex, options, { glob, posix } = {}) => { + if (typeof input !== 'string') { + throw new TypeError('Expected input to be a string'); + } + if (input === '') { + return { isMatch: false, output: '' }; + } -/***/ }), -/* 293 */ -/***/ (function(module, exports, __webpack_require__) { + const opts = options || {}; + const format = opts.format || (posix ? utils.toPosixSlashes : null); + let match = input === glob; + let output = (match && format) ? format(input) : input; -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const fs = __webpack_require__(132); -exports.FILE_SYSTEM_ADAPTER = { - lstat: fs.lstat, - stat: fs.stat, - lstatSync: fs.lstatSync, - statSync: fs.statSync -}; -function createFileSystemAdapter(fsMethods) { - if (fsMethods === undefined) { - return exports.FILE_SYSTEM_ADAPTER; - } - return Object.assign(Object.assign({}, exports.FILE_SYSTEM_ADAPTER), fsMethods); -} -exports.createFileSystemAdapter = createFileSystemAdapter; + if (match === false) { + output = format ? format(input) : input; + match = output === glob; + } + if (match === false || opts.capture === true) { + if (opts.matchBase === true || opts.basename === true) { + match = picomatch.matchBase(input, regex, options, posix); + } else { + match = regex.exec(output); + } + } -/***/ }), -/* 294 */ -/***/ (function(module, exports, __webpack_require__) { + return { isMatch: Boolean(match), match, output }; +}; -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const async_1 = __webpack_require__(295); -const stream_1 = __webpack_require__(310); -const sync_1 = __webpack_require__(311); -const settings_1 = __webpack_require__(313); -exports.Settings = settings_1.default; -function walk(directory, optionsOrSettingsOrCallback, callback) { - if (typeof optionsOrSettingsOrCallback === 'function') { - return new async_1.default(directory, getSettings()).read(optionsOrSettingsOrCallback); - } - new async_1.default(directory, getSettings(optionsOrSettingsOrCallback)).read(callback); -} -exports.walk = walk; -function walkSync(directory, optionsOrSettings) { - const settings = getSettings(optionsOrSettings); - const provider = new sync_1.default(directory, settings); - return provider.read(); -} -exports.walkSync = walkSync; -function walkStream(directory, optionsOrSettings) { - const settings = getSettings(optionsOrSettings); - const provider = new stream_1.default(directory, settings); - return provider.read(); -} -exports.walkStream = walkStream; -function getSettings(settingsOrOptions = {}) { - if (settingsOrOptions instanceof settings_1.default) { - return settingsOrOptions; - } - return new settings_1.default(settingsOrOptions); -} +/** + * Match the basename of a filepath. + * + * ```js + * const picomatch = require('picomatch'); + * // picomatch.matchBase(input, glob[, options]); + * console.log(picomatch.matchBase('foo/bar.js', '*.js'); // true + * ``` + * @param {String} `input` String to test. + * @param {RegExp|String} `glob` Glob pattern or regex created by [.makeRe](#makeRe). + * @return {Boolean} + * @api public + */ +picomatch.matchBase = (input, glob, options, posix = utils.isWindows(options)) => { + const regex = glob instanceof RegExp ? glob : picomatch.makeRe(glob, options); + return regex.test(path.basename(input)); +}; -/***/ }), -/* 295 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const async_1 = __webpack_require__(296); -class AsyncProvider { - constructor(_root, _settings) { - this._root = _root; - this._settings = _settings; - this._reader = new async_1.default(this._root, this._settings); - this._storage = new Set(); - } - read(callback) { - this._reader.onError((error) => { - callFailureCallback(callback, error); - }); - this._reader.onEntry((entry) => { - this._storage.add(entry); - }); - this._reader.onEnd(() => { - callSuccessCallback(callback, [...this._storage]); - }); - this._reader.read(); - } -} -exports.default = AsyncProvider; -function callFailureCallback(callback, error) { - callback(error); -} -function callSuccessCallback(callback, entries) { - callback(null, entries); -} +/** + * Returns true if **any** of the given glob `patterns` match the specified `string`. + * + * ```js + * const picomatch = require('picomatch'); + * // picomatch.isMatch(string, patterns[, options]); + * + * console.log(picomatch.isMatch('a.a', ['b.*', '*.a'])); //=> true + * console.log(picomatch.isMatch('a.a', 'b.*')); //=> false + * ``` + * @param {String|Array} str The string to test. + * @param {String|Array} patterns One or more glob patterns to use for matching. + * @param {Object} [options] See available [options](#options). + * @return {Boolean} Returns true if any patterns match `str` + * @api public + */ +picomatch.isMatch = (str, patterns, options) => picomatch(patterns, options)(str); -/***/ }), -/* 296 */ -/***/ (function(module, exports, __webpack_require__) { +/** + * Parse a glob pattern to create the source string for a regular + * expression. + * + * ```js + * const picomatch = require('picomatch'); + * const result = picomatch.parse(pattern[, options]); + * ``` + * @param {String} `pattern` + * @param {Object} `options` + * @return {Object} Returns an object with useful properties and output to be used as a regex source string. + * @api public + */ -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const events_1 = __webpack_require__(164); -const fsScandir = __webpack_require__(297); -const fastq = __webpack_require__(306); -const common = __webpack_require__(308); -const reader_1 = __webpack_require__(309); -class AsyncReader extends reader_1.default { - constructor(_root, _settings) { - super(_root, _settings); - this._settings = _settings; - this._scandir = fsScandir.scandir; - this._emitter = new events_1.EventEmitter(); - this._queue = fastq(this._worker.bind(this), this._settings.concurrency); - this._isFatalError = false; - this._isDestroyed = false; - this._queue.drain = () => { - if (!this._isFatalError) { - this._emitter.emit('end'); - } - }; - } - read() { - this._isFatalError = false; - this._isDestroyed = false; - setImmediate(() => { - this._pushToQueue(this._root, this._settings.basePath); - }); - return this._emitter; - } - destroy() { - if (this._isDestroyed) { - throw new Error('The reader is already destroyed'); - } - this._isDestroyed = true; - this._queue.killAndDrain(); - } - onEntry(callback) { - this._emitter.on('entry', callback); - } - onError(callback) { - this._emitter.once('error', callback); - } - onEnd(callback) { - this._emitter.once('end', callback); - } - _pushToQueue(directory, base) { - const queueItem = { directory, base }; - this._queue.push(queueItem, (error) => { - if (error !== null) { - this._handleError(error); - } - }); - } - _worker(item, done) { - this._scandir(item.directory, this._settings.fsScandirSettings, (error, entries) => { - if (error !== null) { - return done(error, undefined); - } - for (const entry of entries) { - this._handleEntry(entry, item.base); - } - done(null, undefined); - }); - } - _handleError(error) { - if (!common.isFatalError(this._settings, error)) { - return; - } - this._isFatalError = true; - this._isDestroyed = true; - this._emitter.emit('error', error); - } - _handleEntry(entry, base) { - if (this._isDestroyed || this._isFatalError) { - return; - } - const fullpath = entry.path; - if (base !== undefined) { - entry.path = common.joinPathSegments(base, entry.name, this._settings.pathSegmentSeparator); - } - if (common.isAppliedFilter(this._settings.entryFilter, entry)) { - this._emitEntry(entry); - } - if (entry.dirent.isDirectory() && common.isAppliedFilter(this._settings.deepFilter, entry)) { - this._pushToQueue(fullpath, entry.path); - } - } - _emitEntry(entry) { - this._emitter.emit('entry', entry); - } -} -exports.default = AsyncReader; +picomatch.parse = (pattern, options) => { + if (Array.isArray(pattern)) return pattern.map(p => picomatch.parse(p, options)); + return parse(pattern, { ...options, fastpaths: false }); +}; +/** + * Scan a glob pattern to separate the pattern into segments. + * + * ```js + * const picomatch = require('picomatch'); + * // picomatch.scan(input[, options]); + * + * const result = picomatch.scan('!./foo/*.js'); + * console.log(result); + * { prefix: '!./', + * input: '!./foo/*.js', + * start: 3, + * base: 'foo', + * glob: '*.js', + * isBrace: false, + * isBracket: false, + * isGlob: true, + * isExtglob: false, + * isGlobstar: false, + * negated: true } + * ``` + * @param {String} `input` Glob pattern to scan. + * @param {Object} `options` + * @return {Object} Returns an object with + * @api public + */ -/***/ }), -/* 297 */ -/***/ (function(module, exports, __webpack_require__) { +picomatch.scan = (input, options) => scan(input, options); -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const async = __webpack_require__(298); -const sync = __webpack_require__(303); -const settings_1 = __webpack_require__(304); -exports.Settings = settings_1.default; -function scandir(path, optionsOrSettingsOrCallback, callback) { - if (typeof optionsOrSettingsOrCallback === 'function') { - return async.read(path, getSettings(), optionsOrSettingsOrCallback); - } - async.read(path, getSettings(optionsOrSettingsOrCallback), callback); -} -exports.scandir = scandir; -function scandirSync(path, optionsOrSettings) { - const settings = getSettings(optionsOrSettings); - return sync.read(path, settings); -} -exports.scandirSync = scandirSync; -function getSettings(settingsOrOptions = {}) { - if (settingsOrOptions instanceof settings_1.default) { - return settingsOrOptions; - } - return new settings_1.default(settingsOrOptions); -} +/** + * Create a regular expression from a parsed glob pattern. + * + * ```js + * const picomatch = require('picomatch'); + * const state = picomatch.parse('*.js'); + * // picomatch.compileRe(state[, options]); + * + * console.log(picomatch.compileRe(state)); + * //=> /^(?:(?!\.)(?=.)[^/]*?\.js)$/ + * ``` + * @param {String} `state` The object returned from the `.parse` method. + * @param {Object} `options` + * @return {RegExp} Returns a regex created from the given pattern. + * @api public + */ +picomatch.compileRe = (parsed, options, returnOutput = false, returnState = false) => { + if (returnOutput === true) { + return parsed.output; + } -/***/ }), -/* 298 */ -/***/ (function(module, exports, __webpack_require__) { + const opts = options || {}; + const prepend = opts.contains ? '' : '^'; + const append = opts.contains ? '' : '$'; -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const fsStat = __webpack_require__(289); -const rpl = __webpack_require__(299); -const constants_1 = __webpack_require__(300); -const utils = __webpack_require__(301); -function read(directory, settings, callback) { - if (!settings.stats && constants_1.IS_SUPPORT_READDIR_WITH_FILE_TYPES) { - return readdirWithFileTypes(directory, settings, callback); - } - return readdir(directory, settings, callback); -} -exports.read = read; -function readdirWithFileTypes(directory, settings, callback) { - settings.fs.readdir(directory, { withFileTypes: true }, (readdirError, dirents) => { - if (readdirError !== null) { - return callFailureCallback(callback, readdirError); - } - const entries = dirents.map((dirent) => ({ - dirent, - name: dirent.name, - path: `${directory}${settings.pathSegmentSeparator}${dirent.name}` - })); - if (!settings.followSymbolicLinks) { - return callSuccessCallback(callback, entries); - } - const tasks = entries.map((entry) => makeRplTaskEntry(entry, settings)); - rpl(tasks, (rplError, rplEntries) => { - if (rplError !== null) { - return callFailureCallback(callback, rplError); - } - callSuccessCallback(callback, rplEntries); - }); - }); -} -exports.readdirWithFileTypes = readdirWithFileTypes; -function makeRplTaskEntry(entry, settings) { - return (done) => { - if (!entry.dirent.isSymbolicLink()) { - return done(null, entry); - } - settings.fs.stat(entry.path, (statError, stats) => { - if (statError !== null) { - if (settings.throwErrorOnBrokenSymbolicLink) { - return done(statError); - } - return done(null, entry); - } - entry.dirent = utils.fs.createDirentFromStats(entry.name, stats); - return done(null, entry); - }); - }; -} -function readdir(directory, settings, callback) { - settings.fs.readdir(directory, (readdirError, names) => { - if (readdirError !== null) { - return callFailureCallback(callback, readdirError); - } - const filepaths = names.map((name) => `${directory}${settings.pathSegmentSeparator}${name}`); - const tasks = filepaths.map((filepath) => { - return (done) => fsStat.stat(filepath, settings.fsStatSettings, done); - }); - rpl(tasks, (rplError, results) => { - if (rplError !== null) { - return callFailureCallback(callback, rplError); - } - const entries = []; - names.forEach((name, index) => { - const stats = results[index]; - const entry = { - name, - path: filepaths[index], - dirent: utils.fs.createDirentFromStats(name, stats) - }; - if (settings.stats) { - entry.stats = stats; - } - entries.push(entry); - }); - callSuccessCallback(callback, entries); - }); - }); -} -exports.readdir = readdir; -function callFailureCallback(callback, error) { - callback(error); -} -function callSuccessCallback(callback, result) { - callback(null, result); -} + let source = `${prepend}(?:${parsed.output})${append}`; + if (parsed && parsed.negated === true) { + source = `^(?!${source}).*$`; + } + const regex = picomatch.toRegex(source, options); + if (returnState === true) { + regex.state = parsed; + } -/***/ }), -/* 299 */ -/***/ (function(module, exports) { + return regex; +}; -module.exports = runParallel +picomatch.makeRe = (input, options, returnOutput = false, returnState = false) => { + if (!input || typeof input !== 'string') { + throw new TypeError('Expected a non-empty string'); + } -function runParallel (tasks, cb) { - var results, pending, keys - var isSync = true + const opts = options || {}; + let parsed = { negated: false, fastpaths: true }; + let prefix = ''; + let output; - if (Array.isArray(tasks)) { - results = [] - pending = tasks.length - } else { - keys = Object.keys(tasks) - results = {} - pending = keys.length + if (input.startsWith('./')) { + input = input.slice(2); + prefix = parsed.prefix = './'; } - function done (err) { - function end () { - if (cb) cb(err, results) - cb = null - } - if (isSync) process.nextTick(end) - else end() + if (opts.fastpaths !== false && (input[0] === '.' || input[0] === '*')) { + output = parse.fastpaths(input, options); } - function each (i, err, result) { - results[i] = result - if (--pending === 0 || err) { - done(err) - } + if (output === undefined) { + parsed = parse(input, options); + parsed.prefix = prefix + (parsed.prefix || ''); + } else { + parsed.output = output; } - if (!pending) { - // empty - done(null) - } else if (keys) { - // object - keys.forEach(function (key) { - tasks[key](function (err, result) { each(key, err, result) }) - }) - } else { - // array - tasks.forEach(function (task, i) { - task(function (err, result) { each(i, err, result) }) - }) + return picomatch.compileRe(parsed, options, returnOutput, returnState); +}; + +/** + * Create a regular expression from the given regex source string. + * + * ```js + * const picomatch = require('picomatch'); + * // picomatch.toRegex(source[, options]); + * + * const { output } = picomatch.parse('*.js'); + * console.log(picomatch.toRegex(output)); + * //=> /^(?:(?!\.)(?=.)[^/]*?\.js)$/ + * ``` + * @param {String} `source` Regular expression source string. + * @param {Object} `options` + * @return {RegExp} + * @api public + */ + +picomatch.toRegex = (source, options) => { + try { + const opts = options || {}; + return new RegExp(source, opts.flags || (opts.nocase ? 'i' : '')); + } catch (err) { + if (options && options.debug === true) throw err; + return /$^/; } +}; - isSync = false -} +/** + * Picomatch constants. + * @return {Object} + */ +picomatch.constants = constants; -/***/ }), -/* 300 */ -/***/ (function(module, exports, __webpack_require__) { +/** + * Expose "picomatch" + */ -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const NODE_PROCESS_VERSION_PARTS = process.versions.node.split('.'); -const MAJOR_VERSION = parseInt(NODE_PROCESS_VERSION_PARTS[0], 10); -const MINOR_VERSION = parseInt(NODE_PROCESS_VERSION_PARTS[1], 10); -const SUPPORTED_MAJOR_VERSION = 10; -const SUPPORTED_MINOR_VERSION = 10; -const IS_MATCHED_BY_MAJOR = MAJOR_VERSION > SUPPORTED_MAJOR_VERSION; -const IS_MATCHED_BY_MAJOR_AND_MINOR = MAJOR_VERSION === SUPPORTED_MAJOR_VERSION && MINOR_VERSION >= SUPPORTED_MINOR_VERSION; -/** - * IS `true` for Node.js 10.10 and greater. - */ -exports.IS_SUPPORT_READDIR_WITH_FILE_TYPES = IS_MATCHED_BY_MAJOR || IS_MATCHED_BY_MAJOR_AND_MINOR; +module.exports = picomatch; /***/ }), -/* 301 */ +/* 287 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const fs = __webpack_require__(302); -exports.fs = fs; -/***/ }), -/* 302 */ -/***/ (function(module, exports, __webpack_require__) { +const utils = __webpack_require__(288); +const { + CHAR_ASTERISK, /* * */ + CHAR_AT, /* @ */ + CHAR_BACKWARD_SLASH, /* \ */ + CHAR_COMMA, /* , */ + CHAR_DOT, /* . */ + CHAR_EXCLAMATION_MARK, /* ! */ + CHAR_FORWARD_SLASH, /* / */ + CHAR_LEFT_CURLY_BRACE, /* { */ + CHAR_LEFT_PARENTHESES, /* ( */ + CHAR_LEFT_SQUARE_BRACKET, /* [ */ + CHAR_PLUS, /* + */ + CHAR_QUESTION_MARK, /* ? */ + CHAR_RIGHT_CURLY_BRACE, /* } */ + CHAR_RIGHT_PARENTHESES, /* ) */ + CHAR_RIGHT_SQUARE_BRACKET /* ] */ +} = __webpack_require__(289); -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -class DirentFromStats { - constructor(name, stats) { - this.name = name; - this.isBlockDevice = stats.isBlockDevice.bind(stats); - this.isCharacterDevice = stats.isCharacterDevice.bind(stats); - this.isDirectory = stats.isDirectory.bind(stats); - this.isFIFO = stats.isFIFO.bind(stats); - this.isFile = stats.isFile.bind(stats); - this.isSocket = stats.isSocket.bind(stats); - this.isSymbolicLink = stats.isSymbolicLink.bind(stats); - } -} -function createDirentFromStats(name, stats) { - return new DirentFromStats(name, stats); -} -exports.createDirentFromStats = createDirentFromStats; +const isPathSeparator = code => { + return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH; +}; +const depth = token => { + if (token.isPrefix !== true) { + token.depth = token.isGlobstar ? Infinity : 1; + } +}; -/***/ }), -/* 303 */ -/***/ (function(module, exports, __webpack_require__) { +/** + * Quickly scans a glob pattern and returns an object with a handful of + * useful properties, like `isGlob`, `path` (the leading non-glob, if it exists), + * `glob` (the actual pattern), and `negated` (true if the path starts with `!`). + * + * ```js + * const pm = require('picomatch'); + * console.log(pm.scan('foo/bar/*.js')); + * { isGlob: true, input: 'foo/bar/*.js', base: 'foo/bar', glob: '*.js' } + * ``` + * @param {String} `str` + * @param {Object} `options` + * @return {Object} Returns an object with tokens and regex source string. + * @api public + */ -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const fsStat = __webpack_require__(289); -const constants_1 = __webpack_require__(300); -const utils = __webpack_require__(301); -function read(directory, settings) { - if (!settings.stats && constants_1.IS_SUPPORT_READDIR_WITH_FILE_TYPES) { - return readdirWithFileTypes(directory, settings); - } - return readdir(directory, settings); -} -exports.read = read; -function readdirWithFileTypes(directory, settings) { - const dirents = settings.fs.readdirSync(directory, { withFileTypes: true }); - return dirents.map((dirent) => { - const entry = { - dirent, - name: dirent.name, - path: `${directory}${settings.pathSegmentSeparator}${dirent.name}` - }; - if (entry.dirent.isSymbolicLink() && settings.followSymbolicLinks) { - try { - const stats = settings.fs.statSync(entry.path); - entry.dirent = utils.fs.createDirentFromStats(entry.name, stats); - } - catch (error) { - if (settings.throwErrorOnBrokenSymbolicLink) { - throw error; - } - } - } - return entry; - }); -} -exports.readdirWithFileTypes = readdirWithFileTypes; -function readdir(directory, settings) { - const names = settings.fs.readdirSync(directory); - return names.map((name) => { - const entryPath = `${directory}${settings.pathSegmentSeparator}${name}`; - const stats = fsStat.statSync(entryPath, settings.fsStatSettings); - const entry = { - name, - path: entryPath, - dirent: utils.fs.createDirentFromStats(name, stats) - }; - if (settings.stats) { - entry.stats = stats; - } - return entry; - }); -} -exports.readdir = readdir; +const scan = (input, options) => { + const opts = options || {}; + const length = input.length - 1; + const scanToEnd = opts.parts === true || opts.scanToEnd === true; + const slashes = []; + const tokens = []; + const parts = []; -/***/ }), -/* 304 */ -/***/ (function(module, exports, __webpack_require__) { + let str = input; + let index = -1; + let start = 0; + let lastIndex = 0; + let isBrace = false; + let isBracket = false; + let isGlob = false; + let isExtglob = false; + let isGlobstar = false; + let braceEscaped = false; + let backslashes = false; + let negated = false; + let finished = false; + let braces = 0; + let prev; + let code; + let token = { value: '', depth: 0, isGlob: false }; -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const path = __webpack_require__(4); -const fsStat = __webpack_require__(289); -const fs = __webpack_require__(305); -class Settings { - constructor(_options = {}) { - this._options = _options; - this.followSymbolicLinks = this._getValue(this._options.followSymbolicLinks, false); - this.fs = fs.createFileSystemAdapter(this._options.fs); - this.pathSegmentSeparator = this._getValue(this._options.pathSegmentSeparator, path.sep); - this.stats = this._getValue(this._options.stats, false); - this.throwErrorOnBrokenSymbolicLink = this._getValue(this._options.throwErrorOnBrokenSymbolicLink, true); - this.fsStatSettings = new fsStat.Settings({ - followSymbolicLink: this.followSymbolicLinks, - fs: this.fs, - throwErrorOnBrokenSymbolicLink: this.throwErrorOnBrokenSymbolicLink - }); - } - _getValue(option, value) { - return option === undefined ? value : option; - } -} -exports.default = Settings; + const eos = () => index >= length; + const peek = () => str.charCodeAt(index + 1); + const advance = () => { + prev = code; + return str.charCodeAt(++index); + }; + while (index < length) { + code = advance(); + let next; -/***/ }), -/* 305 */ -/***/ (function(module, exports, __webpack_require__) { + if (code === CHAR_BACKWARD_SLASH) { + backslashes = token.backslashes = true; + code = advance(); -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const fs = __webpack_require__(132); -exports.FILE_SYSTEM_ADAPTER = { - lstat: fs.lstat, - stat: fs.stat, - lstatSync: fs.lstatSync, - statSync: fs.statSync, - readdir: fs.readdir, - readdirSync: fs.readdirSync -}; -function createFileSystemAdapter(fsMethods) { - if (fsMethods === undefined) { - return exports.FILE_SYSTEM_ADAPTER; - } - return Object.assign(Object.assign({}, exports.FILE_SYSTEM_ADAPTER), fsMethods); -} -exports.createFileSystemAdapter = createFileSystemAdapter; + if (code === CHAR_LEFT_CURLY_BRACE) { + braceEscaped = true; + } + continue; + } + if (braceEscaped === true || code === CHAR_LEFT_CURLY_BRACE) { + braces++; -/***/ }), -/* 306 */ -/***/ (function(module, exports, __webpack_require__) { + while (eos() !== true && (code = advance())) { + if (code === CHAR_BACKWARD_SLASH) { + backslashes = token.backslashes = true; + advance(); + continue; + } -"use strict"; + if (code === CHAR_LEFT_CURLY_BRACE) { + braces++; + continue; + } + if (braceEscaped !== true && code === CHAR_DOT && (code = advance()) === CHAR_DOT) { + isBrace = token.isBrace = true; + isGlob = token.isGlob = true; + finished = true; -var reusify = __webpack_require__(307) + if (scanToEnd === true) { + continue; + } -function fastqueue (context, worker, concurrency) { - if (typeof context === 'function') { - concurrency = worker - worker = context - context = null - } + break; + } - var cache = reusify(Task) - var queueHead = null - var queueTail = null - var _running = 0 + if (braceEscaped !== true && code === CHAR_COMMA) { + isBrace = token.isBrace = true; + isGlob = token.isGlob = true; + finished = true; - var self = { - push: push, - drain: noop, - saturated: noop, - pause: pause, - paused: false, - concurrency: concurrency, - running: running, - resume: resume, - idle: idle, - length: length, - unshift: unshift, - empty: noop, - kill: kill, - killAndDrain: killAndDrain - } + if (scanToEnd === true) { + continue; + } - return self + break; + } - function running () { - return _running - } + if (code === CHAR_RIGHT_CURLY_BRACE) { + braces--; - function pause () { - self.paused = true - } + if (braces === 0) { + braceEscaped = false; + isBrace = token.isBrace = true; + finished = true; + break; + } + } + } - function length () { - var current = queueHead - var counter = 0 + if (scanToEnd === true) { + continue; + } - while (current) { - current = current.next - counter++ + break; } - return counter - } + if (code === CHAR_FORWARD_SLASH) { + slashes.push(index); + tokens.push(token); + token = { value: '', depth: 0, isGlob: false }; - function resume () { - if (!self.paused) return - self.paused = false - for (var i = 0; i < self.concurrency; i++) { - _running++ - release() + if (finished === true) continue; + if (prev === CHAR_DOT && index === (start + 1)) { + start += 2; + continue; + } + + lastIndex = index + 1; + continue; } - } - function idle () { - return _running === 0 && self.length() === 0 - } + if (opts.noext !== true) { + const isExtglobChar = code === CHAR_PLUS + || code === CHAR_AT + || code === CHAR_ASTERISK + || code === CHAR_QUESTION_MARK + || code === CHAR_EXCLAMATION_MARK; - function push (value, done) { - var current = cache.get() + if (isExtglobChar === true && peek() === CHAR_LEFT_PARENTHESES) { + isGlob = token.isGlob = true; + isExtglob = token.isExtglob = true; + finished = true; - current.context = context - current.release = release - current.value = value - current.callback = done || noop + if (scanToEnd === true) { + while (eos() !== true && (code = advance())) { + if (code === CHAR_BACKWARD_SLASH) { + backslashes = token.backslashes = true; + code = advance(); + continue; + } - if (_running === self.concurrency || self.paused) { - if (queueTail) { - queueTail.next = current - queueTail = current - } else { - queueHead = current - queueTail = current - self.saturated() + if (code === CHAR_RIGHT_PARENTHESES) { + isGlob = token.isGlob = true; + finished = true; + break; + } + } + continue; + } + break; } - } else { - _running++ - worker.call(context, current.value, current.worked) } - } - - function unshift (value, done) { - var current = cache.get() - current.context = context - current.release = release - current.value = value - current.callback = done || noop + if (code === CHAR_ASTERISK) { + if (prev === CHAR_ASTERISK) isGlobstar = token.isGlobstar = true; + isGlob = token.isGlob = true; + finished = true; - if (_running === self.concurrency || self.paused) { - if (queueHead) { - current.next = queueHead - queueHead = current - } else { - queueHead = current - queueTail = current - self.saturated() + if (scanToEnd === true) { + continue; } - } else { - _running++ - worker.call(context, current.value, current.worked) + break; } - } - function release (holder) { - if (holder) { - cache.release(holder) + if (code === CHAR_QUESTION_MARK) { + isGlob = token.isGlob = true; + finished = true; + + if (scanToEnd === true) { + continue; + } + break; } - var next = queueHead - if (next) { - if (!self.paused) { - if (queueTail === queueHead) { - queueTail = null + + if (code === CHAR_LEFT_SQUARE_BRACKET) { + while (eos() !== true && (next = advance())) { + if (next === CHAR_BACKWARD_SLASH) { + backslashes = token.backslashes = true; + advance(); + continue; } - queueHead = next.next - next.next = null - worker.call(context, next.value, next.worked) - if (queueTail === null) { - self.empty() + + if (next === CHAR_RIGHT_SQUARE_BRACKET) { + isBracket = token.isBracket = true; + isGlob = token.isGlob = true; + finished = true; + + if (scanToEnd === true) { + continue; + } + break; } - } else { - _running-- } - } else if (--_running === 0) { - self.drain() } - } - - function kill () { - queueHead = null - queueTail = null - self.drain = noop - } - - function killAndDrain () { - queueHead = null - queueTail = null - self.drain() - self.drain = noop - } -} -function noop () {} + if (opts.nonegate !== true && code === CHAR_EXCLAMATION_MARK && index === start) { + negated = token.negated = true; + start++; + continue; + } -function Task () { - this.value = null - this.callback = noop - this.next = null - this.release = noop - this.context = null + if (opts.noparen !== true && code === CHAR_LEFT_PARENTHESES) { + isGlob = token.isGlob = true; - var self = this + if (scanToEnd === true) { + while (eos() !== true && (code = advance())) { + if (code === CHAR_LEFT_PARENTHESES) { + backslashes = token.backslashes = true; + code = advance(); + continue; + } - this.worked = function worked (err, result) { - var callback = self.callback - self.value = null - self.callback = noop - callback.call(self.context, err, result) - self.release(self) - } -} + if (code === CHAR_RIGHT_PARENTHESES) { + finished = true; + break; + } + } + continue; + } + break; + } -module.exports = fastqueue + if (isGlob === true) { + finished = true; + if (scanToEnd === true) { + continue; + } -/***/ }), -/* 307 */ -/***/ (function(module, exports, __webpack_require__) { + break; + } + } -"use strict"; + if (opts.noext === true) { + isExtglob = false; + isGlob = false; + } + let base = str; + let prefix = ''; + let glob = ''; -function reusify (Constructor) { - var head = new Constructor() - var tail = head + if (start > 0) { + prefix = str.slice(0, start); + str = str.slice(start); + lastIndex -= start; + } - function get () { - var current = head + if (base && isGlob === true && lastIndex > 0) { + base = str.slice(0, lastIndex); + glob = str.slice(lastIndex); + } else if (isGlob === true) { + base = ''; + glob = str; + } else { + base = str; + } - if (current.next) { - head = current.next - } else { - head = new Constructor() - tail = head + if (base && base !== '' && base !== '/' && base !== str) { + if (isPathSeparator(base.charCodeAt(base.length - 1))) { + base = base.slice(0, -1); } + } - current.next = null + if (opts.unescape === true) { + if (glob) glob = utils.removeBackslashes(glob); - return current + if (base && backslashes === true) { + base = utils.removeBackslashes(base); + } } - function release (obj) { - tail.next = obj - tail = obj - } + const state = { + prefix, + input, + start, + base, + glob, + isBrace, + isBracket, + isGlob, + isExtglob, + isGlobstar, + negated + }; - return { - get: get, - release: release + if (opts.tokens === true) { + state.maxDepth = 0; + if (!isPathSeparator(code)) { + tokens.push(token); + } + state.tokens = tokens; } -} -module.exports = reusify + if (opts.parts === true || opts.tokens === true) { + let prevIndex; + for (let idx = 0; idx < slashes.length; idx++) { + const n = prevIndex ? prevIndex + 1 : start; + const i = slashes[idx]; + const value = input.slice(n, i); + if (opts.tokens) { + if (idx === 0 && start !== 0) { + tokens[idx].isPrefix = true; + tokens[idx].value = prefix; + } else { + tokens[idx].value = value; + } + depth(tokens[idx]); + state.maxDepth += tokens[idx].depth; + } + if (idx !== 0 || value !== '') { + parts.push(value); + } + prevIndex = i; + } -/***/ }), -/* 308 */ -/***/ (function(module, exports, __webpack_require__) { + if (prevIndex && prevIndex + 1 < input.length) { + const value = input.slice(prevIndex + 1); + parts.push(value); -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -function isFatalError(settings, error) { - if (settings.errorFilter === null) { - return true; - } - return !settings.errorFilter(error); -} -exports.isFatalError = isFatalError; -function isAppliedFilter(filter, value) { - return filter === null || filter(value); -} -exports.isAppliedFilter = isAppliedFilter; -function replacePathSegmentSeparator(filepath, separator) { - return filepath.split(/[\\/]/).join(separator); -} -exports.replacePathSegmentSeparator = replacePathSegmentSeparator; -function joinPathSegments(a, b, separator) { - if (a === '') { - return b; - } - return a + separator + b; -} -exports.joinPathSegments = joinPathSegments; + if (opts.tokens) { + tokens[tokens.length - 1].value = value; + depth(tokens[tokens.length - 1]); + state.maxDepth += tokens[tokens.length - 1].depth; + } + } + state.slashes = slashes; + state.parts = parts; + } -/***/ }), -/* 309 */ -/***/ (function(module, exports, __webpack_require__) { + return state; +}; -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const common = __webpack_require__(308); -class Reader { - constructor(_root, _settings) { - this._root = _root; - this._settings = _settings; - this._root = common.replacePathSegmentSeparator(_root, _settings.pathSegmentSeparator); - } -} -exports.default = Reader; +module.exports = scan; /***/ }), -/* 310 */ +/* 288 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const stream_1 = __webpack_require__(173); -const async_1 = __webpack_require__(296); -class StreamProvider { - constructor(_root, _settings) { - this._root = _root; - this._settings = _settings; - this._reader = new async_1.default(this._root, this._settings); - this._stream = new stream_1.Readable({ - objectMode: true, - read: () => { }, - destroy: this._reader.destroy.bind(this._reader) - }); - } - read() { - this._reader.onError((error) => { - this._stream.emit('error', error); - }); - this._reader.onEntry((entry) => { - this._stream.push(entry); - }); - this._reader.onEnd(() => { - this._stream.push(null); - }); - this._reader.read(); - return this._stream; - } -} -exports.default = StreamProvider; -/***/ }), -/* 311 */ -/***/ (function(module, exports, __webpack_require__) { +const path = __webpack_require__(4); +const win32 = process.platform === 'win32'; +const { + REGEX_BACKSLASH, + REGEX_REMOVE_BACKSLASH, + REGEX_SPECIAL_CHARS, + REGEX_SPECIAL_CHARS_GLOBAL +} = __webpack_require__(289); -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const sync_1 = __webpack_require__(312); -class SyncProvider { - constructor(_root, _settings) { - this._root = _root; - this._settings = _settings; - this._reader = new sync_1.default(this._root, this._settings); - } - read() { - return this._reader.read(); - } -} -exports.default = SyncProvider; +exports.isObject = val => val !== null && typeof val === 'object' && !Array.isArray(val); +exports.hasRegexChars = str => REGEX_SPECIAL_CHARS.test(str); +exports.isRegexChar = str => str.length === 1 && exports.hasRegexChars(str); +exports.escapeRegex = str => str.replace(REGEX_SPECIAL_CHARS_GLOBAL, '\\$1'); +exports.toPosixSlashes = str => str.replace(REGEX_BACKSLASH, '/'); +exports.removeBackslashes = str => { + return str.replace(REGEX_REMOVE_BACKSLASH, match => { + return match === '\\' ? '' : match; + }); +}; -/***/ }), -/* 312 */ -/***/ (function(module, exports, __webpack_require__) { +exports.supportsLookbehinds = () => { + const segs = process.version.slice(1).split('.').map(Number); + if (segs.length === 3 && segs[0] >= 9 || (segs[0] === 8 && segs[1] >= 10)) { + return true; + } + return false; +}; -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const fsScandir = __webpack_require__(297); -const common = __webpack_require__(308); -const reader_1 = __webpack_require__(309); -class SyncReader extends reader_1.default { - constructor() { - super(...arguments); - this._scandir = fsScandir.scandirSync; - this._storage = new Set(); - this._queue = new Set(); - } - read() { - this._pushToQueue(this._root, this._settings.basePath); - this._handleQueue(); - return [...this._storage]; - } - _pushToQueue(directory, base) { - this._queue.add({ directory, base }); - } - _handleQueue() { - for (const item of this._queue.values()) { - this._handleDirectory(item.directory, item.base); - } - } - _handleDirectory(directory, base) { - try { - const entries = this._scandir(directory, this._settings.fsScandirSettings); - for (const entry of entries) { - this._handleEntry(entry, base); - } - } - catch (error) { - this._handleError(error); - } - } - _handleError(error) { - if (!common.isFatalError(this._settings, error)) { - return; - } - throw error; - } - _handleEntry(entry, base) { - const fullpath = entry.path; - if (base !== undefined) { - entry.path = common.joinPathSegments(base, entry.name, this._settings.pathSegmentSeparator); - } - if (common.isAppliedFilter(this._settings.entryFilter, entry)) { - this._pushToStorage(entry); - } - if (entry.dirent.isDirectory() && common.isAppliedFilter(this._settings.deepFilter, entry)) { - this._pushToQueue(fullpath, entry.path); - } - } - _pushToStorage(entry) { - this._storage.add(entry); - } -} -exports.default = SyncReader; +exports.isWindows = options => { + if (options && typeof options.windows === 'boolean') { + return options.windows; + } + return win32 === true || path.sep === '\\'; +}; +exports.escapeLast = (input, char, lastIdx) => { + const idx = input.lastIndexOf(char, lastIdx); + if (idx === -1) return input; + if (input[idx - 1] === '\\') return exports.escapeLast(input, char, idx - 1); + return `${input.slice(0, idx)}\\${input.slice(idx)}`; +}; -/***/ }), -/* 313 */ -/***/ (function(module, exports, __webpack_require__) { +exports.removePrefix = (input, state = {}) => { + let output = input; + if (output.startsWith('./')) { + output = output.slice(2); + state.prefix = './'; + } + return output; +}; -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const path = __webpack_require__(4); -const fsScandir = __webpack_require__(297); -class Settings { - constructor(_options = {}) { - this._options = _options; - this.basePath = this._getValue(this._options.basePath, undefined); - this.concurrency = this._getValue(this._options.concurrency, Infinity); - this.deepFilter = this._getValue(this._options.deepFilter, null); - this.entryFilter = this._getValue(this._options.entryFilter, null); - this.errorFilter = this._getValue(this._options.errorFilter, null); - this.pathSegmentSeparator = this._getValue(this._options.pathSegmentSeparator, path.sep); - this.fsScandirSettings = new fsScandir.Settings({ - followSymbolicLinks: this._options.followSymbolicLinks, - fs: this._options.fs, - pathSegmentSeparator: this._options.pathSegmentSeparator, - stats: this._options.stats, - throwErrorOnBrokenSymbolicLink: this._options.throwErrorOnBrokenSymbolicLink - }); - } - _getValue(option, value) { - return option === undefined ? value : option; - } -} -exports.default = Settings; +exports.wrapOutput = (input, state = {}, options = {}) => { + const prepend = options.contains ? '' : '^'; + const append = options.contains ? '' : '$'; + + let output = `${prepend}(?:${input})${append}`; + if (state.negated === true) { + output = `(?:^(?!${output}).*$)`; + } + return output; +}; /***/ }), -/* 314 */ +/* 289 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const path = __webpack_require__(4); -const fsStat = __webpack_require__(289); -const utils = __webpack_require__(259); -class Reader { - constructor(_settings) { - this._settings = _settings; - this._fsStatSettings = new fsStat.Settings({ - followSymbolicLink: this._settings.followSymbolicLinks, - fs: this._settings.fs, - throwErrorOnBrokenSymbolicLink: this._settings.followSymbolicLinks - }); - } - _getFullEntryPath(filepath) { - return path.resolve(this._settings.cwd, filepath); - } - _makeEntry(stats, pattern) { - const entry = { - name: pattern, - path: pattern, - dirent: utils.fs.createDirentFromStats(pattern, stats) - }; - if (this._settings.stats) { - entry.stats = stats; - } - return entry; - } - _isFatalError(error) { - return !utils.errno.isEnoentCodeError(error) && !this._settings.suppressErrors; - } -} -exports.default = Reader; -/***/ }), -/* 315 */ -/***/ (function(module, exports, __webpack_require__) { +const path = __webpack_require__(4); +const WIN_SLASH = '\\\\/'; +const WIN_NO_SLASH = `[^${WIN_SLASH}]`; -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const path = __webpack_require__(4); -const deep_1 = __webpack_require__(316); -const entry_1 = __webpack_require__(319); -const error_1 = __webpack_require__(320); -const entry_2 = __webpack_require__(321); -class Provider { - constructor(_settings) { - this._settings = _settings; - this.errorFilter = new error_1.default(this._settings); - this.entryFilter = new entry_1.default(this._settings, this._getMicromatchOptions()); - this.deepFilter = new deep_1.default(this._settings, this._getMicromatchOptions()); - this.entryTransformer = new entry_2.default(this._settings); - } - _getRootDirectory(task) { - return path.resolve(this._settings.cwd, task.base); - } - _getReaderOptions(task) { - const basePath = task.base === '.' ? '' : task.base; - return { - basePath, - pathSegmentSeparator: '/', - concurrency: this._settings.concurrency, - deepFilter: this.deepFilter.getFilter(basePath, task.positive, task.negative), - entryFilter: this.entryFilter.getFilter(task.positive, task.negative), - errorFilter: this.errorFilter.getFilter(), - followSymbolicLinks: this._settings.followSymbolicLinks, - fs: this._settings.fs, - stats: this._settings.stats, - throwErrorOnBrokenSymbolicLink: this._settings.throwErrorOnBrokenSymbolicLink, - transform: this.entryTransformer.getTransformer() - }; - } - _getMicromatchOptions() { - return { - dot: this._settings.dot, - matchBase: this._settings.baseNameMatch, - nobrace: !this._settings.braceExpansion, - nocase: !this._settings.caseSensitiveMatch, - noext: !this._settings.extglob, - noglobstar: !this._settings.globstar, - posix: true, - strictSlashes: false - }; - } -} -exports.default = Provider; +/** + * Posix glob regex + */ +const DOT_LITERAL = '\\.'; +const PLUS_LITERAL = '\\+'; +const QMARK_LITERAL = '\\?'; +const SLASH_LITERAL = '\\/'; +const ONE_CHAR = '(?=.)'; +const QMARK = '[^/]'; +const END_ANCHOR = `(?:${SLASH_LITERAL}|$)`; +const START_ANCHOR = `(?:^|${SLASH_LITERAL})`; +const DOTS_SLASH = `${DOT_LITERAL}{1,2}${END_ANCHOR}`; +const NO_DOT = `(?!${DOT_LITERAL})`; +const NO_DOTS = `(?!${START_ANCHOR}${DOTS_SLASH})`; +const NO_DOT_SLASH = `(?!${DOT_LITERAL}{0,1}${END_ANCHOR})`; +const NO_DOTS_SLASH = `(?!${DOTS_SLASH})`; +const QMARK_NO_DOT = `[^.${SLASH_LITERAL}]`; +const STAR = `${QMARK}*?`; -/***/ }), -/* 316 */ -/***/ (function(module, exports, __webpack_require__) { +const POSIX_CHARS = { + DOT_LITERAL, + PLUS_LITERAL, + QMARK_LITERAL, + SLASH_LITERAL, + ONE_CHAR, + QMARK, + END_ANCHOR, + DOTS_SLASH, + NO_DOT, + NO_DOTS, + NO_DOT_SLASH, + NO_DOTS_SLASH, + QMARK_NO_DOT, + STAR, + START_ANCHOR +}; -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(259); -const partial_1 = __webpack_require__(317); -class DeepFilter { - constructor(_settings, _micromatchOptions) { - this._settings = _settings; - this._micromatchOptions = _micromatchOptions; - } - getFilter(basePath, positive, negative) { - const matcher = this._getMatcher(positive); - const negativeRe = this._getNegativePatternsRe(negative); - return (entry) => this._filter(basePath, entry, matcher, negativeRe); - } - _getMatcher(patterns) { - return new partial_1.default(patterns, this._settings, this._micromatchOptions); - } - _getNegativePatternsRe(patterns) { - const affectDepthOfReadingPatterns = patterns.filter(utils.pattern.isAffectDepthOfReadingPattern); - return utils.pattern.convertPatternsToRe(affectDepthOfReadingPatterns, this._micromatchOptions); - } - _filter(basePath, entry, matcher, negativeRe) { - if (this._isSkippedByDeep(basePath, entry.path)) { - return false; - } - if (this._isSkippedSymbolicLink(entry)) { - return false; - } - const filepath = utils.path.removeLeadingDotSegment(entry.path); - if (this._isSkippedByPositivePatterns(filepath, matcher)) { - return false; - } - return this._isSkippedByNegativePatterns(filepath, negativeRe); - } - _isSkippedByDeep(basePath, entryPath) { - /** - * Avoid unnecessary depth calculations when it doesn't matter. - */ - if (this._settings.deep === Infinity) { - return false; - } - return this._getEntryLevel(basePath, entryPath) >= this._settings.deep; - } - _getEntryLevel(basePath, entryPath) { - const entryPathDepth = entryPath.split('/').length; - if (basePath === '') { - return entryPathDepth; - } - const basePathDepth = basePath.split('/').length; - return entryPathDepth - basePathDepth; - } - _isSkippedSymbolicLink(entry) { - return !this._settings.followSymbolicLinks && entry.dirent.isSymbolicLink(); - } - _isSkippedByPositivePatterns(entryPath, matcher) { - return !this._settings.baseNameMatch && !matcher.match(entryPath); - } - _isSkippedByNegativePatterns(entryPath, patternsRe) { - return !utils.pattern.matchAny(entryPath, patternsRe); - } -} -exports.default = DeepFilter; +/** + * Windows glob regex + */ +const WINDOWS_CHARS = { + ...POSIX_CHARS, -/***/ }), -/* 317 */ -/***/ (function(module, exports, __webpack_require__) { + SLASH_LITERAL: `[${WIN_SLASH}]`, + QMARK: WIN_NO_SLASH, + STAR: `${WIN_NO_SLASH}*?`, + DOTS_SLASH: `${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$)`, + NO_DOT: `(?!${DOT_LITERAL})`, + NO_DOTS: `(?!(?:^|[${WIN_SLASH}])${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`, + NO_DOT_SLASH: `(?!${DOT_LITERAL}{0,1}(?:[${WIN_SLASH}]|$))`, + NO_DOTS_SLASH: `(?!${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`, + QMARK_NO_DOT: `[^.${WIN_SLASH}]`, + START_ANCHOR: `(?:^|[${WIN_SLASH}])`, + END_ANCHOR: `(?:[${WIN_SLASH}]|$)` +}; -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const matcher_1 = __webpack_require__(318); -class PartialMatcher extends matcher_1.default { - match(filepath) { - const parts = filepath.split('/'); - const levels = parts.length; - const patterns = this._storage.filter((info) => !info.complete || info.segments.length > levels); - for (const pattern of patterns) { - const section = pattern.sections[0]; - /** - * In this case, the pattern has a globstar and we must read all directories unconditionally, - * but only if the level has reached the end of the first group. - * - * fixtures/{a,b}/** - * ^ true/false ^ always true - */ - if (!pattern.complete && levels > section.length) { - return true; - } - const match = parts.every((part, index) => { - const segment = pattern.segments[index]; - if (segment.dynamic && segment.patternRe.test(part)) { - return true; - } - if (!segment.dynamic && segment.pattern === part) { - return true; - } - return false; - }); - if (match) { - return true; - } - } - return false; - } -} -exports.default = PartialMatcher; +/** + * POSIX Bracket Regex + */ +const POSIX_REGEX_SOURCE = { + alnum: 'a-zA-Z0-9', + alpha: 'a-zA-Z', + ascii: '\\x00-\\x7F', + blank: ' \\t', + cntrl: '\\x00-\\x1F\\x7F', + digit: '0-9', + graph: '\\x21-\\x7E', + lower: 'a-z', + print: '\\x20-\\x7E ', + punct: '\\-!"#$%&\'()\\*+,./:;<=>?@[\\]^_`{|}~', + space: ' \\t\\r\\n\\v\\f', + upper: 'A-Z', + word: 'A-Za-z0-9_', + xdigit: 'A-Fa-f0-9' +}; -/***/ }), -/* 318 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(259); -class Matcher { - constructor(_patterns, _settings, _micromatchOptions) { - this._patterns = _patterns; - this._settings = _settings; - this._micromatchOptions = _micromatchOptions; - this._storage = []; - this._fillStorage(); - } - _fillStorage() { - /** - * The original pattern may include `{,*,**,a/*}`, which will lead to problems with matching (unresolved level). - * So, before expand patterns with brace expansion into separated patterns. - */ - const patterns = utils.pattern.expandPatternsWithBraceExpansion(this._patterns); - for (const pattern of patterns) { - const segments = this._getPatternSegments(pattern); - const sections = this._splitSegmentsIntoSections(segments); - this._storage.push({ - complete: sections.length <= 1, - pattern, - segments, - sections - }); - } - } - _getPatternSegments(pattern) { - const parts = utils.pattern.getPatternParts(pattern, this._micromatchOptions); - return parts.map((part) => { - const dynamic = utils.pattern.isDynamicPattern(part, this._settings); - if (!dynamic) { - return { - dynamic: false, - pattern: part - }; - } - return { - dynamic: true, - pattern: part, - patternRe: utils.pattern.makeRe(part, this._micromatchOptions) - }; - }); - } - _splitSegmentsIntoSections(segments) { - return utils.array.splitWhen(segments, (segment) => segment.dynamic && utils.pattern.hasGlobStar(segment.pattern)); - } -} -exports.default = Matcher; - - -/***/ }), -/* 319 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(259); -class EntryFilter { - constructor(_settings, _micromatchOptions) { - this._settings = _settings; - this._micromatchOptions = _micromatchOptions; - this.index = new Map(); - } - getFilter(positive, negative) { - const positiveRe = utils.pattern.convertPatternsToRe(positive, this._micromatchOptions); - const negativeRe = utils.pattern.convertPatternsToRe(negative, this._micromatchOptions); - return (entry) => this._filter(entry, positiveRe, negativeRe); - } - _filter(entry, positiveRe, negativeRe) { - if (this._settings.unique && this._isDuplicateEntry(entry)) { - return false; - } - if (this._onlyFileFilter(entry) || this._onlyDirectoryFilter(entry)) { - return false; - } - if (this._isSkippedByAbsoluteNegativePatterns(entry.path, negativeRe)) { - return false; - } - const filepath = this._settings.baseNameMatch ? entry.name : entry.path; - const isMatched = this._isMatchToPatterns(filepath, positiveRe) && !this._isMatchToPatterns(entry.path, negativeRe); - if (this._settings.unique && isMatched) { - this._createIndexRecord(entry); - } - return isMatched; - } - _isDuplicateEntry(entry) { - return this.index.has(entry.path); - } - _createIndexRecord(entry) { - this.index.set(entry.path, undefined); - } - _onlyFileFilter(entry) { - return this._settings.onlyFiles && !entry.dirent.isFile(); - } - _onlyDirectoryFilter(entry) { - return this._settings.onlyDirectories && !entry.dirent.isDirectory(); - } - _isSkippedByAbsoluteNegativePatterns(entryPath, patternsRe) { - if (!this._settings.absolute) { - return false; - } - const fullpath = utils.path.makeAbsolute(this._settings.cwd, entryPath); - return utils.pattern.matchAny(fullpath, patternsRe); - } - _isMatchToPatterns(entryPath, patternsRe) { - const filepath = utils.path.removeLeadingDotSegment(entryPath); - return utils.pattern.matchAny(filepath, patternsRe); - } -} -exports.default = EntryFilter; +module.exports = { + MAX_LENGTH: 1024 * 64, + POSIX_REGEX_SOURCE, + // regular expressions + REGEX_BACKSLASH: /\\(?![*+?^${}(|)[\]])/g, + REGEX_NON_SPECIAL_CHARS: /^[^@![\].,$*+?^{}()|\\/]+/, + REGEX_SPECIAL_CHARS: /[-*+?.^${}(|)[\]]/, + REGEX_SPECIAL_CHARS_BACKREF: /(\\?)((\W)(\3*))/g, + REGEX_SPECIAL_CHARS_GLOBAL: /([-*+?.^${}(|)[\]])/g, + REGEX_REMOVE_BACKSLASH: /(?:\[.*?[^\\]\]|\\(?=.))/g, -/***/ }), -/* 320 */ -/***/ (function(module, exports, __webpack_require__) { + // Replace globs with equivalent patterns to reduce parsing time. + REPLACEMENTS: { + '***': '*', + '**/**': '**', + '**/**/**': '**' + }, -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(259); -class ErrorFilter { - constructor(_settings) { - this._settings = _settings; - } - getFilter() { - return (error) => this._isNonFatalError(error); - } - _isNonFatalError(error) { - return utils.errno.isEnoentCodeError(error) || this._settings.suppressErrors; - } -} -exports.default = ErrorFilter; + // Digits + CHAR_0: 48, /* 0 */ + CHAR_9: 57, /* 9 */ + // Alphabet chars. + CHAR_UPPERCASE_A: 65, /* A */ + CHAR_LOWERCASE_A: 97, /* a */ + CHAR_UPPERCASE_Z: 90, /* Z */ + CHAR_LOWERCASE_Z: 122, /* z */ -/***/ }), -/* 321 */ -/***/ (function(module, exports, __webpack_require__) { + CHAR_LEFT_PARENTHESES: 40, /* ( */ + CHAR_RIGHT_PARENTHESES: 41, /* ) */ -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(259); -class EntryTransformer { - constructor(_settings) { - this._settings = _settings; - } - getTransformer() { - return (entry) => this._transform(entry); - } - _transform(entry) { - let filepath = entry.path; - if (this._settings.absolute) { - filepath = utils.path.makeAbsolute(this._settings.cwd, filepath); - filepath = utils.path.unixify(filepath); - } - if (this._settings.markDirectories && entry.dirent.isDirectory()) { - filepath += '/'; - } - if (!this._settings.objectMode) { - return filepath; - } - return Object.assign(Object.assign({}, entry), { path: filepath }); - } -} -exports.default = EntryTransformer; + CHAR_ASTERISK: 42, /* * */ + // Non-alphabetic chars. + CHAR_AMPERSAND: 38, /* & */ + CHAR_AT: 64, /* @ */ + CHAR_BACKWARD_SLASH: 92, /* \ */ + CHAR_CARRIAGE_RETURN: 13, /* \r */ + CHAR_CIRCUMFLEX_ACCENT: 94, /* ^ */ + CHAR_COLON: 58, /* : */ + CHAR_COMMA: 44, /* , */ + CHAR_DOT: 46, /* . */ + CHAR_DOUBLE_QUOTE: 34, /* " */ + CHAR_EQUAL: 61, /* = */ + CHAR_EXCLAMATION_MARK: 33, /* ! */ + CHAR_FORM_FEED: 12, /* \f */ + CHAR_FORWARD_SLASH: 47, /* / */ + CHAR_GRAVE_ACCENT: 96, /* ` */ + CHAR_HASH: 35, /* # */ + CHAR_HYPHEN_MINUS: 45, /* - */ + CHAR_LEFT_ANGLE_BRACKET: 60, /* < */ + CHAR_LEFT_CURLY_BRACE: 123, /* { */ + CHAR_LEFT_SQUARE_BRACKET: 91, /* [ */ + CHAR_LINE_FEED: 10, /* \n */ + CHAR_NO_BREAK_SPACE: 160, /* \u00A0 */ + CHAR_PERCENT: 37, /* % */ + CHAR_PLUS: 43, /* + */ + CHAR_QUESTION_MARK: 63, /* ? */ + CHAR_RIGHT_ANGLE_BRACKET: 62, /* > */ + CHAR_RIGHT_CURLY_BRACE: 125, /* } */ + CHAR_RIGHT_SQUARE_BRACKET: 93, /* ] */ + CHAR_SEMICOLON: 59, /* ; */ + CHAR_SINGLE_QUOTE: 39, /* ' */ + CHAR_SPACE: 32, /* */ + CHAR_TAB: 9, /* \t */ + CHAR_UNDERSCORE: 95, /* _ */ + CHAR_VERTICAL_LINE: 124, /* | */ + CHAR_ZERO_WIDTH_NOBREAK_SPACE: 65279, /* \uFEFF */ -/***/ }), -/* 322 */ -/***/ (function(module, exports, __webpack_require__) { + SEP: path.sep, -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const stream_1 = __webpack_require__(173); -const stream_2 = __webpack_require__(288); -const provider_1 = __webpack_require__(315); -class ProviderStream extends provider_1.default { - constructor() { - super(...arguments); - this._reader = new stream_2.default(this._settings); - } - read(task) { - const root = this._getRootDirectory(task); - const options = this._getReaderOptions(task); - const source = this.api(root, task, options); - const destination = new stream_1.Readable({ objectMode: true, read: () => { } }); - source - .once('error', (error) => destination.emit('error', error)) - .on('data', (entry) => destination.emit('data', options.transform(entry))) - .once('end', () => destination.emit('end')); - destination - .once('close', () => source.destroy()); - return destination; - } - api(root, task, options) { - if (task.dynamic) { - return this._reader.dynamic(root, options); - } - return this._reader.static(task.patterns, options); - } -} -exports.default = ProviderStream; + /** + * Create EXTGLOB_CHARS + */ + extglobChars(chars) { + return { + '!': { type: 'negate', open: '(?:(?!(?:', close: `))${chars.STAR})` }, + '?': { type: 'qmark', open: '(?:', close: ')?' }, + '+': { type: 'plus', open: '(?:', close: ')+' }, + '*': { type: 'star', open: '(?:', close: ')*' }, + '@': { type: 'at', open: '(?:', close: ')' } + }; + }, -/***/ }), -/* 323 */ -/***/ (function(module, exports, __webpack_require__) { + /** + * Create GLOB_CHARS + */ -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const sync_1 = __webpack_require__(324); -const provider_1 = __webpack_require__(315); -class ProviderSync extends provider_1.default { - constructor() { - super(...arguments); - this._reader = new sync_1.default(this._settings); - } - read(task) { - const root = this._getRootDirectory(task); - const options = this._getReaderOptions(task); - const entries = this.api(root, task, options); - return entries.map(options.transform); - } - api(root, task, options) { - if (task.dynamic) { - return this._reader.dynamic(root, options); - } - return this._reader.static(task.patterns, options); - } -} -exports.default = ProviderSync; + globChars(win32) { + return win32 === true ? WINDOWS_CHARS : POSIX_CHARS; + } +}; /***/ }), -/* 324 */ +/* 290 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const fsStat = __webpack_require__(289); -const fsWalk = __webpack_require__(294); -const reader_1 = __webpack_require__(314); -class ReaderSync extends reader_1.default { - constructor() { - super(...arguments); - this._walkSync = fsWalk.walkSync; - this._statSync = fsStat.statSync; - } - dynamic(root, options) { - return this._walkSync(root, options); - } - static(patterns, options) { - const entries = []; - for (const pattern of patterns) { - const filepath = this._getFullEntryPath(pattern); - const entry = this._getEntry(filepath, pattern, options); - if (entry === null || !options.entryFilter(entry)) { - continue; - } - entries.push(entry); - } - return entries; - } - _getEntry(filepath, pattern, options) { - try { - const stats = this._getStat(filepath); - return this._makeEntry(stats, pattern); - } - catch (error) { - if (options.errorFilter(error)) { - return null; - } - throw error; - } - } - _getStat(filepath) { - return this._statSync(filepath, this._fsStatSettings); - } -} -exports.default = ReaderSync; -/***/ }), -/* 325 */ -/***/ (function(module, exports, __webpack_require__) { +const constants = __webpack_require__(289); +const utils = __webpack_require__(288); -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.DEFAULT_FILE_SYSTEM_ADAPTER = void 0; -const fs = __webpack_require__(132); -const os = __webpack_require__(122); -/** - * The `os.cpus` method can return zero. We expect the number of cores to be greater than zero. - * https://github.com/nodejs/node/blob/7faeddf23a98c53896f8b574a6e66589e8fb1eb8/lib/os.js#L106-L107 - */ -const CPU_COUNT = Math.max(os.cpus().length, 1); -exports.DEFAULT_FILE_SYSTEM_ADAPTER = { - lstat: fs.lstat, - lstatSync: fs.lstatSync, - stat: fs.stat, - statSync: fs.statSync, - readdir: fs.readdir, - readdirSync: fs.readdirSync -}; -class Settings { - constructor(_options = {}) { - this._options = _options; - this.absolute = this._getValue(this._options.absolute, false); - this.baseNameMatch = this._getValue(this._options.baseNameMatch, false); - this.braceExpansion = this._getValue(this._options.braceExpansion, true); - this.caseSensitiveMatch = this._getValue(this._options.caseSensitiveMatch, true); - this.concurrency = this._getValue(this._options.concurrency, CPU_COUNT); - this.cwd = this._getValue(this._options.cwd, process.cwd()); - this.deep = this._getValue(this._options.deep, Infinity); - this.dot = this._getValue(this._options.dot, false); - this.extglob = this._getValue(this._options.extglob, true); - this.followSymbolicLinks = this._getValue(this._options.followSymbolicLinks, true); - this.fs = this._getFileSystemMethods(this._options.fs); - this.globstar = this._getValue(this._options.globstar, true); - this.ignore = this._getValue(this._options.ignore, []); - this.markDirectories = this._getValue(this._options.markDirectories, false); - this.objectMode = this._getValue(this._options.objectMode, false); - this.onlyDirectories = this._getValue(this._options.onlyDirectories, false); - this.onlyFiles = this._getValue(this._options.onlyFiles, true); - this.stats = this._getValue(this._options.stats, false); - this.suppressErrors = this._getValue(this._options.suppressErrors, false); - this.throwErrorOnBrokenSymbolicLink = this._getValue(this._options.throwErrorOnBrokenSymbolicLink, false); - this.unique = this._getValue(this._options.unique, true); - if (this.onlyDirectories) { - this.onlyFiles = false; - } - if (this.stats) { - this.objectMode = true; - } - } - _getValue(option, value) { - return option === undefined ? value : option; - } - _getFileSystemMethods(methods = {}) { - return Object.assign(Object.assign({}, exports.DEFAULT_FILE_SYSTEM_ADAPTER), methods); - } -} -exports.default = Settings; +/** + * Constants + */ +const { + MAX_LENGTH, + POSIX_REGEX_SOURCE, + REGEX_NON_SPECIAL_CHARS, + REGEX_SPECIAL_CHARS_BACKREF, + REPLACEMENTS +} = constants; -/***/ }), -/* 326 */ -/***/ (function(module, exports, __webpack_require__) { +/** + * Helpers + */ -"use strict"; +const expandRange = (args, options) => { + if (typeof options.expandRange === 'function') { + return options.expandRange(...args, options); + } -const path = __webpack_require__(4); -const pathType = __webpack_require__(327); + args.sort(); + const value = `[${args.join('-')}]`; -const getExtensions = extensions => extensions.length > 1 ? `{${extensions.join(',')}}` : extensions[0]; + try { + /* eslint-disable-next-line no-new */ + new RegExp(value); + } catch (ex) { + return args.map(v => utils.escapeRegex(v)).join('..'); + } -const getPath = (filepath, cwd) => { - const pth = filepath[0] === '!' ? filepath.slice(1) : filepath; - return path.isAbsolute(pth) ? pth : path.join(cwd, pth); + return value; }; -const addExtensions = (file, extensions) => { - if (path.extname(file)) { - return `**/${file}`; - } +/** + * Create the message for a syntax error + */ - return `**/${file}.${getExtensions(extensions)}`; +const syntaxError = (type, char) => { + return `Missing ${type}: "${char}" - use "\\\\${char}" to match literal characters`; }; -const getGlob = (directory, options) => { - if (options.files && !Array.isArray(options.files)) { - throw new TypeError(`Expected \`files\` to be of type \`Array\` but received type \`${typeof options.files}\``); - } +/** + * Parse the given input string. + * @param {String} input + * @param {Object} options + * @return {Object} + */ - if (options.extensions && !Array.isArray(options.extensions)) { - throw new TypeError(`Expected \`extensions\` to be of type \`Array\` but received type \`${typeof options.extensions}\``); - } +const parse = (input, options) => { + if (typeof input !== 'string') { + throw new TypeError('Expected a string'); + } - if (options.files && options.extensions) { - return options.files.map(x => path.posix.join(directory, addExtensions(x, options.extensions))); - } + input = REPLACEMENTS[input] || input; - if (options.files) { - return options.files.map(x => path.posix.join(directory, `**/${x}`)); - } + const opts = { ...options }; + const max = typeof opts.maxLength === 'number' ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH; - if (options.extensions) { - return [path.posix.join(directory, `**/*.${getExtensions(options.extensions)}`)]; - } + let len = input.length; + if (len > max) { + throw new SyntaxError(`Input length: ${len}, exceeds maximum allowed length: ${max}`); + } - return [path.posix.join(directory, '**')]; -}; + const bos = { type: 'bos', value: '', output: opts.prepend || '' }; + const tokens = [bos]; -module.exports = async (input, options) => { - options = { - cwd: process.cwd(), - ...options - }; + const capture = opts.capture ? '' : '?:'; + const win32 = utils.isWindows(options); - if (typeof options.cwd !== 'string') { - throw new TypeError(`Expected \`cwd\` to be of type \`string\` but received type \`${typeof options.cwd}\``); - } + // create constants based on platform, for windows or posix + const PLATFORM_CHARS = constants.globChars(win32); + const EXTGLOB_CHARS = constants.extglobChars(PLATFORM_CHARS); - const globs = await Promise.all([].concat(input).map(async x => { - const isDirectory = await pathType.isDirectory(getPath(x, options.cwd)); - return isDirectory ? getGlob(x, options) : x; - })); + const { + DOT_LITERAL, + PLUS_LITERAL, + SLASH_LITERAL, + ONE_CHAR, + DOTS_SLASH, + NO_DOT, + NO_DOT_SLASH, + NO_DOTS_SLASH, + QMARK, + QMARK_NO_DOT, + STAR, + START_ANCHOR + } = PLATFORM_CHARS; - return [].concat.apply([], globs); // eslint-disable-line prefer-spread -}; - -module.exports.sync = (input, options) => { - options = { - cwd: process.cwd(), - ...options - }; + const globstar = (opts) => { + return `(${capture}(?:(?!${START_ANCHOR}${opts.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`; + }; - if (typeof options.cwd !== 'string') { - throw new TypeError(`Expected \`cwd\` to be of type \`string\` but received type \`${typeof options.cwd}\``); - } + const nodot = opts.dot ? '' : NO_DOT; + const qmarkNoDot = opts.dot ? QMARK : QMARK_NO_DOT; + let star = opts.bash === true ? globstar(opts) : STAR; - const globs = [].concat(input).map(x => pathType.isDirectorySync(getPath(x, options.cwd)) ? getGlob(x, options) : x); + if (opts.capture) { + star = `(${star})`; + } - return [].concat.apply([], globs); // eslint-disable-line prefer-spread -}; + // minimatch options support + if (typeof opts.noext === 'boolean') { + opts.noextglob = opts.noext; + } + const state = { + input, + index: -1, + start: 0, + dot: opts.dot === true, + consumed: '', + output: '', + prefix: '', + backtrack: false, + negated: false, + brackets: 0, + braces: 0, + parens: 0, + quotes: 0, + globstar: false, + tokens + }; -/***/ }), -/* 327 */ -/***/ (function(module, exports, __webpack_require__) { + input = utils.removePrefix(input, state); + len = input.length; -"use strict"; + const extglobs = []; + const braces = []; + const stack = []; + let prev = bos; + let value; -const {promisify} = __webpack_require__(113); -const fs = __webpack_require__(132); + /** + * Tokenizing helpers + */ -async function isType(fsStatType, statsMethodName, filePath) { - if (typeof filePath !== 'string') { - throw new TypeError(`Expected a string, got ${typeof filePath}`); - } + const eos = () => state.index === len - 1; + const peek = state.peek = (n = 1) => input[state.index + n]; + const advance = state.advance = () => input[++state.index]; + const remaining = () => input.slice(state.index + 1); + const consume = (value = '', num = 0) => { + state.consumed += value; + state.index += num; + }; + const append = token => { + state.output += token.output != null ? token.output : token.value; + consume(token.value); + }; - try { - const stats = await promisify(fs[fsStatType])(filePath); - return stats[statsMethodName](); - } catch (error) { - if (error.code === 'ENOENT') { - return false; - } + const negate = () => { + let count = 1; - throw error; - } -} + while (peek() === '!' && (peek(2) !== '(' || peek(3) === '?')) { + advance(); + state.start++; + count++; + } -function isTypeSync(fsStatType, statsMethodName, filePath) { - if (typeof filePath !== 'string') { - throw new TypeError(`Expected a string, got ${typeof filePath}`); - } + if (count % 2 === 0) { + return false; + } - try { - return fs[fsStatType](filePath)[statsMethodName](); - } catch (error) { - if (error.code === 'ENOENT') { - return false; - } + state.negated = true; + state.start++; + return true; + }; - throw error; - } -} + const increment = type => { + state[type]++; + stack.push(type); + }; -exports.isFile = isType.bind(null, 'stat', 'isFile'); -exports.isDirectory = isType.bind(null, 'stat', 'isDirectory'); -exports.isSymlink = isType.bind(null, 'lstat', 'isSymbolicLink'); -exports.isFileSync = isTypeSync.bind(null, 'statSync', 'isFile'); -exports.isDirectorySync = isTypeSync.bind(null, 'statSync', 'isDirectory'); -exports.isSymlinkSync = isTypeSync.bind(null, 'lstatSync', 'isSymbolicLink'); + const decrement = type => { + state[type]--; + stack.pop(); + }; + /** + * Push tokens onto the tokens array. This helper speeds up + * tokenizing by 1) helping us avoid backtracking as much as possible, + * and 2) helping us avoid creating extra tokens when consecutive + * characters are plain text. This improves performance and simplifies + * lookbehinds. + */ -/***/ }), -/* 328 */ -/***/ (function(module, exports, __webpack_require__) { + const push = tok => { + if (prev.type === 'globstar') { + const isBrace = state.braces > 0 && (tok.type === 'comma' || tok.type === 'brace'); + const isExtglob = tok.extglob === true || (extglobs.length && (tok.type === 'pipe' || tok.type === 'paren')); -"use strict"; + if (tok.type !== 'slash' && tok.type !== 'paren' && !isBrace && !isExtglob) { + state.output = state.output.slice(0, -prev.output.length); + prev.type = 'star'; + prev.value = '*'; + prev.output = star; + state.output += prev.output; + } + } -const {promisify} = __webpack_require__(113); -const fs = __webpack_require__(132); -const path = __webpack_require__(4); -const fastGlob = __webpack_require__(257); -const gitIgnore = __webpack_require__(329); -const slash = __webpack_require__(330); + if (extglobs.length && tok.type !== 'paren' && !EXTGLOB_CHARS[tok.value]) { + extglobs[extglobs.length - 1].inner += tok.value; + } -const DEFAULT_IGNORE = [ - '**/node_modules/**', - '**/flow-typed/**', - '**/coverage/**', - '**/.git' -]; + if (tok.value || tok.output) append(tok); + if (prev && prev.type === 'text' && tok.type === 'text') { + prev.value += tok.value; + prev.output = (prev.output || '') + tok.value; + return; + } -const readFileP = promisify(fs.readFile); + tok.prev = prev; + tokens.push(tok); + prev = tok; + }; -const mapGitIgnorePatternTo = base => ignore => { - if (ignore.startsWith('!')) { - return '!' + path.posix.join(base, ignore.slice(1)); - } + const extglobOpen = (type, value) => { + const token = { ...EXTGLOB_CHARS[value], conditions: 1, inner: '' }; - return path.posix.join(base, ignore); -}; + token.prev = prev; + token.parens = state.parens; + token.output = state.output; + const output = (opts.capture ? '(' : '') + token.open; -const parseGitIgnore = (content, options) => { - const base = slash(path.relative(options.cwd, path.dirname(options.fileName))); + increment('parens'); + push({ type, value, output: state.output ? '' : ONE_CHAR }); + push({ type: 'paren', extglob: true, value: advance(), output }); + extglobs.push(token); + }; - return content - .split(/\r?\n/) - .filter(Boolean) - .filter(line => !line.startsWith('#')) - .map(mapGitIgnorePatternTo(base)); -}; + const extglobClose = token => { + let output = token.close + (opts.capture ? ')' : ''); -const reduceIgnore = files => { - return files.reduce((ignores, file) => { - ignores.add(parseGitIgnore(file.content, { - cwd: file.cwd, - fileName: file.filePath - })); - return ignores; - }, gitIgnore()); -}; + if (token.type === 'negate') { + let extglobStar = star; -const ensureAbsolutePathForCwd = (cwd, p) => { - if (path.isAbsolute(p)) { - if (p.startsWith(cwd)) { - return p; - } + if (token.inner && token.inner.length > 1 && token.inner.includes('/')) { + extglobStar = globstar(opts); + } - throw new Error(`Path ${p} is not in cwd ${cwd}`); - } + if (extglobStar !== star || eos() || /^\)+$/.test(remaining())) { + output = token.close = `)$))${extglobStar}`; + } - return path.join(cwd, p); -}; + if (token.prev.type === 'bos' && eos()) { + state.negatedExtglob = true; + } + } -const getIsIgnoredPredecate = (ignores, cwd) => { - return p => ignores.ignores(slash(path.relative(cwd, ensureAbsolutePathForCwd(cwd, p)))); -}; + push({ type: 'paren', extglob: true, value, output }); + decrement('parens'); + }; -const getFile = async (file, cwd) => { - const filePath = path.join(cwd, file); - const content = await readFileP(filePath, 'utf8'); + /** + * Fast paths + */ - return { - cwd, - filePath, - content - }; -}; + if (opts.fastpaths !== false && !/(^[*!]|[/()[\]{}"])/.test(input)) { + let backslashes = false; -const getFileSync = (file, cwd) => { - const filePath = path.join(cwd, file); - const content = fs.readFileSync(filePath, 'utf8'); + let output = input.replace(REGEX_SPECIAL_CHARS_BACKREF, (m, esc, chars, first, rest, index) => { + if (first === '\\') { + backslashes = true; + return m; + } - return { - cwd, - filePath, - content - }; -}; + if (first === '?') { + if (esc) { + return esc + first + (rest ? QMARK.repeat(rest.length) : ''); + } + if (index === 0) { + return qmarkNoDot + (rest ? QMARK.repeat(rest.length) : ''); + } + return QMARK.repeat(chars.length); + } -const normalizeOptions = ({ - ignore = [], - cwd = slash(process.cwd()) -} = {}) => { - return {ignore, cwd}; -}; + if (first === '.') { + return DOT_LITERAL.repeat(chars.length); + } -module.exports = async options => { - options = normalizeOptions(options); + if (first === '*') { + if (esc) { + return esc + first + (rest ? star : ''); + } + return star; + } + return esc ? m : `\\${m}`; + }); - const paths = await fastGlob('**/.gitignore', { - ignore: DEFAULT_IGNORE.concat(options.ignore), - cwd: options.cwd - }); + if (backslashes === true) { + if (opts.unescape === true) { + output = output.replace(/\\/g, ''); + } else { + output = output.replace(/\\+/g, m => { + return m.length % 2 === 0 ? '\\\\' : (m ? '\\' : ''); + }); + } + } - const files = await Promise.all(paths.map(file => getFile(file, options.cwd))); - const ignores = reduceIgnore(files); + if (output === input && opts.contains === true) { + state.output = input; + return state; + } - return getIsIgnoredPredecate(ignores, options.cwd); -}; + state.output = utils.wrapOutput(output, state, options); + return state; + } -module.exports.sync = options => { - options = normalizeOptions(options); + /** + * Tokenize input until we reach end-of-string + */ - const paths = fastGlob.sync('**/.gitignore', { - ignore: DEFAULT_IGNORE.concat(options.ignore), - cwd: options.cwd - }); + while (!eos()) { + value = advance(); - const files = paths.map(file => getFileSync(file, options.cwd)); - const ignores = reduceIgnore(files); + if (value === '\u0000') { + continue; + } - return getIsIgnoredPredecate(ignores, options.cwd); -}; + /** + * Escaped characters + */ + if (value === '\\') { + const next = peek(); -/***/ }), -/* 329 */ -/***/ (function(module, exports) { + if (next === '/' && opts.bash !== true) { + continue; + } -// A simple implementation of make-array -function makeArray (subject) { - return Array.isArray(subject) - ? subject - : [subject] -} + if (next === '.' || next === ';') { + continue; + } -const EMPTY = '' -const SPACE = ' ' -const ESCAPE = '\\' -const REGEX_TEST_BLANK_LINE = /^\s+$/ -const REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION = /^\\!/ -const REGEX_REPLACE_LEADING_EXCAPED_HASH = /^\\#/ -const REGEX_SPLITALL_CRLF = /\r?\n/g -// /foo, -// ./foo, -// ../foo, -// . -// .. -const REGEX_TEST_INVALID_PATH = /^\.*\/|^\.+$/ + if (!next) { + value += '\\'; + push({ type: 'text', value }); + continue; + } -const SLASH = '/' -const KEY_IGNORE = typeof Symbol !== 'undefined' - ? Symbol.for('node-ignore') - /* istanbul ignore next */ - : 'node-ignore' + // collapse slashes to reduce potential for exploits + const match = /^\\+/.exec(remaining()); + let slashes = 0; -const define = (object, key, value) => - Object.defineProperty(object, key, {value}) + if (match && match[0].length > 2) { + slashes = match[0].length; + state.index += slashes; + if (slashes % 2 !== 0) { + value += '\\'; + } + } -const REGEX_REGEXP_RANGE = /([0-z])-([0-z])/g + if (opts.unescape === true) { + value = advance() || ''; + } else { + value += advance() || ''; + } -// Sanitize the range of a regular expression -// The cases are complicated, see test cases for details -const sanitizeRange = range => range.replace( - REGEX_REGEXP_RANGE, - (match, from, to) => from.charCodeAt(0) <= to.charCodeAt(0) - ? match - // Invalid range (out of order) which is ok for gitignore rules but - // fatal for JavaScript regular expression, so eliminate it. - : EMPTY -) + if (state.brackets === 0) { + push({ type: 'text', value }); + continue; + } + } -// See fixtures #59 -const cleanRangeBackSlash = slashes => { - const {length} = slashes - return slashes.slice(0, length - length % 2) -} + /** + * If we're inside a regex character class, continue + * until we reach the closing bracket. + */ -// > If the pattern ends with a slash, -// > it is removed for the purpose of the following description, -// > but it would only find a match with a directory. -// > In other words, foo/ will match a directory foo and paths underneath it, -// > but will not match a regular file or a symbolic link foo -// > (this is consistent with the way how pathspec works in general in Git). -// '`foo/`' will not match regular file '`foo`' or symbolic link '`foo`' -// -> ignore-rules will not deal with it, because it costs extra `fs.stat` call -// you could use option `mark: true` with `glob` + if (state.brackets > 0 && (value !== ']' || prev.value === '[' || prev.value === '[^')) { + if (opts.posix !== false && value === ':') { + const inner = prev.value.slice(1); + if (inner.includes('[')) { + prev.posix = true; -// '`foo/`' should not continue with the '`..`' -const REPLACERS = [ + if (inner.includes(':')) { + const idx = prev.value.lastIndexOf('['); + const pre = prev.value.slice(0, idx); + const rest = prev.value.slice(idx + 2); + const posix = POSIX_REGEX_SOURCE[rest]; + if (posix) { + prev.value = pre + posix; + state.backtrack = true; + advance(); - // > Trailing spaces are ignored unless they are quoted with backslash ("\") - [ - // (a\ ) -> (a ) - // (a ) -> (a) - // (a \ ) -> (a ) - /\\?\s+$/, - match => match.indexOf('\\') === 0 - ? SPACE - : EMPTY - ], + if (!bos.output && tokens.indexOf(prev) === 1) { + bos.output = ONE_CHAR; + } + continue; + } + } + } + } - // replace (\ ) with ' ' - [ - /\\\s/g, - () => SPACE - ], + if ((value === '[' && peek() !== ':') || (value === '-' && peek() === ']')) { + value = `\\${value}`; + } - // Escape metacharacters - // which is written down by users but means special for regular expressions. + if (value === ']' && (prev.value === '[' || prev.value === '[^')) { + value = `\\${value}`; + } - // > There are 12 characters with special meanings: - // > - the backslash \, - // > - the caret ^, - // > - the dollar sign $, - // > - the period or dot ., - // > - the vertical bar or pipe symbol |, - // > - the question mark ?, - // > - the asterisk or star *, - // > - the plus sign +, - // > - the opening parenthesis (, - // > - the closing parenthesis ), - // > - and the opening square bracket [, - // > - the opening curly brace {, - // > These special characters are often called "metacharacters". - [ - /[\\$.|*+(){^]/g, - match => `\\${match}` - ], + if (opts.posix === true && value === '!' && prev.value === '[') { + value = '^'; + } - [ - // > a question mark (?) matches a single character - /(?!\\)\?/g, - () => '[^/]' - ], + prev.value += value; + append({ value }); + continue; + } - // leading slash - [ + /** + * If we're inside a quoted string, continue + * until we reach the closing double quote. + */ - // > A leading slash matches the beginning of the pathname. - // > For example, "/*.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c". - // A leading slash matches the beginning of the pathname - /^\//, - () => '^' - ], - - // replace special metacharacter slash after the leading slash - [ - /\//g, - () => '\\/' - ], - - [ - // > A leading "**" followed by a slash means match in all directories. - // > For example, "**/foo" matches file or directory "foo" anywhere, - // > the same as pattern "foo". - // > "**/foo/bar" matches file or directory "bar" anywhere that is directly - // > under directory "foo". - // Notice that the '*'s have been replaced as '\\*' - /^\^*\\\*\\\*\\\//, + if (state.quotes === 1 && value !== '"') { + value = utils.escapeRegex(value); + prev.value += value; + append({ value }); + continue; + } - // '**/foo' <-> 'foo' - () => '^(?:.*\\/)?' - ], + /** + * Double quotes + */ - // starting - [ - // there will be no leading '/' - // (which has been replaced by section "leading slash") - // If starts with '**', adding a '^' to the regular expression also works - /^(?=[^^])/, - function startingReplacer () { - // If has a slash `/` at the beginning or middle - return !/\/(?!$)/.test(this) - // > Prior to 2.22.1 - // > If the pattern does not contain a slash /, - // > Git treats it as a shell glob pattern - // Actually, if there is only a trailing slash, - // git also treats it as a shell glob pattern + if (value === '"') { + state.quotes = state.quotes === 1 ? 0 : 1; + if (opts.keepQuotes === true) { + push({ type: 'text', value }); + } + continue; + } - // After 2.22.1 (compatible but clearer) - // > If there is a separator at the beginning or middle (or both) - // > of the pattern, then the pattern is relative to the directory - // > level of the particular .gitignore file itself. - // > Otherwise the pattern may also match at any level below - // > the .gitignore level. - ? '(?:^|\\/)' + /** + * Parentheses + */ - // > Otherwise, Git treats the pattern as a shell glob suitable for - // > consumption by fnmatch(3) - : '^' + if (value === '(') { + increment('parens'); + push({ type: 'paren', value }); + continue; } - ], - // two globstars - [ - // Use lookahead assertions so that we could match more than one `'/**'` - /\\\/\\\*\\\*(?=\\\/|$)/g, + if (value === ')') { + if (state.parens === 0 && opts.strictBrackets === true) { + throw new SyntaxError(syntaxError('opening', '(')); + } - // Zero, one or several directories - // should not use '*', or it will be replaced by the next replacer + const extglob = extglobs[extglobs.length - 1]; + if (extglob && state.parens === extglob.parens + 1) { + extglobClose(extglobs.pop()); + continue; + } - // Check if it is not the last `'/**'` - (_, index, str) => index + 6 < str.length + push({ type: 'paren', value, output: state.parens ? ')' : '\\)' }); + decrement('parens'); + continue; + } - // case: /**/ - // > A slash followed by two consecutive asterisks then a slash matches - // > zero or more directories. - // > For example, "a/**/b" matches "a/b", "a/x/b", "a/x/y/b" and so on. - // '/**/' - ? '(?:\\/[^\\/]+)*' + /** + * Square brackets + */ - // case: /** - // > A trailing `"/**"` matches everything inside. + if (value === '[') { + if (opts.nobracket === true || !remaining().includes(']')) { + if (opts.nobracket !== true && opts.strictBrackets === true) { + throw new SyntaxError(syntaxError('closing', ']')); + } - // #21: everything inside but it should not include the current folder - : '\\/.+' - ], + value = `\\${value}`; + } else { + increment('brackets'); + } - // intermediate wildcards - [ - // Never replace escaped '*' - // ignore rule '\*' will match the path '*' + push({ type: 'bracket', value }); + continue; + } - // 'abc.*/' -> go - // 'abc.*' -> skip this rule - /(^|[^\\]+)\\\*(?=.+)/g, + if (value === ']') { + if (opts.nobracket === true || (prev && prev.type === 'bracket' && prev.value.length === 1)) { + push({ type: 'text', value, output: `\\${value}` }); + continue; + } - // '*.js' matches '.js' - // '*.js' doesn't match 'abc' - (_, p1) => `${p1}[^\\/]*` - ], + if (state.brackets === 0) { + if (opts.strictBrackets === true) { + throw new SyntaxError(syntaxError('opening', '[')); + } - [ - // unescape, revert step 3 except for back slash - // For example, if a user escape a '\\*', - // after step 3, the result will be '\\\\\\*' - /\\\\\\(?=[$.|*+(){^])/g, - () => ESCAPE - ], + push({ type: 'text', value, output: `\\${value}` }); + continue; + } - [ - // '\\\\' -> '\\' - /\\\\/g, - () => ESCAPE - ], + decrement('brackets'); - [ - // > The range notation, e.g. [a-zA-Z], - // > can be used to match one of the characters in a range. + const prevValue = prev.value.slice(1); + if (prev.posix !== true && prevValue[0] === '^' && !prevValue.includes('/')) { + value = `/${value}`; + } - // `\` is escaped by step 3 - /(\\)?\[([^\]/]*?)(\\*)($|\])/g, - (match, leadEscape, range, endEscape, close) => leadEscape === ESCAPE - // '\\[bar]' -> '\\\\[bar\\]' - ? `\\[${range}${cleanRangeBackSlash(endEscape)}${close}` - : close === ']' - ? endEscape.length % 2 === 0 - // A normal case, and it is a range notation - // '[bar]' - // '[bar\\\\]' - ? `[${sanitizeRange(range)}${endEscape}]` - // Invalid range notaton - // '[bar\\]' -> '[bar\\\\]' - : '[]' - : '[]' - ], + prev.value += value; + append({ value }); - // ending - [ - // 'js' will not match 'js.' - // 'ab' will not match 'abc' - /(?:[^*])$/, + // when literal brackets are explicitly disabled + // assume we should match with a regex character class + if (opts.literalBrackets === false || utils.hasRegexChars(prevValue)) { + continue; + } - // WTF! - // https://git-scm.com/docs/gitignore - // changes in [2.22.1](https://git-scm.com/docs/gitignore/2.22.1) - // which re-fixes #24, #38 + const escaped = utils.escapeRegex(prev.value); + state.output = state.output.slice(0, -prev.value.length); - // > If there is a separator at the end of the pattern then the pattern - // > will only match directories, otherwise the pattern can match both - // > files and directories. + // when literal brackets are explicitly enabled + // assume we should escape the brackets to match literal characters + if (opts.literalBrackets === true) { + state.output += escaped; + prev.value = escaped; + continue; + } - // 'js*' will not match 'a.js' - // 'js/' will not match 'a.js' - // 'js' will match 'a.js' and 'a.js/' - match => /\/$/.test(match) - // foo/ will not match 'foo' - ? `${match}$` - // foo matches 'foo' and 'foo/' - : `${match}(?=$|\\/$)` - ], + // when the user specifies nothing, try to match both + prev.value = `(${capture}${escaped}|${prev.value})`; + state.output += prev.value; + continue; + } - // trailing wildcard - [ - /(\^|\\\/)?\\\*$/, - (_, p1) => { - const prefix = p1 - // '\^': - // '/*' does not match EMPTY - // '/*' does not match everything + /** + * Braces + */ - // '\\\/': - // 'abc/*' does not match 'abc/' - ? `${p1}[^/]+` + if (value === '{' && opts.nobrace !== true) { + increment('braces'); - // 'a*' matches 'a' - // 'a*' matches 'aa' - : '[^/]*' + const open = { + type: 'brace', + value, + output: '(', + outputIndex: state.output.length, + tokensIndex: state.tokens.length + }; - return `${prefix}(?=$|\\/$)` + braces.push(open); + push(open); + continue; } - ], -] -// A simple cache, because an ignore rule only has only one certain meaning -const regexCache = Object.create(null) + if (value === '}') { + const brace = braces[braces.length - 1]; -// @param {pattern} -const makeRegex = (pattern, negative, ignorecase) => { - const r = regexCache[pattern] - if (r) { - return r - } + if (opts.nobrace === true || !brace) { + push({ type: 'text', value, output: value }); + continue; + } - // const replacers = negative - // ? NEGATIVE_REPLACERS - // : POSITIVE_REPLACERS + let output = ')'; - const source = REPLACERS.reduce( - (prev, current) => prev.replace(current[0], current[1].bind(pattern)), - pattern - ) + if (brace.dots === true) { + const arr = tokens.slice(); + const range = []; - return regexCache[pattern] = ignorecase - ? new RegExp(source, 'i') - : new RegExp(source) -} + for (let i = arr.length - 1; i >= 0; i--) { + tokens.pop(); + if (arr[i].type === 'brace') { + break; + } + if (arr[i].type !== 'dots') { + range.unshift(arr[i].value); + } + } -const isString = subject => typeof subject === 'string' + output = expandRange(range, opts); + state.backtrack = true; + } -// > A blank line matches no files, so it can serve as a separator for readability. -const checkPattern = pattern => pattern - && isString(pattern) - && !REGEX_TEST_BLANK_LINE.test(pattern) + if (brace.comma !== true && brace.dots !== true) { + const out = state.output.slice(0, brace.outputIndex); + const toks = state.tokens.slice(brace.tokensIndex); + brace.value = brace.output = '\\{'; + value = output = '\\}'; + state.output = out; + for (const t of toks) { + state.output += (t.output || t.value); + } + } - // > A line starting with # serves as a comment. - && pattern.indexOf('#') !== 0 + push({ type: 'brace', value, output }); + decrement('braces'); + braces.pop(); + continue; + } -const splitPattern = pattern => pattern.split(REGEX_SPLITALL_CRLF) + /** + * Pipes + */ -class IgnoreRule { - constructor ( - origin, - pattern, - negative, - regex - ) { - this.origin = origin - this.pattern = pattern - this.negative = negative - this.regex = regex - } -} + if (value === '|') { + if (extglobs.length > 0) { + extglobs[extglobs.length - 1].conditions++; + } + push({ type: 'text', value }); + continue; + } -const createRule = (pattern, ignorecase) => { - const origin = pattern - let negative = false + /** + * Commas + */ - // > An optional prefix "!" which negates the pattern; - if (pattern.indexOf('!') === 0) { - negative = true - pattern = pattern.substr(1) - } + if (value === ',') { + let output = value; - pattern = pattern - // > Put a backslash ("\") in front of the first "!" for patterns that - // > begin with a literal "!", for example, `"\!important!.txt"`. - .replace(REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION, '!') - // > Put a backslash ("\") in front of the first hash for patterns that - // > begin with a hash. - .replace(REGEX_REPLACE_LEADING_EXCAPED_HASH, '#') + const brace = braces[braces.length - 1]; + if (brace && stack[stack.length - 1] === 'braces') { + brace.comma = true; + output = '|'; + } - const regex = makeRegex(pattern, negative, ignorecase) + push({ type: 'comma', value, output }); + continue; + } - return new IgnoreRule( - origin, - pattern, - negative, - regex - ) -} + /** + * Slashes + */ -const throwError = (message, Ctor) => { - throw new Ctor(message) -} + if (value === '/') { + // if the beginning of the glob is "./", advance the start + // to the current index, and don't add the "./" characters + // to the state. This greatly simplifies lookbehinds when + // checking for BOS characters like "!" and "." (not "./") + if (prev.type === 'dot' && state.index === state.start + 1) { + state.start = state.index + 1; + state.consumed = ''; + state.output = ''; + tokens.pop(); + prev = bos; // reset "prev" to the first token + continue; + } -const checkPath = (path, originalPath, doThrow) => { - if (!isString(path)) { - return doThrow( - `path must be a string, but got \`${originalPath}\``, - TypeError - ) - } + push({ type: 'slash', value, output: SLASH_LITERAL }); + continue; + } - // We don't know if we should ignore EMPTY, so throw - if (!path) { - return doThrow(`path must not be empty`, TypeError) - } + /** + * Dots + */ - // Check if it is a relative path - if (checkPath.isNotRelative(path)) { - const r = '`path.relative()`d' - return doThrow( - `path should be a ${r} string, but got "${originalPath}"`, - RangeError - ) - } + if (value === '.') { + if (state.braces > 0 && prev.type === 'dot') { + if (prev.value === '.') prev.output = DOT_LITERAL; + const brace = braces[braces.length - 1]; + prev.type = 'dots'; + prev.output += value; + prev.value += value; + brace.dots = true; + continue; + } - return true -} + if ((state.braces + state.parens) === 0 && prev.type !== 'bos' && prev.type !== 'slash') { + push({ type: 'text', value, output: DOT_LITERAL }); + continue; + } -const isNotRelative = path => REGEX_TEST_INVALID_PATH.test(path) + push({ type: 'dot', value, output: DOT_LITERAL }); + continue; + } -checkPath.isNotRelative = isNotRelative -checkPath.convert = p => p + /** + * Question marks + */ -class Ignore { - constructor ({ - ignorecase = true - } = {}) { - this._rules = [] - this._ignorecase = ignorecase - define(this, KEY_IGNORE, true) - this._initCache() - } + if (value === '?') { + const isGroup = prev && prev.value === '('; + if (!isGroup && opts.noextglob !== true && peek() === '(' && peek(2) !== '?') { + extglobOpen('qmark', value); + continue; + } - _initCache () { - this._ignoreCache = Object.create(null) - this._testCache = Object.create(null) - } + if (prev && prev.type === 'paren') { + const next = peek(); + let output = value; - _addPattern (pattern) { - // #32 - if (pattern && pattern[KEY_IGNORE]) { - this._rules = this._rules.concat(pattern._rules) - this._added = true - return - } + if (next === '<' && !utils.supportsLookbehinds()) { + throw new Error('Node.js v10 or higher is required for regex lookbehinds'); + } - if (checkPattern(pattern)) { - const rule = createRule(pattern, this._ignorecase) - this._added = true - this._rules.push(rule) - } - } + if ((prev.value === '(' && !/[!=<:]/.test(next)) || (next === '<' && !/<([!=]|\w+>)/.test(remaining()))) { + output = `\\${value}`; + } - // @param {Array | string | Ignore} pattern - add (pattern) { - this._added = false + push({ type: 'text', value, output }); + continue; + } - makeArray( - isString(pattern) - ? splitPattern(pattern) - : pattern - ).forEach(this._addPattern, this) + if (opts.dot !== true && (prev.type === 'slash' || prev.type === 'bos')) { + push({ type: 'qmark', value, output: QMARK_NO_DOT }); + continue; + } - // Some rules have just added to the ignore, - // making the behavior changed. - if (this._added) { - this._initCache() + push({ type: 'qmark', value, output: QMARK }); + continue; } - return this - } + /** + * Exclamation + */ - // legacy - addPattern (pattern) { - return this.add(pattern) - } + if (value === '!') { + if (opts.noextglob !== true && peek() === '(') { + if (peek(2) !== '?' || !/[!=<:]/.test(peek(3))) { + extglobOpen('negate', value); + continue; + } + } - // | ignored : unignored - // negative | 0:0 | 0:1 | 1:0 | 1:1 - // -------- | ------- | ------- | ------- | -------- - // 0 | TEST | TEST | SKIP | X - // 1 | TESTIF | SKIP | TEST | X + if (opts.nonegate !== true && state.index === 0) { + negate(); + continue; + } + } - // - SKIP: always skip - // - TEST: always test - // - TESTIF: only test if checkUnignored - // - X: that never happen + /** + * Plus + */ - // @param {boolean} whether should check if the path is unignored, - // setting `checkUnignored` to `false` could reduce additional - // path matching. + if (value === '+') { + if (opts.noextglob !== true && peek() === '(' && peek(2) !== '?') { + extglobOpen('plus', value); + continue; + } - // @returns {TestResult} true if a file is ignored - _testOne (path, checkUnignored) { - let ignored = false - let unignored = false + if ((prev && prev.value === '(') || opts.regex === false) { + push({ type: 'plus', value, output: PLUS_LITERAL }); + continue; + } - this._rules.forEach(rule => { - const {negative} = rule - if ( - unignored === negative && ignored !== unignored - || negative && !ignored && !unignored && !checkUnignored - ) { - return + if ((prev && (prev.type === 'bracket' || prev.type === 'paren' || prev.type === 'brace')) || state.parens > 0) { + push({ type: 'plus', value }); + continue; } - const matched = rule.regex.test(path) + push({ type: 'plus', value: PLUS_LITERAL }); + continue; + } - if (matched) { - ignored = !negative - unignored = negative + /** + * Plain text + */ + + if (value === '@') { + if (opts.noextglob !== true && peek() === '(' && peek(2) !== '?') { + push({ type: 'at', extglob: true, value, output: '' }); + continue; } - }) - return { - ignored, - unignored + push({ type: 'text', value }); + continue; } - } - // @returns {TestResult} - _test (originalPath, cache, checkUnignored, slices) { - const path = originalPath - // Supports nullable path - && checkPath.convert(originalPath) + /** + * Plain text + */ - checkPath(path, originalPath, throwError) + if (value !== '*') { + if (value === '$' || value === '^') { + value = `\\${value}`; + } - return this._t(path, cache, checkUnignored, slices) - } + const match = REGEX_NON_SPECIAL_CHARS.exec(remaining()); + if (match) { + value += match[0]; + state.index += match[0].length; + } - _t (path, cache, checkUnignored, slices) { - if (path in cache) { - return cache[path] + push({ type: 'text', value }); + continue; } - if (!slices) { - // path/to/a.js - // ['path', 'to', 'a.js'] - slices = path.split(SLASH) - } + /** + * Stars + */ - slices.pop() + if (prev && (prev.type === 'globstar' || prev.star === true)) { + prev.type = 'star'; + prev.star = true; + prev.value += value; + prev.output = star; + state.backtrack = true; + state.globstar = true; + consume(value); + continue; + } - // If the path has no parent directory, just test it - if (!slices.length) { - return cache[path] = this._testOne(path, checkUnignored) + let rest = remaining(); + if (opts.noextglob !== true && /^\([^?]/.test(rest)) { + extglobOpen('star', value); + continue; } - const parent = this._t( - slices.join(SLASH) + SLASH, - cache, - checkUnignored, - slices - ) + if (prev.type === 'star') { + if (opts.noglobstar === true) { + consume(value); + continue; + } - // If the path contains a parent directory, check the parent first - return cache[path] = parent.ignored - // > It is not possible to re-include a file if a parent directory of - // > that file is excluded. - ? parent - : this._testOne(path, checkUnignored) - } + const prior = prev.prev; + const before = prior.prev; + const isStart = prior.type === 'slash' || prior.type === 'bos'; + const afterStar = before && (before.type === 'star' || before.type === 'globstar'); - ignores (path) { - return this._test(path, this._ignoreCache, false).ignored - } + if (opts.bash === true && (!isStart || (rest[0] && rest[0] !== '/'))) { + push({ type: 'star', value, output: '' }); + continue; + } - createFilter () { - return path => !this.ignores(path) - } + const isBrace = state.braces > 0 && (prior.type === 'comma' || prior.type === 'brace'); + const isExtglob = extglobs.length && (prior.type === 'pipe' || prior.type === 'paren'); + if (!isStart && prior.type !== 'paren' && !isBrace && !isExtglob) { + push({ type: 'star', value, output: '' }); + continue; + } - filter (paths) { - return makeArray(paths).filter(this.createFilter()) - } + // strip consecutive `/**/` + while (rest.slice(0, 3) === '/**') { + const after = input[state.index + 4]; + if (after && after !== '/') { + break; + } + rest = rest.slice(3); + consume('/**', 3); + } - // @returns {TestResult} - test (path) { - return this._test(path, this._testCache, true) - } -} + if (prior.type === 'bos' && eos()) { + prev.type = 'globstar'; + prev.value += value; + prev.output = globstar(opts); + state.output = prev.output; + state.globstar = true; + consume(value); + continue; + } -const factory = options => new Ignore(options) + if (prior.type === 'slash' && prior.prev.type !== 'bos' && !afterStar && eos()) { + state.output = state.output.slice(0, -(prior.output + prev.output).length); + prior.output = `(?:${prior.output}`; -const returnFalse = () => false + prev.type = 'globstar'; + prev.output = globstar(opts) + (opts.strictSlashes ? ')' : '|$)'); + prev.value += value; + state.globstar = true; + state.output += prior.output + prev.output; + consume(value); + continue; + } -const isPathValid = path => - checkPath(path && checkPath.convert(path), path, returnFalse) + if (prior.type === 'slash' && prior.prev.type !== 'bos' && rest[0] === '/') { + const end = rest[1] !== void 0 ? '|$' : ''; -factory.isPathValid = isPathValid + state.output = state.output.slice(0, -(prior.output + prev.output).length); + prior.output = `(?:${prior.output}`; -// Fixes typescript -factory.default = factory + prev.type = 'globstar'; + prev.output = `${globstar(opts)}${SLASH_LITERAL}|${SLASH_LITERAL}${end})`; + prev.value += value; -module.exports = factory + state.output += prior.output + prev.output; + state.globstar = true; -// Windows -// -------------------------------------------------------------- -/* istanbul ignore if */ -if ( - // Detect `process` so that it can run in browsers. - typeof process !== 'undefined' - && ( - process.env && process.env.IGNORE_TEST_WIN32 - || process.platform === 'win32' - ) -) { - /* eslint no-control-regex: "off" */ - const makePosix = str => /^\\\\\?\\/.test(str) - || /["<>|\u0000-\u001F]+/u.test(str) - ? str - : str.replace(/\\/g, '/') + consume(value + advance()); - checkPath.convert = makePosix + push({ type: 'slash', value: '/', output: '' }); + continue; + } - // 'C:\\foo' <- 'C:\\foo' has been converted to 'C:/' - // 'd:\\foo' - const REGIX_IS_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i - checkPath.isNotRelative = path => - REGIX_IS_WINDOWS_PATH_ABSOLUTE.test(path) - || isNotRelative(path) -} + if (prior.type === 'bos' && rest[0] === '/') { + prev.type = 'globstar'; + prev.value += value; + prev.output = `(?:^|${SLASH_LITERAL}|${globstar(opts)}${SLASH_LITERAL})`; + state.output = prev.output; + state.globstar = true; + consume(value + advance()); + push({ type: 'slash', value: '/', output: '' }); + continue; + } + + // remove single star from output + state.output = state.output.slice(0, -prev.output.length); + // reset previous token to globstar + prev.type = 'globstar'; + prev.output = globstar(opts); + prev.value += value; -/***/ }), -/* 330 */ -/***/ (function(module, exports, __webpack_require__) { + // reset output with globstar + state.output += prev.output; + state.globstar = true; + consume(value); + continue; + } -"use strict"; + const token = { type: 'star', value, output: star }; -module.exports = path => { - const isExtendedLengthPath = /^\\\\\?\\/.test(path); - const hasNonAscii = /[^\u0000-\u0080]+/.test(path); // eslint-disable-line no-control-regex + if (opts.bash === true) { + token.output = '.*?'; + if (prev.type === 'bos' || prev.type === 'slash') { + token.output = nodot + token.output; + } + push(token); + continue; + } - if (isExtendedLengthPath || hasNonAscii) { - return path; - } + if (prev && (prev.type === 'bracket' || prev.type === 'paren') && opts.regex === true) { + token.output = value; + push(token); + continue; + } - return path.replace(/\\/g, '/'); -}; + if (state.index === state.start || prev.type === 'slash' || prev.type === 'dot') { + if (prev.type === 'dot') { + state.output += NO_DOT_SLASH; + prev.output += NO_DOT_SLASH; + } else if (opts.dot === true) { + state.output += NO_DOTS_SLASH; + prev.output += NO_DOTS_SLASH; -/***/ }), -/* 331 */ -/***/ (function(module, exports, __webpack_require__) { + } else { + state.output += nodot; + prev.output += nodot; + } -"use strict"; + if (peek() !== '*') { + state.output += ONE_CHAR; + prev.output += ONE_CHAR; + } + } -const {Transform} = __webpack_require__(173); + push(token); + } -class ObjectTransform extends Transform { - constructor() { - super({ - objectMode: true - }); - } -} + while (state.brackets > 0) { + if (opts.strictBrackets === true) throw new SyntaxError(syntaxError('closing', ']')); + state.output = utils.escapeLast(state.output, '['); + decrement('brackets'); + } -class FilterStream extends ObjectTransform { - constructor(filter) { - super(); - this._filter = filter; - } + while (state.parens > 0) { + if (opts.strictBrackets === true) throw new SyntaxError(syntaxError('closing', ')')); + state.output = utils.escapeLast(state.output, '('); + decrement('parens'); + } - _transform(data, encoding, callback) { - if (this._filter(data)) { - this.push(data); - } + while (state.braces > 0) { + if (opts.strictBrackets === true) throw new SyntaxError(syntaxError('closing', '}')); + state.output = utils.escapeLast(state.output, '{'); + decrement('braces'); + } - callback(); - } -} + if (opts.strictSlashes !== true && (prev.type === 'star' || prev.type === 'bracket')) { + push({ type: 'maybe_slash', value: '', output: `${SLASH_LITERAL}?` }); + } -class UniqueStream extends ObjectTransform { - constructor() { - super(); - this._pushed = new Set(); - } + // rebuild the output if we had to backtrack at any point + if (state.backtrack === true) { + state.output = ''; - _transform(data, encoding, callback) { - if (!this._pushed.has(data)) { - this.push(data); - this._pushed.add(data); - } + for (const token of state.tokens) { + state.output += token.output != null ? token.output : token.value; - callback(); - } -} + if (token.suffix) { + state.output += token.suffix; + } + } + } -module.exports = { - FilterStream, - UniqueStream + return state; }; +/** + * Fast paths for creating regular expressions for common glob patterns. + * This can significantly speed up processing and has very little downside + * impact when none of the fast paths match. + */ -/***/ }), -/* 332 */ -/***/ (function(module, exports, __webpack_require__) { +parse.fastpaths = (input, options) => { + const opts = { ...options }; + const max = typeof opts.maxLength === 'number' ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH; + const len = input.length; + if (len > max) { + throw new SyntaxError(`Input length: ${len}, exceeds maximum allowed length: ${max}`); + } -"use strict"; + input = REPLACEMENTS[input] || input; + const win32 = utils.isWindows(options); -const path = __webpack_require__(4); + // create constants based on platform, for windows or posix + const { + DOT_LITERAL, + SLASH_LITERAL, + ONE_CHAR, + DOTS_SLASH, + NO_DOT, + NO_DOTS, + NO_DOTS_SLASH, + STAR, + START_ANCHOR + } = constants.globChars(win32); -module.exports = path_ => { - let cwd = process.cwd(); + const nodot = opts.dot ? NO_DOTS : NO_DOT; + const slashDot = opts.dot ? NO_DOTS_SLASH : NO_DOT; + const capture = opts.capture ? '' : '?:'; + const state = { negated: false, prefix: '' }; + let star = opts.bash === true ? '.*?' : STAR; - path_ = path.resolve(path_); + if (opts.capture) { + star = `(${star})`; + } - if (process.platform === 'win32') { - cwd = cwd.toLowerCase(); - path_ = path_.toLowerCase(); - } + const globstar = (opts) => { + if (opts.noglobstar === true) return star; + return `(${capture}(?:(?!${START_ANCHOR}${opts.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`; + }; - return path_ === cwd; -}; + const create = str => { + switch (str) { + case '*': + return `${nodot}${ONE_CHAR}${star}`; + case '.*': + return `${DOT_LITERAL}${ONE_CHAR}${star}`; -/***/ }), -/* 333 */ -/***/ (function(module, exports, __webpack_require__) { + case '*.*': + return `${nodot}${star}${DOT_LITERAL}${ONE_CHAR}${star}`; -"use strict"; + case '*/*': + return `${nodot}${star}${SLASH_LITERAL}${ONE_CHAR}${slashDot}${star}`; -const path = __webpack_require__(4); + case '**': + return nodot + globstar(opts); -module.exports = (childPath, parentPath) => { - childPath = path.resolve(childPath); - parentPath = path.resolve(parentPath); + case '**/*': + return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${slashDot}${ONE_CHAR}${star}`; - if (process.platform === 'win32') { - childPath = childPath.toLowerCase(); - parentPath = parentPath.toLowerCase(); - } + case '**/*.*': + return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${slashDot}${star}${DOT_LITERAL}${ONE_CHAR}${star}`; - if (childPath === parentPath) { - return false; - } + case '**/.*': + return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${DOT_LITERAL}${ONE_CHAR}${star}`; - childPath += path.sep; - parentPath += path.sep; + default: { + const match = /^(.*?)\.(\w+)$/.exec(str); + if (!match) return; - return childPath.startsWith(parentPath); -}; + const source = create(match[1]); + if (!source) return; + return source + DOT_LITERAL + match[2]; + } + } + }; -/***/ }), -/* 334 */ -/***/ (function(module, exports, __webpack_require__) { + const output = utils.removePrefix(input, state); + let source = create(output); -const assert = __webpack_require__(162) -const path = __webpack_require__(4) -const fs = __webpack_require__(132) -let glob = undefined -try { - glob = __webpack_require__(244) -} catch (_err) { - // treat glob as optional. -} + if (source && opts.strictSlashes !== true) { + source += `${SLASH_LITERAL}?`; + } -const defaultGlobOpts = { - nosort: true, - silent: true -} + return source; +}; -// for EMFILE handling -let timeout = 0 +module.exports = parse; -const isWindows = (process.platform === "win32") -const defaults = options => { - const methods = [ - 'unlink', - 'chmod', - 'stat', - 'lstat', - 'rmdir', - 'readdir' - ] - methods.forEach(m => { - options[m] = options[m] || fs[m] - m = m + 'Sync' - options[m] = options[m] || fs[m] - }) +/***/ }), +/* 291 */ +/***/ (function(module, exports, __webpack_require__) { - options.maxBusyTries = options.maxBusyTries || 3 - options.emfileWait = options.emfileWait || 1000 - if (options.glob === false) { - options.disableGlob = true - } - if (options.disableGlob !== true && glob === undefined) { - throw Error('glob dependency not found, set `options.disableGlob = true` if intentional') - } - options.disableGlob = options.disableGlob || false - options.glob = options.glob || defaultGlobOpts -} +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.merge = void 0; +const merge2 = __webpack_require__(243); +function merge(streams) { + const mergedStream = merge2(streams); + streams.forEach((stream) => { + stream.once('error', (error) => mergedStream.emit('error', error)); + }); + mergedStream.once('close', () => propagateCloseEventToSources(streams)); + mergedStream.once('end', () => propagateCloseEventToSources(streams)); + return mergedStream; +} +exports.merge = merge; +function propagateCloseEventToSources(streams) { + streams.forEach((stream) => stream.emit('close')); +} -const rimraf = (p, options, cb) => { - if (typeof options === 'function') { - cb = options - options = {} - } - assert(p, 'rimraf: missing path') - assert.equal(typeof p, 'string', 'rimraf: path should be a string') - assert.equal(typeof cb, 'function', 'rimraf: callback function required') - assert(options, 'rimraf: invalid options argument provided') - assert.equal(typeof options, 'object', 'rimraf: options should be object') +/***/ }), +/* 292 */ +/***/ (function(module, exports, __webpack_require__) { - defaults(options) +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.isEmpty = exports.isString = void 0; +function isString(input) { + return typeof input === 'string'; +} +exports.isString = isString; +function isEmpty(input) { + return input === ''; +} +exports.isEmpty = isEmpty; - let busyTries = 0 - let errState = null - let n = 0 - const next = (er) => { - errState = errState || er - if (--n === 0) - cb(errState) - } +/***/ }), +/* 293 */ +/***/ (function(module, exports, __webpack_require__) { - const afterGlob = (er, results) => { - if (er) - return cb(er) +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const stream_1 = __webpack_require__(294); +const provider_1 = __webpack_require__(321); +class ProviderAsync extends provider_1.default { + constructor() { + super(...arguments); + this._reader = new stream_1.default(this._settings); + } + read(task) { + const root = this._getRootDirectory(task); + const options = this._getReaderOptions(task); + const entries = []; + return new Promise((resolve, reject) => { + const stream = this.api(root, task, options); + stream.once('error', reject); + stream.on('data', (entry) => entries.push(options.transform(entry))); + stream.once('end', () => resolve(entries)); + }); + } + api(root, task, options) { + if (task.dynamic) { + return this._reader.dynamic(root, options); + } + return this._reader.static(task.patterns, options); + } +} +exports.default = ProviderAsync; - n = results.length - if (n === 0) - return cb() - results.forEach(p => { - const CB = (er) => { - if (er) { - if ((er.code === "EBUSY" || er.code === "ENOTEMPTY" || er.code === "EPERM") && - busyTries < options.maxBusyTries) { - busyTries ++ - // try again, with the same exact callback as this one. - return setTimeout(() => rimraf_(p, options, CB), busyTries * 100) - } +/***/ }), +/* 294 */ +/***/ (function(module, exports, __webpack_require__) { - // this one won't happen if graceful-fs is used. - if (er.code === "EMFILE" && timeout < options.emfileWait) { - return setTimeout(() => rimraf_(p, options, CB), timeout ++) - } +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const stream_1 = __webpack_require__(173); +const fsStat = __webpack_require__(295); +const fsWalk = __webpack_require__(300); +const reader_1 = __webpack_require__(320); +class ReaderStream extends reader_1.default { + constructor() { + super(...arguments); + this._walkStream = fsWalk.walkStream; + this._stat = fsStat.stat; + } + dynamic(root, options) { + return this._walkStream(root, options); + } + static(patterns, options) { + const filepaths = patterns.map(this._getFullEntryPath, this); + const stream = new stream_1.PassThrough({ objectMode: true }); + stream._write = (index, _enc, done) => { + return this._getEntry(filepaths[index], patterns[index], options) + .then((entry) => { + if (entry !== null && options.entryFilter(entry)) { + stream.push(entry); + } + if (index === filepaths.length - 1) { + stream.end(); + } + done(); + }) + .catch(done); + }; + for (let i = 0; i < filepaths.length; i++) { + stream.write(i); + } + return stream; + } + _getEntry(filepath, pattern, options) { + return this._getStat(filepath) + .then((stats) => this._makeEntry(stats, pattern)) + .catch((error) => { + if (options.errorFilter(error)) { + return null; + } + throw error; + }); + } + _getStat(filepath) { + return new Promise((resolve, reject) => { + this._stat(filepath, this._fsStatSettings, (error, stats) => { + return error === null ? resolve(stats) : reject(error); + }); + }); + } +} +exports.default = ReaderStream; - // already gone - if (er.code === "ENOENT") er = null - } - timeout = 0 - next(er) - } - rimraf_(p, options, CB) - }) - } +/***/ }), +/* 295 */ +/***/ (function(module, exports, __webpack_require__) { - if (options.disableGlob || !glob.hasMagic(p)) - return afterGlob(null, [p]) +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const async = __webpack_require__(296); +const sync = __webpack_require__(297); +const settings_1 = __webpack_require__(298); +exports.Settings = settings_1.default; +function stat(path, optionsOrSettingsOrCallback, callback) { + if (typeof optionsOrSettingsOrCallback === 'function') { + return async.read(path, getSettings(), optionsOrSettingsOrCallback); + } + async.read(path, getSettings(optionsOrSettingsOrCallback), callback); +} +exports.stat = stat; +function statSync(path, optionsOrSettings) { + const settings = getSettings(optionsOrSettings); + return sync.read(path, settings); +} +exports.statSync = statSync; +function getSettings(settingsOrOptions = {}) { + if (settingsOrOptions instanceof settings_1.default) { + return settingsOrOptions; + } + return new settings_1.default(settingsOrOptions); +} - options.lstat(p, (er, stat) => { - if (!er) - return afterGlob(null, [p]) - glob(p, options.glob, afterGlob) - }) +/***/ }), +/* 296 */ +/***/ (function(module, exports, __webpack_require__) { -} +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +function read(path, settings, callback) { + settings.fs.lstat(path, (lstatError, lstat) => { + if (lstatError !== null) { + return callFailureCallback(callback, lstatError); + } + if (!lstat.isSymbolicLink() || !settings.followSymbolicLink) { + return callSuccessCallback(callback, lstat); + } + settings.fs.stat(path, (statError, stat) => { + if (statError !== null) { + if (settings.throwErrorOnBrokenSymbolicLink) { + return callFailureCallback(callback, statError); + } + return callSuccessCallback(callback, lstat); + } + if (settings.markSymbolicLink) { + stat.isSymbolicLink = () => true; + } + callSuccessCallback(callback, stat); + }); + }); +} +exports.read = read; +function callFailureCallback(callback, error) { + callback(error); +} +function callSuccessCallback(callback, result) { + callback(null, result); +} -// Two possible strategies. -// 1. Assume it's a file. unlink it, then do the dir stuff on EPERM or EISDIR -// 2. Assume it's a directory. readdir, then do the file stuff on ENOTDIR -// -// Both result in an extra syscall when you guess wrong. However, there -// are likely far more normal files in the world than directories. This -// is based on the assumption that a the average number of files per -// directory is >= 1. -// -// If anyone ever complains about this, then I guess the strategy could -// be made configurable somehow. But until then, YAGNI. -const rimraf_ = (p, options, cb) => { - assert(p) - assert(options) - assert(typeof cb === 'function') - // sunos lets the root user unlink directories, which is... weird. - // so we have to lstat here and make sure it's not a dir. - options.lstat(p, (er, st) => { - if (er && er.code === "ENOENT") - return cb(null) +/***/ }), +/* 297 */ +/***/ (function(module, exports, __webpack_require__) { - // Windows can EPERM on stat. Life is suffering. - if (er && er.code === "EPERM" && isWindows) - fixWinEPERM(p, options, er, cb) +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +function read(path, settings) { + const lstat = settings.fs.lstatSync(path); + if (!lstat.isSymbolicLink() || !settings.followSymbolicLink) { + return lstat; + } + try { + const stat = settings.fs.statSync(path); + if (settings.markSymbolicLink) { + stat.isSymbolicLink = () => true; + } + return stat; + } + catch (error) { + if (!settings.throwErrorOnBrokenSymbolicLink) { + return lstat; + } + throw error; + } +} +exports.read = read; - if (st && st.isDirectory()) - return rmdir(p, options, er, cb) - options.unlink(p, er => { - if (er) { - if (er.code === "ENOENT") - return cb(null) - if (er.code === "EPERM") - return (isWindows) - ? fixWinEPERM(p, options, er, cb) - : rmdir(p, options, er, cb) - if (er.code === "EISDIR") - return rmdir(p, options, er, cb) - } - return cb(er) - }) - }) -} +/***/ }), +/* 298 */ +/***/ (function(module, exports, __webpack_require__) { -const fixWinEPERM = (p, options, er, cb) => { - assert(p) - assert(options) - assert(typeof cb === 'function') +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const fs = __webpack_require__(299); +class Settings { + constructor(_options = {}) { + this._options = _options; + this.followSymbolicLink = this._getValue(this._options.followSymbolicLink, true); + this.fs = fs.createFileSystemAdapter(this._options.fs); + this.markSymbolicLink = this._getValue(this._options.markSymbolicLink, false); + this.throwErrorOnBrokenSymbolicLink = this._getValue(this._options.throwErrorOnBrokenSymbolicLink, true); + } + _getValue(option, value) { + return option === undefined ? value : option; + } +} +exports.default = Settings; - options.chmod(p, 0o666, er2 => { - if (er2) - cb(er2.code === "ENOENT" ? null : er) - else - options.stat(p, (er3, stats) => { - if (er3) - cb(er3.code === "ENOENT" ? null : er) - else if (stats.isDirectory()) - rmdir(p, options, er, cb) - else - options.unlink(p, cb) - }) - }) -} -const fixWinEPERMSync = (p, options, er) => { - assert(p) - assert(options) +/***/ }), +/* 299 */ +/***/ (function(module, exports, __webpack_require__) { - try { - options.chmodSync(p, 0o666) - } catch (er2) { - if (er2.code === "ENOENT") - return - else - throw er - } +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const fs = __webpack_require__(132); +exports.FILE_SYSTEM_ADAPTER = { + lstat: fs.lstat, + stat: fs.stat, + lstatSync: fs.lstatSync, + statSync: fs.statSync +}; +function createFileSystemAdapter(fsMethods) { + if (fsMethods === undefined) { + return exports.FILE_SYSTEM_ADAPTER; + } + return Object.assign(Object.assign({}, exports.FILE_SYSTEM_ADAPTER), fsMethods); +} +exports.createFileSystemAdapter = createFileSystemAdapter; - let stats - try { - stats = options.statSync(p) - } catch (er3) { - if (er3.code === "ENOENT") - return - else - throw er - } - if (stats.isDirectory()) - rmdirSync(p, options, er) - else - options.unlinkSync(p) -} +/***/ }), +/* 300 */ +/***/ (function(module, exports, __webpack_require__) { -const rmdir = (p, options, originalEr, cb) => { - assert(p) - assert(options) - assert(typeof cb === 'function') +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const async_1 = __webpack_require__(301); +const stream_1 = __webpack_require__(316); +const sync_1 = __webpack_require__(317); +const settings_1 = __webpack_require__(319); +exports.Settings = settings_1.default; +function walk(directory, optionsOrSettingsOrCallback, callback) { + if (typeof optionsOrSettingsOrCallback === 'function') { + return new async_1.default(directory, getSettings()).read(optionsOrSettingsOrCallback); + } + new async_1.default(directory, getSettings(optionsOrSettingsOrCallback)).read(callback); +} +exports.walk = walk; +function walkSync(directory, optionsOrSettings) { + const settings = getSettings(optionsOrSettings); + const provider = new sync_1.default(directory, settings); + return provider.read(); +} +exports.walkSync = walkSync; +function walkStream(directory, optionsOrSettings) { + const settings = getSettings(optionsOrSettings); + const provider = new stream_1.default(directory, settings); + return provider.read(); +} +exports.walkStream = walkStream; +function getSettings(settingsOrOptions = {}) { + if (settingsOrOptions instanceof settings_1.default) { + return settingsOrOptions; + } + return new settings_1.default(settingsOrOptions); +} - // try to rmdir first, and only readdir on ENOTEMPTY or EEXIST (SunOS) - // if we guessed wrong, and it's not a directory, then - // raise the original error. - options.rmdir(p, er => { - if (er && (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM")) - rmkids(p, options, cb) - else if (er && er.code === "ENOTDIR") - cb(originalEr) - else - cb(er) - }) -} -const rmkids = (p, options, cb) => { - assert(p) - assert(options) - assert(typeof cb === 'function') +/***/ }), +/* 301 */ +/***/ (function(module, exports, __webpack_require__) { - options.readdir(p, (er, files) => { - if (er) - return cb(er) - let n = files.length - if (n === 0) - return options.rmdir(p, cb) - let errState - files.forEach(f => { - rimraf(path.join(p, f), options, er => { - if (errState) - return - if (er) - return cb(errState = er) - if (--n === 0) - options.rmdir(p, cb) - }) - }) - }) -} +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const async_1 = __webpack_require__(302); +class AsyncProvider { + constructor(_root, _settings) { + this._root = _root; + this._settings = _settings; + this._reader = new async_1.default(this._root, this._settings); + this._storage = new Set(); + } + read(callback) { + this._reader.onError((error) => { + callFailureCallback(callback, error); + }); + this._reader.onEntry((entry) => { + this._storage.add(entry); + }); + this._reader.onEnd(() => { + callSuccessCallback(callback, [...this._storage]); + }); + this._reader.read(); + } +} +exports.default = AsyncProvider; +function callFailureCallback(callback, error) { + callback(error); +} +function callSuccessCallback(callback, entries) { + callback(null, entries); +} -// this looks simpler, and is strictly *faster*, but will -// tie up the JavaScript thread and fail on excessively -// deep directory trees. -const rimrafSync = (p, options) => { - options = options || {} - defaults(options) - - assert(p, 'rimraf: missing path') - assert.equal(typeof p, 'string', 'rimraf: path should be a string') - assert(options, 'rimraf: missing options') - assert.equal(typeof options, 'object', 'rimraf: options should be object') - - let results - - if (options.disableGlob || !glob.hasMagic(p)) { - results = [p] - } else { - try { - options.lstatSync(p) - results = [p] - } catch (er) { - results = glob.sync(p, options.glob) - } - } - - if (!results.length) - return - - for (let i = 0; i < results.length; i++) { - const p = results[i] - - let st - try { - st = options.lstatSync(p) - } catch (er) { - if (er.code === "ENOENT") - return - - // Windows can EPERM on stat. Life is suffering. - if (er.code === "EPERM" && isWindows) - fixWinEPERMSync(p, options, er) - } - - try { - // sunos lets the root user unlink directories, which is... weird. - if (st && st.isDirectory()) - rmdirSync(p, options, null) - else - options.unlinkSync(p) - } catch (er) { - if (er.code === "ENOENT") - return - if (er.code === "EPERM") - return isWindows ? fixWinEPERMSync(p, options, er) : rmdirSync(p, options, er) - if (er.code !== "EISDIR") - throw er - - rmdirSync(p, options, er) - } - } -} - -const rmdirSync = (p, options, originalEr) => { - assert(p) - assert(options) - - try { - options.rmdirSync(p) - } catch (er) { - if (er.code === "ENOENT") - return - if (er.code === "ENOTDIR") - throw originalEr - if (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM") - rmkidsSync(p, options) - } -} -const rmkidsSync = (p, options) => { - assert(p) - assert(options) - options.readdirSync(p).forEach(f => rimrafSync(path.join(p, f), options)) - - // We only end up here once we got ENOTEMPTY at least once, and - // at this point, we are guaranteed to have removed all the kids. - // So, we know that it won't be ENOENT or ENOTDIR or anything else. - // try really hard to delete stuff on windows, because it has a - // PROFOUNDLY annoying habit of not closing handles promptly when - // files are deleted, resulting in spurious ENOTEMPTY errors. - const retries = isWindows ? 100 : 1 - let i = 0 - do { - let threw = true - try { - const ret = options.rmdirSync(p, options) - threw = false - return ret - } finally { - if (++i < retries && threw) - continue - } - } while (true) -} +/***/ }), +/* 302 */ +/***/ (function(module, exports, __webpack_require__) { -module.exports = rimraf -rimraf.sync = rimrafSync +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const events_1 = __webpack_require__(164); +const fsScandir = __webpack_require__(303); +const fastq = __webpack_require__(312); +const common = __webpack_require__(314); +const reader_1 = __webpack_require__(315); +class AsyncReader extends reader_1.default { + constructor(_root, _settings) { + super(_root, _settings); + this._settings = _settings; + this._scandir = fsScandir.scandir; + this._emitter = new events_1.EventEmitter(); + this._queue = fastq(this._worker.bind(this), this._settings.concurrency); + this._isFatalError = false; + this._isDestroyed = false; + this._queue.drain = () => { + if (!this._isFatalError) { + this._emitter.emit('end'); + } + }; + } + read() { + this._isFatalError = false; + this._isDestroyed = false; + setImmediate(() => { + this._pushToQueue(this._root, this._settings.basePath); + }); + return this._emitter; + } + destroy() { + if (this._isDestroyed) { + throw new Error('The reader is already destroyed'); + } + this._isDestroyed = true; + this._queue.killAndDrain(); + } + onEntry(callback) { + this._emitter.on('entry', callback); + } + onError(callback) { + this._emitter.once('error', callback); + } + onEnd(callback) { + this._emitter.once('end', callback); + } + _pushToQueue(directory, base) { + const queueItem = { directory, base }; + this._queue.push(queueItem, (error) => { + if (error !== null) { + this._handleError(error); + } + }); + } + _worker(item, done) { + this._scandir(item.directory, this._settings.fsScandirSettings, (error, entries) => { + if (error !== null) { + return done(error, undefined); + } + for (const entry of entries) { + this._handleEntry(entry, item.base); + } + done(null, undefined); + }); + } + _handleError(error) { + if (!common.isFatalError(this._settings, error)) { + return; + } + this._isFatalError = true; + this._isDestroyed = true; + this._emitter.emit('error', error); + } + _handleEntry(entry, base) { + if (this._isDestroyed || this._isFatalError) { + return; + } + const fullpath = entry.path; + if (base !== undefined) { + entry.path = common.joinPathSegments(base, entry.name, this._settings.pathSegmentSeparator); + } + if (common.isAppliedFilter(this._settings.entryFilter, entry)) { + this._emitEntry(entry); + } + if (entry.dirent.isDirectory() && common.isAppliedFilter(this._settings.deepFilter, entry)) { + this._pushToQueue(fullpath, entry.path); + } + } + _emitEntry(entry) { + this._emitter.emit('entry', entry); + } +} +exports.default = AsyncReader; /***/ }), -/* 335 */ +/* 303 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const async = __webpack_require__(304); +const sync = __webpack_require__(309); +const settings_1 = __webpack_require__(310); +exports.Settings = settings_1.default; +function scandir(path, optionsOrSettingsOrCallback, callback) { + if (typeof optionsOrSettingsOrCallback === 'function') { + return async.read(path, getSettings(), optionsOrSettingsOrCallback); + } + async.read(path, getSettings(optionsOrSettingsOrCallback), callback); +} +exports.scandir = scandir; +function scandirSync(path, optionsOrSettings) { + const settings = getSettings(optionsOrSettings); + return sync.read(path, settings); +} +exports.scandirSync = scandirSync; +function getSettings(settingsOrOptions = {}) { + if (settingsOrOptions instanceof settings_1.default) { + return settingsOrOptions; + } + return new settings_1.default(settingsOrOptions); +} -const AggregateError = __webpack_require__(336); - -module.exports = async ( - iterable, - mapper, - { - concurrency = Infinity, - stopOnError = true - } = {} -) => { - return new Promise((resolve, reject) => { - if (typeof mapper !== 'function') { - throw new TypeError('Mapper function is required'); - } - if (!(typeof concurrency === 'number' && concurrency >= 1)) { - throw new TypeError(`Expected \`concurrency\` to be a number from 1 and up, got \`${concurrency}\` (${typeof concurrency})`); - } +/***/ }), +/* 304 */ +/***/ (function(module, exports, __webpack_require__) { - const ret = []; - const errors = []; - const iterator = iterable[Symbol.iterator](); - let isRejected = false; - let isIterableDone = false; - let resolvingCount = 0; - let currentIndex = 0; +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const fsStat = __webpack_require__(295); +const rpl = __webpack_require__(305); +const constants_1 = __webpack_require__(306); +const utils = __webpack_require__(307); +function read(directory, settings, callback) { + if (!settings.stats && constants_1.IS_SUPPORT_READDIR_WITH_FILE_TYPES) { + return readdirWithFileTypes(directory, settings, callback); + } + return readdir(directory, settings, callback); +} +exports.read = read; +function readdirWithFileTypes(directory, settings, callback) { + settings.fs.readdir(directory, { withFileTypes: true }, (readdirError, dirents) => { + if (readdirError !== null) { + return callFailureCallback(callback, readdirError); + } + const entries = dirents.map((dirent) => ({ + dirent, + name: dirent.name, + path: `${directory}${settings.pathSegmentSeparator}${dirent.name}` + })); + if (!settings.followSymbolicLinks) { + return callSuccessCallback(callback, entries); + } + const tasks = entries.map((entry) => makeRplTaskEntry(entry, settings)); + rpl(tasks, (rplError, rplEntries) => { + if (rplError !== null) { + return callFailureCallback(callback, rplError); + } + callSuccessCallback(callback, rplEntries); + }); + }); +} +exports.readdirWithFileTypes = readdirWithFileTypes; +function makeRplTaskEntry(entry, settings) { + return (done) => { + if (!entry.dirent.isSymbolicLink()) { + return done(null, entry); + } + settings.fs.stat(entry.path, (statError, stats) => { + if (statError !== null) { + if (settings.throwErrorOnBrokenSymbolicLink) { + return done(statError); + } + return done(null, entry); + } + entry.dirent = utils.fs.createDirentFromStats(entry.name, stats); + return done(null, entry); + }); + }; +} +function readdir(directory, settings, callback) { + settings.fs.readdir(directory, (readdirError, names) => { + if (readdirError !== null) { + return callFailureCallback(callback, readdirError); + } + const filepaths = names.map((name) => `${directory}${settings.pathSegmentSeparator}${name}`); + const tasks = filepaths.map((filepath) => { + return (done) => fsStat.stat(filepath, settings.fsStatSettings, done); + }); + rpl(tasks, (rplError, results) => { + if (rplError !== null) { + return callFailureCallback(callback, rplError); + } + const entries = []; + names.forEach((name, index) => { + const stats = results[index]; + const entry = { + name, + path: filepaths[index], + dirent: utils.fs.createDirentFromStats(name, stats) + }; + if (settings.stats) { + entry.stats = stats; + } + entries.push(entry); + }); + callSuccessCallback(callback, entries); + }); + }); +} +exports.readdir = readdir; +function callFailureCallback(callback, error) { + callback(error); +} +function callSuccessCallback(callback, result) { + callback(null, result); +} - const next = () => { - if (isRejected) { - return; - } - const nextItem = iterator.next(); - const i = currentIndex; - currentIndex++; +/***/ }), +/* 305 */ +/***/ (function(module, exports) { - if (nextItem.done) { - isIterableDone = true; +module.exports = runParallel - if (resolvingCount === 0) { - if (!stopOnError && errors.length !== 0) { - reject(new AggregateError(errors)); - } else { - resolve(ret); - } - } +function runParallel (tasks, cb) { + var results, pending, keys + var isSync = true - return; - } + if (Array.isArray(tasks)) { + results = [] + pending = tasks.length + } else { + keys = Object.keys(tasks) + results = {} + pending = keys.length + } - resolvingCount++; + function done (err) { + function end () { + if (cb) cb(err, results) + cb = null + } + if (isSync) process.nextTick(end) + else end() + } - (async () => { - try { - const element = await nextItem.value; - ret[i] = await mapper(element, i); - resolvingCount--; - next(); - } catch (error) { - if (stopOnError) { - isRejected = true; - reject(error); - } else { - errors.push(error); - resolvingCount--; - next(); - } - } - })(); - }; + function each (i, err, result) { + results[i] = result + if (--pending === 0 || err) { + done(err) + } + } - for (let i = 0; i < concurrency; i++) { - next(); + if (!pending) { + // empty + done(null) + } else if (keys) { + // object + keys.forEach(function (key) { + tasks[key](function (err, result) { each(key, err, result) }) + }) + } else { + // array + tasks.forEach(function (task, i) { + task(function (err, result) { each(i, err, result) }) + }) + } - if (isIterableDone) { - break; - } - } - }); -}; + isSync = false +} /***/ }), -/* 336 */ +/* 306 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const NODE_PROCESS_VERSION_PARTS = process.versions.node.split('.'); +const MAJOR_VERSION = parseInt(NODE_PROCESS_VERSION_PARTS[0], 10); +const MINOR_VERSION = parseInt(NODE_PROCESS_VERSION_PARTS[1], 10); +const SUPPORTED_MAJOR_VERSION = 10; +const SUPPORTED_MINOR_VERSION = 10; +const IS_MATCHED_BY_MAJOR = MAJOR_VERSION > SUPPORTED_MAJOR_VERSION; +const IS_MATCHED_BY_MAJOR_AND_MINOR = MAJOR_VERSION === SUPPORTED_MAJOR_VERSION && MINOR_VERSION >= SUPPORTED_MINOR_VERSION; +/** + * IS `true` for Node.js 10.10 and greater. + */ +exports.IS_SUPPORT_READDIR_WITH_FILE_TYPES = IS_MATCHED_BY_MAJOR || IS_MATCHED_BY_MAJOR_AND_MINOR; -const indentString = __webpack_require__(337); -const cleanStack = __webpack_require__(338); - -const cleanInternalStack = stack => stack.replace(/\s+at .*aggregate-error\/index.js:\d+:\d+\)?/g, ''); - -class AggregateError extends Error { - constructor(errors) { - if (!Array.isArray(errors)) { - throw new TypeError(`Expected input to be an Array, got ${typeof errors}`); - } - - errors = [...errors].map(error => { - if (error instanceof Error) { - return error; - } - - if (error !== null && typeof error === 'object') { - // Handle plain error objects with message property and/or possibly other metadata - return Object.assign(new Error(error.message), error); - } - - return new Error(error); - }); - - let message = errors - .map(error => { - // The `stack` property is not standardized, so we can't assume it exists - return typeof error.stack === 'string' ? cleanInternalStack(cleanStack(error.stack)) : String(error); - }) - .join('\n'); - message = '\n' + indentString(message, 4); - super(message); - - this.name = 'AggregateError'; - - Object.defineProperty(this, '_errors', {value: errors}); - } - * [Symbol.iterator]() { - for (const error of this._errors) { - yield error; - } - } -} +/***/ }), +/* 307 */ +/***/ (function(module, exports, __webpack_require__) { -module.exports = AggregateError; +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const fs = __webpack_require__(308); +exports.fs = fs; /***/ }), -/* 337 */ +/* 308 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +class DirentFromStats { + constructor(name, stats) { + this.name = name; + this.isBlockDevice = stats.isBlockDevice.bind(stats); + this.isCharacterDevice = stats.isCharacterDevice.bind(stats); + this.isDirectory = stats.isDirectory.bind(stats); + this.isFIFO = stats.isFIFO.bind(stats); + this.isFile = stats.isFile.bind(stats); + this.isSocket = stats.isSocket.bind(stats); + this.isSymbolicLink = stats.isSymbolicLink.bind(stats); + } +} +function createDirentFromStats(name, stats) { + return new DirentFromStats(name, stats); +} +exports.createDirentFromStats = createDirentFromStats; -module.exports = (string, count = 1, options) => { - options = { - indent: ' ', - includeEmptyLines: false, - ...options - }; +/***/ }), +/* 309 */ +/***/ (function(module, exports, __webpack_require__) { - if (typeof string !== 'string') { - throw new TypeError( - `Expected \`input\` to be a \`string\`, got \`${typeof string}\`` - ); - } - - if (typeof count !== 'number') { - throw new TypeError( - `Expected \`count\` to be a \`number\`, got \`${typeof count}\`` - ); - } - - if (typeof options.indent !== 'string') { - throw new TypeError( - `Expected \`options.indent\` to be a \`string\`, got \`${typeof options.indent}\`` - ); - } - - if (count === 0) { - return string; - } - - const regex = options.includeEmptyLines ? /^/gm : /^(?!\s*$)/gm; - - return string.replace(regex, options.indent.repeat(count)); -}; +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const fsStat = __webpack_require__(295); +const constants_1 = __webpack_require__(306); +const utils = __webpack_require__(307); +function read(directory, settings) { + if (!settings.stats && constants_1.IS_SUPPORT_READDIR_WITH_FILE_TYPES) { + return readdirWithFileTypes(directory, settings); + } + return readdir(directory, settings); +} +exports.read = read; +function readdirWithFileTypes(directory, settings) { + const dirents = settings.fs.readdirSync(directory, { withFileTypes: true }); + return dirents.map((dirent) => { + const entry = { + dirent, + name: dirent.name, + path: `${directory}${settings.pathSegmentSeparator}${dirent.name}` + }; + if (entry.dirent.isSymbolicLink() && settings.followSymbolicLinks) { + try { + const stats = settings.fs.statSync(entry.path); + entry.dirent = utils.fs.createDirentFromStats(entry.name, stats); + } + catch (error) { + if (settings.throwErrorOnBrokenSymbolicLink) { + throw error; + } + } + } + return entry; + }); +} +exports.readdirWithFileTypes = readdirWithFileTypes; +function readdir(directory, settings) { + const names = settings.fs.readdirSync(directory); + return names.map((name) => { + const entryPath = `${directory}${settings.pathSegmentSeparator}${name}`; + const stats = fsStat.statSync(entryPath, settings.fsStatSettings); + const entry = { + name, + path: entryPath, + dirent: utils.fs.createDirentFromStats(name, stats) + }; + if (settings.stats) { + entry.stats = stats; + } + return entry; + }); +} +exports.readdir = readdir; /***/ }), -/* 338 */ +/* 310 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const path = __webpack_require__(4); +const fsStat = __webpack_require__(295); +const fs = __webpack_require__(311); +class Settings { + constructor(_options = {}) { + this._options = _options; + this.followSymbolicLinks = this._getValue(this._options.followSymbolicLinks, false); + this.fs = fs.createFileSystemAdapter(this._options.fs); + this.pathSegmentSeparator = this._getValue(this._options.pathSegmentSeparator, path.sep); + this.stats = this._getValue(this._options.stats, false); + this.throwErrorOnBrokenSymbolicLink = this._getValue(this._options.throwErrorOnBrokenSymbolicLink, true); + this.fsStatSettings = new fsStat.Settings({ + followSymbolicLink: this.followSymbolicLinks, + fs: this.fs, + throwErrorOnBrokenSymbolicLink: this.throwErrorOnBrokenSymbolicLink + }); + } + _getValue(option, value) { + return option === undefined ? value : option; + } +} +exports.default = Settings; -const os = __webpack_require__(122); - -const extractPathRegex = /\s+at.*(?:\(|\s)(.*)\)?/; -const pathRegex = /^(?:(?:(?:node|(?:internal\/[\w/]*|.*node_modules\/(?:babel-polyfill|pirates)\/.*)?\w+)\.js:\d+:\d+)|native)/; -const homeDir = typeof os.homedir === 'undefined' ? '' : os.homedir(); - -module.exports = (stack, options) => { - options = Object.assign({pretty: false}, options); - - return stack.replace(/\\/g, '/') - .split('\n') - .filter(line => { - const pathMatches = line.match(extractPathRegex); - if (pathMatches === null || !pathMatches[1]) { - return true; - } - - const match = pathMatches[1]; - - // Electron - if ( - match.includes('.app/Contents/Resources/electron.asar') || - match.includes('.app/Contents/Resources/default_app.asar') - ) { - return false; - } - return !pathRegex.test(match); - }) - .filter(line => line.trim() !== '') - .map(line => { - if (options.pretty) { - return line.replace(extractPathRegex, (m, p1) => m.replace(p1, p1.replace(homeDir, '~'))); - } +/***/ }), +/* 311 */ +/***/ (function(module, exports, __webpack_require__) { - return line; - }) - .join('\n'); -}; +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const fs = __webpack_require__(132); +exports.FILE_SYSTEM_ADAPTER = { + lstat: fs.lstat, + stat: fs.stat, + lstatSync: fs.lstatSync, + statSync: fs.statSync, + readdir: fs.readdir, + readdirSync: fs.readdirSync +}; +function createFileSystemAdapter(fsMethods) { + if (fsMethods === undefined) { + return exports.FILE_SYSTEM_ADAPTER; + } + return Object.assign(Object.assign({}, exports.FILE_SYSTEM_ADAPTER), fsMethods); +} +exports.createFileSystemAdapter = createFileSystemAdapter; /***/ }), -/* 339 */ +/* 312 */ /***/ (function(module, exports, __webpack_require__) { -var fs = __webpack_require__(132), - path = __webpack_require__(4); +"use strict"; -module.exports = ncp; -ncp.ncp = ncp; -function ncp (source, dest, options, callback) { - var cback = callback; +var reusify = __webpack_require__(313) - if (!callback) { - cback = options; - options = {}; +function fastqueue (context, worker, concurrency) { + if (typeof context === 'function') { + concurrency = worker + worker = context + context = null } - var basePath = process.cwd(), - currentPath = path.resolve(basePath, source), - targetPath = path.resolve(basePath, dest), - filter = options.filter, - rename = options.rename, - transform = options.transform, - clobber = options.clobber !== false, - modified = options.modified, - dereference = options.dereference, - errs = null, - started = 0, - finished = 0, - running = 0, - limit = options.limit || ncp.limit || 16; - - limit = (limit < 1) ? 1 : (limit > 512) ? 512 : limit; + var cache = reusify(Task) + var queueHead = null + var queueTail = null + var _running = 0 - startCopy(currentPath); - - function startCopy(source) { - started++; - if (filter) { - if (filter instanceof RegExp) { - if (!filter.test(source)) { - return cb(true); - } - } - else if (typeof filter === 'function') { - if (!filter(source)) { - return cb(true); - } - } - } - return getStats(source); + var self = { + push: push, + drain: noop, + saturated: noop, + pause: pause, + paused: false, + concurrency: concurrency, + running: running, + resume: resume, + idle: idle, + length: length, + unshift: unshift, + empty: noop, + kill: kill, + killAndDrain: killAndDrain } - function getStats(source) { - var stat = dereference ? fs.stat : fs.lstat; - if (running >= limit) { - return setImmediate(function () { - getStats(source); - }); - } - running++; - stat(source, function (err, stats) { - var item = {}; - if (err) { - return onError(err); - } + return self - // We need to get the mode from the stats object and preserve it. - item.name = source; - item.mode = stats.mode; - item.mtime = stats.mtime; //modified time - item.atime = stats.atime; //access time + function running () { + return _running + } - if (stats.isDirectory()) { - return onDir(item); - } - else if (stats.isFile()) { - return onFile(item); - } - else if (stats.isSymbolicLink()) { - // Symlinks don't really need to know about the mode. - return onLink(source); - } - }); + function pause () { + self.paused = true } - function onFile(file) { - var target = file.name.replace(currentPath, targetPath); - if(rename) { - target = rename(target); + function length () { + var current = queueHead + var counter = 0 + + while (current) { + current = current.next + counter++ } - isWritable(target, function (writable) { - if (writable) { - return copyFile(file, target); - } - if(clobber) { - rmFile(target, function () { - copyFile(file, target); - }); - } - if (modified) { - var stat = dereference ? fs.stat : fs.lstat; - stat(target, function(err, stats) { - //if souce modified time greater to target modified time copy file - if (file.mtime.getTime()>stats.mtime.getTime()) - copyFile(file, target); - else return cb(); - }); - } - else { - return cb(); - } - }); + + return counter } - function copyFile(file, target) { - var readStream = fs.createReadStream(file.name), - writeStream = fs.createWriteStream(target, { mode: file.mode }); - - readStream.on('error', onError); - writeStream.on('error', onError); - - if(transform) { - transform(readStream, writeStream, file); - } else { - writeStream.on('open', function() { - readStream.pipe(writeStream); - }); + function resume () { + if (!self.paused) return + self.paused = false + for (var i = 0; i < self.concurrency; i++) { + _running++ + release() } - writeStream.once('finish', function() { - if (modified) { - //target file modified date sync. - fs.utimesSync(target, file.atime, file.mtime); - cb(); - } - else cb(); - }); } - function rmFile(file, done) { - fs.unlink(file, function (err) { - if (err) { - return onError(err); - } - return done(); - }); + function idle () { + return _running === 0 && self.length() === 0 } - function onDir(dir) { - var target = dir.name.replace(currentPath, targetPath); - isWritable(target, function (writable) { - if (writable) { - return mkDir(dir, target); - } - copyDir(dir.name); - }); - } + function push (value, done) { + var current = cache.get() - function mkDir(dir, target) { - fs.mkdir(target, dir.mode, function (err) { - if (err) { - return onError(err); - } - copyDir(dir.name); - }); - } + current.context = context + current.release = release + current.value = value + current.callback = done || noop - function copyDir(dir) { - fs.readdir(dir, function (err, items) { - if (err) { - return onError(err); + if (_running === self.concurrency || self.paused) { + if (queueTail) { + queueTail.next = current + queueTail = current + } else { + queueHead = current + queueTail = current + self.saturated() } - items.forEach(function (item) { - startCopy(path.join(dir, item)); - }); - return cb(); - }); + } else { + _running++ + worker.call(context, current.value, current.worked) + } } - function onLink(link) { - var target = link.replace(currentPath, targetPath); - fs.readlink(link, function (err, resolvedPath) { - if (err) { - return onError(err); + function unshift (value, done) { + var current = cache.get() + + current.context = context + current.release = release + current.value = value + current.callback = done || noop + + if (_running === self.concurrency || self.paused) { + if (queueHead) { + current.next = queueHead + queueHead = current + } else { + queueHead = current + queueTail = current + self.saturated() } - checkLink(resolvedPath, target); - }); + } else { + _running++ + worker.call(context, current.value, current.worked) + } } - function checkLink(resolvedPath, target) { - if (dereference) { - resolvedPath = path.resolve(basePath, resolvedPath); + function release (holder) { + if (holder) { + cache.release(holder) } - isWritable(target, function (writable) { - if (writable) { - return makeLink(resolvedPath, target); - } - fs.readlink(target, function (err, targetDest) { - if (err) { - return onError(err); - } - if (dereference) { - targetDest = path.resolve(basePath, targetDest); + var next = queueHead + if (next) { + if (!self.paused) { + if (queueTail === queueHead) { + queueTail = null } - if (targetDest === resolvedPath) { - return cb(); + queueHead = next.next + next.next = null + worker.call(context, next.value, next.worked) + if (queueTail === null) { + self.empty() } - return rmFile(target, function () { - makeLink(resolvedPath, target); - }); - }); - }); - } - - function makeLink(linkPath, target) { - fs.symlink(linkPath, target, function (err) { - if (err) { - return onError(err); + } else { + _running-- } - return cb(); - }); + } else if (--_running === 0) { + self.drain() + } } - function isWritable(path, done) { - fs.lstat(path, function (err) { - if (err) { - if (err.code === 'ENOENT') return done(true); - return done(false); - } - return done(false); - }); + function kill () { + queueHead = null + queueTail = null + self.drain = noop } - function onError(err) { - if (options.stopOnError) { - return cback(err); - } - else if (!errs && options.errs) { - errs = fs.createWriteStream(options.errs); - } - else if (!errs) { - errs = []; - } - if (typeof errs.write === 'undefined') { - errs.push(err); - } - else { - errs.write(err.stack + '\n\n'); - } - return cb(); + function killAndDrain () { + queueHead = null + queueTail = null + self.drain() + self.drain = noop } +} - function cb(skipped) { - if (!skipped) running--; - finished++; - if ((started === finished) && (running === 0)) { - if (cback !== undefined ) { - return errs ? cback(errs) : cback(null); - } - } +function noop () {} + +function Task () { + this.value = null + this.callback = noop + this.next = null + this.release = noop + this.context = null + + var self = this + + this.worked = function worked (err, result) { + var callback = self.callback + self.value = null + self.callback = noop + callback.call(self.context, err, result) + self.release(self) } } - +module.exports = fastqueue /***/ }), -/* 340 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* 313 */ +/***/ (function(module, exports, __webpack_require__) { "use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getProjects", function() { return getProjects; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getNonBazelProjectsOnly", function() { return getNonBazelProjectsOnly; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getBazelProjectsOnly", function() { return getBazelProjectsOnly; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "buildProjectGraph", function() { return buildProjectGraph; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "topologicallyBatchProjects", function() { return topologicallyBatchProjects; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "includeTransitiveProjects", function() { return includeTransitiveProjects; }); -/* harmony import */ var glob__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(244); -/* harmony import */ var glob__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(glob__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(113); -/* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(util__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var _errors__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(341); -/* harmony import */ var _project__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(342); -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - - - - - -const glob = Object(util__WEBPACK_IMPORTED_MODULE_2__["promisify"])(glob__WEBPACK_IMPORTED_MODULE_0___default.a); -/** a Map of project names to Project instances */ - -async function getProjects(rootPath, projectsPathsPatterns, { - include = [], - exclude = [] -} = {}, bazelOnly = false) { - const projects = new Map(); - - for (const pattern of projectsPathsPatterns) { - const pathsToProcess = await packagesFromGlobPattern({ - pattern, - rootPath - }); - - for (const filePath of pathsToProcess) { - const projectConfigPath = normalize(filePath); - const projectDir = path__WEBPACK_IMPORTED_MODULE_1___default.a.dirname(projectConfigPath); - const project = await _project__WEBPACK_IMPORTED_MODULE_4__["Project"].fromPath(projectDir); - const excludeProject = exclude.includes(project.name) || include.length > 0 && !include.includes(project.name) || bazelOnly && !project.isBazelPackage(); - - if (excludeProject) { - continue; - } - if (projects.has(project.name)) { - throw new _errors__WEBPACK_IMPORTED_MODULE_3__["CliError"](`There are multiple projects with the same name [${project.name}]`, { - name: project.name, - paths: [project.path, projects.get(project.name).path] - }); - } - projects.set(project.name, project); - } - } +function reusify (Constructor) { + var head = new Constructor() + var tail = head - return projects; -} -async function getNonBazelProjectsOnly(projects) { - const bazelProjectsOnly = new Map(); + function get () { + var current = head - for (const project of projects.values()) { - if (!project.isBazelPackage()) { - bazelProjectsOnly.set(project.name, project); + if (current.next) { + head = current.next + } else { + head = new Constructor() + tail = head } - } - return bazelProjectsOnly; -} -async function getBazelProjectsOnly(projects) { - const bazelProjectsOnly = new Map(); + current.next = null - for (const project of projects.values()) { - if (project.isBazelPackage()) { - bazelProjectsOnly.set(project.name, project); - } + return current } - return bazelProjectsOnly; -} - -function packagesFromGlobPattern({ - pattern, - rootPath -}) { - const globOptions = { - cwd: rootPath, - // Should throw in case of unusual errors when reading the file system - strict: true, - // Always returns absolute paths for matched files - absolute: true, - // Do not match ** against multiple filenames - // (This is only specified because we currently don't have a need for it.) - noglobstar: true - }; - return glob(path__WEBPACK_IMPORTED_MODULE_1___default.a.join(pattern, 'package.json'), globOptions); -} // https://github.com/isaacs/node-glob/blob/master/common.js#L104 -// glob always returns "\\" as "/" in windows, so everyone -// gets normalized because we can't have nice things. - - -function normalize(dir) { - return path__WEBPACK_IMPORTED_MODULE_1___default.a.normalize(dir); -} - -function buildProjectGraph(projects) { - const projectGraph = new Map(); - - for (const project of projects.values()) { - const projectDeps = []; - const dependencies = project.allDependencies; - - for (const depName of Object.keys(dependencies)) { - if (projects.has(depName)) { - const dep = projects.get(depName); - project.ensureValidProjectDependency(dep); - projectDeps.push(dep); - } - } - - projectGraph.set(project.name, projectDeps); + function release (obj) { + tail.next = obj + tail = obj } - return projectGraph; -} -function topologicallyBatchProjects(projectsToBatch, projectGraph) { - // We're going to be chopping stuff out of this list, so copy it. - const projectsLeftToBatch = new Set(projectsToBatch.keys()); - const batches = []; - - while (projectsLeftToBatch.size > 0) { - // Get all projects that have no remaining dependencies within the repo - // that haven't yet been picked. - const batch = []; - - for (const projectName of projectsLeftToBatch) { - const projectDeps = projectGraph.get(projectName); - const needsDependenciesBatched = projectDeps.some(dep => projectsLeftToBatch.has(dep.name)); - - if (!needsDependenciesBatched) { - batch.push(projectsToBatch.get(projectName)); - } - } // If we weren't able to find a project with no remaining dependencies, - // then we've encountered a cycle in the dependency graph. - - - const hasCycles = batch.length === 0; - - if (hasCycles) { - const cycleProjectNames = [...projectsLeftToBatch]; - const message = 'Encountered a cycle in the dependency graph. Projects in cycle are:\n' + cycleProjectNames.join(', '); - throw new _errors__WEBPACK_IMPORTED_MODULE_3__["CliError"](message); - } - - batches.push(batch); - batch.forEach(project => projectsLeftToBatch.delete(project.name)); + return { + get: get, + release: release } - - return batches; } -function includeTransitiveProjects(subsetOfProjects, allProjects, { - onlyProductionDependencies = false -} = {}) { - const projectsWithDependents = new Map(); // the current list of packages we are expanding using breadth-first-search - - const toProcess = [...subsetOfProjects]; - while (toProcess.length > 0) { - const project = toProcess.shift(); - const dependencies = onlyProductionDependencies ? project.productionDependencies : project.allDependencies; - Object.keys(dependencies).forEach(dep => { - if (allProjects.has(dep)) { - toProcess.push(allProjects.get(dep)); - } - }); - projectsWithDependents.set(project.name, project); - } +module.exports = reusify - return projectsWithDependents; -} /***/ }), -/* 341 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* 314 */ +/***/ (function(module, exports, __webpack_require__) { "use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "CliError", function() { return CliError; }); -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ -class CliError extends Error { - constructor(message, meta = {}) { - super(message); - this.meta = meta; - } + +Object.defineProperty(exports, "__esModule", { value: true }); +function isFatalError(settings, error) { + if (settings.errorFilter === null) { + return true; + } + return !settings.errorFilter(error); +} +exports.isFatalError = isFatalError; +function isAppliedFilter(filter, value) { + return filter === null || filter(value); +} +exports.isAppliedFilter = isAppliedFilter; +function replacePathSegmentSeparator(filepath, separator) { + return filepath.split(/[\\/]/).join(separator); +} +exports.replacePathSegmentSeparator = replacePathSegmentSeparator; +function joinPathSegments(a, b, separator) { + if (a === '') { + return b; + } + return a + separator + b; +} +exports.joinPathSegments = joinPathSegments; -} /***/ }), -/* 342 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* 315 */ +/***/ (function(module, exports, __webpack_require__) { "use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Project", function() { return Project; }); -/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(132); -/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(fs__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(113); -/* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(util__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var _errors__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(341); -/* harmony import */ var _log__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(220); -/* harmony import */ var _package_json__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(343); -/* harmony import */ var _scripts__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(407); -function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } + +Object.defineProperty(exports, "__esModule", { value: true }); +const common = __webpack_require__(314); +class Reader { + constructor(_root, _settings) { + this._root = _root; + this._settings = _settings; + this._root = common.replacePathSegmentSeparator(_root, _settings.pathSegmentSeparator); + } +} +exports.default = Reader; -function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } -function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } +/***/ }), +/* 316 */ +/***/ (function(module, exports, __webpack_require__) { -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const stream_1 = __webpack_require__(173); +const async_1 = __webpack_require__(302); +class StreamProvider { + constructor(_root, _settings) { + this._root = _root; + this._settings = _settings; + this._reader = new async_1.default(this._root, this._settings); + this._stream = new stream_1.Readable({ + objectMode: true, + read: () => { }, + destroy: this._reader.destroy.bind(this._reader) + }); + } + read() { + this._reader.onError((error) => { + this._stream.emit('error', error); + }); + this._reader.onEntry((entry) => { + this._stream.push(entry); + }); + this._reader.onEnd(() => { + this._stream.push(null); + }); + this._reader.read(); + return this._stream; + } +} +exports.default = StreamProvider; +/***/ }), +/* 317 */ +/***/ (function(module, exports, __webpack_require__) { +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const sync_1 = __webpack_require__(318); +class SyncProvider { + constructor(_root, _settings) { + this._root = _root; + this._settings = _settings; + this._reader = new sync_1.default(this._root, this._settings); + } + read() { + return this._reader.read(); + } +} +exports.default = SyncProvider; +/***/ }), +/* 318 */ +/***/ (function(module, exports, __webpack_require__) { +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const fsScandir = __webpack_require__(303); +const common = __webpack_require__(314); +const reader_1 = __webpack_require__(315); +class SyncReader extends reader_1.default { + constructor() { + super(...arguments); + this._scandir = fsScandir.scandirSync; + this._storage = new Set(); + this._queue = new Set(); + } + read() { + this._pushToQueue(this._root, this._settings.basePath); + this._handleQueue(); + return [...this._storage]; + } + _pushToQueue(directory, base) { + this._queue.add({ directory, base }); + } + _handleQueue() { + for (const item of this._queue.values()) { + this._handleDirectory(item.directory, item.base); + } + } + _handleDirectory(directory, base) { + try { + const entries = this._scandir(directory, this._settings.fsScandirSettings); + for (const entry of entries) { + this._handleEntry(entry, base); + } + } + catch (error) { + this._handleError(error); + } + } + _handleError(error) { + if (!common.isFatalError(this._settings, error)) { + return; + } + throw error; + } + _handleEntry(entry, base) { + const fullpath = entry.path; + if (base !== undefined) { + entry.path = common.joinPathSegments(base, entry.name, this._settings.pathSegmentSeparator); + } + if (common.isAppliedFilter(this._settings.entryFilter, entry)) { + this._pushToStorage(entry); + } + if (entry.dirent.isDirectory() && common.isAppliedFilter(this._settings.deepFilter, entry)) { + this._pushToQueue(fullpath, entry.path); + } + } + _pushToStorage(entry) { + this._storage.add(entry); + } +} +exports.default = SyncReader; -class Project { - static async fromPath(path) { - const pkgJson = await Object(_package_json__WEBPACK_IMPORTED_MODULE_5__["readPackageJson"])(path); - return new Project(pkgJson, path); - } - /** parsed package.json */ +/***/ }), +/* 319 */ +/***/ (function(module, exports, __webpack_require__) { - constructor(packageJson, projectPath) { - _defineProperty(this, "json", void 0); +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const path = __webpack_require__(4); +const fsScandir = __webpack_require__(303); +class Settings { + constructor(_options = {}) { + this._options = _options; + this.basePath = this._getValue(this._options.basePath, undefined); + this.concurrency = this._getValue(this._options.concurrency, Infinity); + this.deepFilter = this._getValue(this._options.deepFilter, null); + this.entryFilter = this._getValue(this._options.entryFilter, null); + this.errorFilter = this._getValue(this._options.errorFilter, null); + this.pathSegmentSeparator = this._getValue(this._options.pathSegmentSeparator, path.sep); + this.fsScandirSettings = new fsScandir.Settings({ + followSymbolicLinks: this._options.followSymbolicLinks, + fs: this._options.fs, + pathSegmentSeparator: this._options.pathSegmentSeparator, + stats: this._options.stats, + throwErrorOnBrokenSymbolicLink: this._options.throwErrorOnBrokenSymbolicLink + }); + } + _getValue(option, value) { + return option === undefined ? value : option; + } +} +exports.default = Settings; - _defineProperty(this, "packageJsonLocation", void 0); - _defineProperty(this, "nodeModulesLocation", void 0); +/***/ }), +/* 320 */ +/***/ (function(module, exports, __webpack_require__) { - _defineProperty(this, "targetLocation", void 0); +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const path = __webpack_require__(4); +const fsStat = __webpack_require__(295); +const utils = __webpack_require__(259); +class Reader { + constructor(_settings) { + this._settings = _settings; + this._fsStatSettings = new fsStat.Settings({ + followSymbolicLink: this._settings.followSymbolicLinks, + fs: this._settings.fs, + throwErrorOnBrokenSymbolicLink: this._settings.followSymbolicLinks + }); + } + _getFullEntryPath(filepath) { + return path.resolve(this._settings.cwd, filepath); + } + _makeEntry(stats, pattern) { + const entry = { + name: pattern, + path: pattern, + dirent: utils.fs.createDirentFromStats(pattern, stats) + }; + if (this._settings.stats) { + entry.stats = stats; + } + return entry; + } + _isFatalError(error) { + return !utils.errno.isEnoentCodeError(error) && !this._settings.suppressErrors; + } +} +exports.default = Reader; - _defineProperty(this, "path", void 0); - _defineProperty(this, "version", void 0); +/***/ }), +/* 321 */ +/***/ (function(module, exports, __webpack_require__) { - _defineProperty(this, "allDependencies", void 0); +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const path = __webpack_require__(4); +const deep_1 = __webpack_require__(322); +const entry_1 = __webpack_require__(325); +const error_1 = __webpack_require__(326); +const entry_2 = __webpack_require__(327); +class Provider { + constructor(_settings) { + this._settings = _settings; + this.errorFilter = new error_1.default(this._settings); + this.entryFilter = new entry_1.default(this._settings, this._getMicromatchOptions()); + this.deepFilter = new deep_1.default(this._settings, this._getMicromatchOptions()); + this.entryTransformer = new entry_2.default(this._settings); + } + _getRootDirectory(task) { + return path.resolve(this._settings.cwd, task.base); + } + _getReaderOptions(task) { + const basePath = task.base === '.' ? '' : task.base; + return { + basePath, + pathSegmentSeparator: '/', + concurrency: this._settings.concurrency, + deepFilter: this.deepFilter.getFilter(basePath, task.positive, task.negative), + entryFilter: this.entryFilter.getFilter(task.positive, task.negative), + errorFilter: this.errorFilter.getFilter(), + followSymbolicLinks: this._settings.followSymbolicLinks, + fs: this._settings.fs, + stats: this._settings.stats, + throwErrorOnBrokenSymbolicLink: this._settings.throwErrorOnBrokenSymbolicLink, + transform: this.entryTransformer.getTransformer() + }; + } + _getMicromatchOptions() { + return { + dot: this._settings.dot, + matchBase: this._settings.baseNameMatch, + nobrace: !this._settings.braceExpansion, + nocase: !this._settings.caseSensitiveMatch, + noext: !this._settings.extglob, + noglobstar: !this._settings.globstar, + posix: true, + strictSlashes: false + }; + } +} +exports.default = Provider; - _defineProperty(this, "productionDependencies", void 0); - _defineProperty(this, "devDependencies", void 0); +/***/ }), +/* 322 */ +/***/ (function(module, exports, __webpack_require__) { - _defineProperty(this, "scripts", void 0); - - _defineProperty(this, "bazelPackage", void 0); - - _defineProperty(this, "isSinglePackageJsonProject", false); - - this.json = Object.freeze(packageJson); - this.path = projectPath; - this.packageJsonLocation = path__WEBPACK_IMPORTED_MODULE_1___default.a.resolve(this.path, 'package.json'); - this.nodeModulesLocation = path__WEBPACK_IMPORTED_MODULE_1___default.a.resolve(this.path, 'node_modules'); - this.targetLocation = path__WEBPACK_IMPORTED_MODULE_1___default.a.resolve(this.path, 'target'); - this.version = this.json.version; - this.productionDependencies = this.json.dependencies || {}; - this.devDependencies = this.json.devDependencies || {}; - this.allDependencies = _objectSpread(_objectSpread({}, this.devDependencies), this.productionDependencies); - this.isSinglePackageJsonProject = this.json.name === 'kibana'; - this.scripts = this.json.scripts || {}; - this.bazelPackage = !this.isSinglePackageJsonProject && fs__WEBPACK_IMPORTED_MODULE_0___default.a.existsSync(path__WEBPACK_IMPORTED_MODULE_1___default.a.resolve(this.path, 'BUILD.bazel')); - } - - get name() { - return this.json.name; - } - - ensureValidProjectDependency(project) { - const relativePathToProject = normalizePath(path__WEBPACK_IMPORTED_MODULE_1___default.a.relative(this.path, project.path)); - const relativePathToProjectIfBazelPkg = normalizePath(path__WEBPACK_IMPORTED_MODULE_1___default.a.relative(this.path, `${__dirname}/../../../bazel-bin/packages/${path__WEBPACK_IMPORTED_MODULE_1___default.a.basename(project.path)}`)); - const versionInPackageJson = this.allDependencies[project.name]; - const expectedVersionInPackageJson = `link:${relativePathToProject}`; - const expectedVersionInPackageJsonIfBazelPkg = `link:${relativePathToProjectIfBazelPkg}`; // TODO: after introduce bazel to build all the packages and completely remove the support for kbn packages - // do not allow child projects to hold dependencies, unless they are meant to be published externally - - if (versionInPackageJson === expectedVersionInPackageJson || versionInPackageJson === expectedVersionInPackageJsonIfBazelPkg) { - return; - } - - const updateMsg = 'Update its package.json to the expected value below.'; - const meta = { - actual: `"${project.name}": "${versionInPackageJson}"`, - expected: `"${project.name}": "${expectedVersionInPackageJson}" or "${project.name}": "${expectedVersionInPackageJsonIfBazelPkg}"`, - package: `${this.name} (${this.packageJsonLocation})` - }; - - if (Object(_package_json__WEBPACK_IMPORTED_MODULE_5__["isLinkDependency"])(versionInPackageJson)) { - throw new _errors__WEBPACK_IMPORTED_MODULE_3__["CliError"](`[${this.name}] depends on [${project.name}] using 'link:', but the path is wrong. ${updateMsg}`, meta); - } +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const utils = __webpack_require__(259); +const partial_1 = __webpack_require__(323); +class DeepFilter { + constructor(_settings, _micromatchOptions) { + this._settings = _settings; + this._micromatchOptions = _micromatchOptions; + } + getFilter(basePath, positive, negative) { + const matcher = this._getMatcher(positive); + const negativeRe = this._getNegativePatternsRe(negative); + return (entry) => this._filter(basePath, entry, matcher, negativeRe); + } + _getMatcher(patterns) { + return new partial_1.default(patterns, this._settings, this._micromatchOptions); + } + _getNegativePatternsRe(patterns) { + const affectDepthOfReadingPatterns = patterns.filter(utils.pattern.isAffectDepthOfReadingPattern); + return utils.pattern.convertPatternsToRe(affectDepthOfReadingPatterns, this._micromatchOptions); + } + _filter(basePath, entry, matcher, negativeRe) { + if (this._isSkippedByDeep(basePath, entry.path)) { + return false; + } + if (this._isSkippedSymbolicLink(entry)) { + return false; + } + const filepath = utils.path.removeLeadingDotSegment(entry.path); + if (this._isSkippedByPositivePatterns(filepath, matcher)) { + return false; + } + return this._isSkippedByNegativePatterns(filepath, negativeRe); + } + _isSkippedByDeep(basePath, entryPath) { + /** + * Avoid unnecessary depth calculations when it doesn't matter. + */ + if (this._settings.deep === Infinity) { + return false; + } + return this._getEntryLevel(basePath, entryPath) >= this._settings.deep; + } + _getEntryLevel(basePath, entryPath) { + const entryPathDepth = entryPath.split('/').length; + if (basePath === '') { + return entryPathDepth; + } + const basePathDepth = basePath.split('/').length; + return entryPathDepth - basePathDepth; + } + _isSkippedSymbolicLink(entry) { + return !this._settings.followSymbolicLinks && entry.dirent.isSymbolicLink(); + } + _isSkippedByPositivePatterns(entryPath, matcher) { + return !this._settings.baseNameMatch && !matcher.match(entryPath); + } + _isSkippedByNegativePatterns(entryPath, patternsRe) { + return !utils.pattern.matchAny(entryPath, patternsRe); + } +} +exports.default = DeepFilter; - throw new _errors__WEBPACK_IMPORTED_MODULE_3__["CliError"](`[${this.name}] depends on [${project.name}] but it's not using the local package. ${updateMsg}`, meta); - } - getBuildConfig() { - return this.json.kibana && this.json.kibana.build || {}; - } - /** - * Returns the directory that should be copied into the Kibana build artifact. - * This config can be specified to only include the project's build artifacts - * instead of everything located in the project directory. - */ +/***/ }), +/* 323 */ +/***/ (function(module, exports, __webpack_require__) { +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const matcher_1 = __webpack_require__(324); +class PartialMatcher extends matcher_1.default { + match(filepath) { + const parts = filepath.split('/'); + const levels = parts.length; + const patterns = this._storage.filter((info) => !info.complete || info.segments.length > levels); + for (const pattern of patterns) { + const section = pattern.sections[0]; + /** + * In this case, the pattern has a globstar and we must read all directories unconditionally, + * but only if the level has reached the end of the first group. + * + * fixtures/{a,b}/** + * ^ true/false ^ always true + */ + if (!pattern.complete && levels > section.length) { + return true; + } + const match = parts.every((part, index) => { + const segment = pattern.segments[index]; + if (segment.dynamic && segment.patternRe.test(part)) { + return true; + } + if (!segment.dynamic && segment.pattern === part) { + return true; + } + return false; + }); + if (match) { + return true; + } + } + return false; + } +} +exports.default = PartialMatcher; - getIntermediateBuildDirectory() { - return path__WEBPACK_IMPORTED_MODULE_1___default.a.resolve(this.path, this.getBuildConfig().intermediateBuildDirectory || '.'); - } - getCleanConfig() { - return this.json.kibana && this.json.kibana.clean || {}; - } +/***/ }), +/* 324 */ +/***/ (function(module, exports, __webpack_require__) { - isBazelPackage() { - return this.bazelPackage; - } +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const utils = __webpack_require__(259); +class Matcher { + constructor(_patterns, _settings, _micromatchOptions) { + this._patterns = _patterns; + this._settings = _settings; + this._micromatchOptions = _micromatchOptions; + this._storage = []; + this._fillStorage(); + } + _fillStorage() { + /** + * The original pattern may include `{,*,**,a/*}`, which will lead to problems with matching (unresolved level). + * So, before expand patterns with brace expansion into separated patterns. + */ + const patterns = utils.pattern.expandPatternsWithBraceExpansion(this._patterns); + for (const pattern of patterns) { + const segments = this._getPatternSegments(pattern); + const sections = this._splitSegmentsIntoSections(segments); + this._storage.push({ + complete: sections.length <= 1, + pattern, + segments, + sections + }); + } + } + _getPatternSegments(pattern) { + const parts = utils.pattern.getPatternParts(pattern, this._micromatchOptions); + return parts.map((part) => { + const dynamic = utils.pattern.isDynamicPattern(part, this._settings); + if (!dynamic) { + return { + dynamic: false, + pattern: part + }; + } + return { + dynamic: true, + pattern: part, + patternRe: utils.pattern.makeRe(part, this._micromatchOptions) + }; + }); + } + _splitSegmentsIntoSections(segments) { + return utils.array.splitWhen(segments, (segment) => segment.dynamic && utils.pattern.hasGlobStar(segment.pattern)); + } +} +exports.default = Matcher; - isFlaggedAsDevOnly() { - return !!(this.json.kibana && this.json.kibana.devOnly); - } - hasScript(name) { - return name in this.scripts; - } +/***/ }), +/* 325 */ +/***/ (function(module, exports, __webpack_require__) { - getExecutables() { - const raw = this.json.bin; +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const utils = __webpack_require__(259); +class EntryFilter { + constructor(_settings, _micromatchOptions) { + this._settings = _settings; + this._micromatchOptions = _micromatchOptions; + this.index = new Map(); + } + getFilter(positive, negative) { + const positiveRe = utils.pattern.convertPatternsToRe(positive, this._micromatchOptions); + const negativeRe = utils.pattern.convertPatternsToRe(negative, this._micromatchOptions); + return (entry) => this._filter(entry, positiveRe, negativeRe); + } + _filter(entry, positiveRe, negativeRe) { + if (this._settings.unique && this._isDuplicateEntry(entry)) { + return false; + } + if (this._onlyFileFilter(entry) || this._onlyDirectoryFilter(entry)) { + return false; + } + if (this._isSkippedByAbsoluteNegativePatterns(entry.path, negativeRe)) { + return false; + } + const filepath = this._settings.baseNameMatch ? entry.name : entry.path; + const isMatched = this._isMatchToPatterns(filepath, positiveRe) && !this._isMatchToPatterns(entry.path, negativeRe); + if (this._settings.unique && isMatched) { + this._createIndexRecord(entry); + } + return isMatched; + } + _isDuplicateEntry(entry) { + return this.index.has(entry.path); + } + _createIndexRecord(entry) { + this.index.set(entry.path, undefined); + } + _onlyFileFilter(entry) { + return this._settings.onlyFiles && !entry.dirent.isFile(); + } + _onlyDirectoryFilter(entry) { + return this._settings.onlyDirectories && !entry.dirent.isDirectory(); + } + _isSkippedByAbsoluteNegativePatterns(entryPath, patternsRe) { + if (!this._settings.absolute) { + return false; + } + const fullpath = utils.path.makeAbsolute(this._settings.cwd, entryPath); + return utils.pattern.matchAny(fullpath, patternsRe); + } + _isMatchToPatterns(entryPath, patternsRe) { + const filepath = utils.path.removeLeadingDotSegment(entryPath); + return utils.pattern.matchAny(filepath, patternsRe); + } +} +exports.default = EntryFilter; - if (!raw) { - return {}; - } - if (typeof raw === 'string') { - return { - [this.name]: path__WEBPACK_IMPORTED_MODULE_1___default.a.resolve(this.path, raw) - }; - } +/***/ }), +/* 326 */ +/***/ (function(module, exports, __webpack_require__) { - if (typeof raw === 'object') { - const binsConfig = {}; +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const utils = __webpack_require__(259); +class ErrorFilter { + constructor(_settings) { + this._settings = _settings; + } + getFilter() { + return (error) => this._isNonFatalError(error); + } + _isNonFatalError(error) { + return utils.errno.isEnoentCodeError(error) || this._settings.suppressErrors; + } +} +exports.default = ErrorFilter; - for (const binName of Object.keys(raw)) { - binsConfig[binName] = path__WEBPACK_IMPORTED_MODULE_1___default.a.resolve(this.path, raw[binName]); - } - return binsConfig; - } +/***/ }), +/* 327 */ +/***/ (function(module, exports, __webpack_require__) { - throw new _errors__WEBPACK_IMPORTED_MODULE_3__["CliError"](`[${this.name}] has an invalid "bin" field in its package.json, ` + `expected an object or a string`, { - binConfig: Object(util__WEBPACK_IMPORTED_MODULE_2__["inspect"])(raw), - package: `${this.name} (${this.packageJsonLocation})` - }); - } +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const utils = __webpack_require__(259); +class EntryTransformer { + constructor(_settings) { + this._settings = _settings; + } + getTransformer() { + return (entry) => this._transform(entry); + } + _transform(entry) { + let filepath = entry.path; + if (this._settings.absolute) { + filepath = utils.path.makeAbsolute(this._settings.cwd, filepath); + filepath = utils.path.unixify(filepath); + } + if (this._settings.markDirectories && entry.dirent.isDirectory()) { + filepath += '/'; + } + if (!this._settings.objectMode) { + return filepath; + } + return Object.assign(Object.assign({}, entry), { path: filepath }); + } +} +exports.default = EntryTransformer; - async runScript(scriptName, args = []) { - _log__WEBPACK_IMPORTED_MODULE_4__["log"].info(`Running script [${scriptName}] in [${this.name}]:`); - return Object(_scripts__WEBPACK_IMPORTED_MODULE_6__["runScriptInPackage"])(scriptName, args, this); - } - runScriptStreaming(scriptName, options = {}) { - return Object(_scripts__WEBPACK_IMPORTED_MODULE_6__["runScriptInPackageStreaming"])({ - script: scriptName, - args: options.args || [], - pkg: this, - debug: options.debug - }); - } +/***/ }), +/* 328 */ +/***/ (function(module, exports, __webpack_require__) { - hasDependencies() { - return Object.keys(this.allDependencies).length > 0; - } +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const stream_1 = __webpack_require__(173); +const stream_2 = __webpack_require__(294); +const provider_1 = __webpack_require__(321); +class ProviderStream extends provider_1.default { + constructor() { + super(...arguments); + this._reader = new stream_2.default(this._settings); + } + read(task) { + const root = this._getRootDirectory(task); + const options = this._getReaderOptions(task); + const source = this.api(root, task, options); + const destination = new stream_1.Readable({ objectMode: true, read: () => { } }); + source + .once('error', (error) => destination.emit('error', error)) + .on('data', (entry) => destination.emit('data', options.transform(entry))) + .once('end', () => destination.emit('end')); + destination + .once('close', () => source.destroy()); + return destination; + } + api(root, task, options) { + if (task.dynamic) { + return this._reader.dynamic(root, options); + } + return this._reader.static(task.patterns, options); + } +} +exports.default = ProviderStream; - isEveryDependencyLocal() { - return Object.values(this.allDependencies).every(dep => Object(_package_json__WEBPACK_IMPORTED_MODULE_5__["isLinkDependency"])(dep)); - } - async installDependencies(options = {}) { - _log__WEBPACK_IMPORTED_MODULE_4__["log"].info(`[${this.name}] running yarn`); - _log__WEBPACK_IMPORTED_MODULE_4__["log"].write(''); - await Object(_scripts__WEBPACK_IMPORTED_MODULE_6__["installInDir"])(this.path, options === null || options === void 0 ? void 0 : options.extraArgs); - _log__WEBPACK_IMPORTED_MODULE_4__["log"].write(''); - } +/***/ }), +/* 329 */ +/***/ (function(module, exports, __webpack_require__) { -} // We normalize all path separators to `/` in generated files +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const sync_1 = __webpack_require__(330); +const provider_1 = __webpack_require__(321); +class ProviderSync extends provider_1.default { + constructor() { + super(...arguments); + this._reader = new sync_1.default(this._settings); + } + read(task) { + const root = this._getRootDirectory(task); + const options = this._getReaderOptions(task); + const entries = this.api(root, task, options); + return entries.map(options.transform); + } + api(root, task, options) { + if (task.dynamic) { + return this._reader.dynamic(root, options); + } + return this._reader.static(task.patterns, options); + } +} +exports.default = ProviderSync; -function normalizePath(path) { - return path.replace(/[\\\/]+/g, '/'); -} /***/ }), -/* 343 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* 330 */ +/***/ (function(module, exports, __webpack_require__) { "use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "readPackageJson", function() { return readPackageJson; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "writePackageJson", function() { return writePackageJson; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "createProductionPackageJson", function() { return createProductionPackageJson; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isLinkDependency", function() { return isLinkDependency; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isBazelPackageDependency", function() { return isBazelPackageDependency; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "transformDependencies", function() { return transformDependencies; }); -/* harmony import */ var read_pkg__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(344); -/* harmony import */ var read_pkg__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(read_pkg__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var write_pkg__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(396); -/* harmony import */ var write_pkg__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(write_pkg__WEBPACK_IMPORTED_MODULE_1__); -function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } - -function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } - -function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +Object.defineProperty(exports, "__esModule", { value: true }); +const fsStat = __webpack_require__(295); +const fsWalk = __webpack_require__(300); +const reader_1 = __webpack_require__(320); +class ReaderSync extends reader_1.default { + constructor() { + super(...arguments); + this._walkSync = fsWalk.walkSync; + this._statSync = fsStat.statSync; + } + dynamic(root, options) { + return this._walkSync(root, options); + } + static(patterns, options) { + const entries = []; + for (const pattern of patterns) { + const filepath = this._getFullEntryPath(pattern); + const entry = this._getEntry(filepath, pattern, options); + if (entry === null || !options.entryFilter(entry)) { + continue; + } + entries.push(entry); + } + return entries; + } + _getEntry(filepath, pattern, options) { + try { + const stats = this._getStat(filepath); + return this._makeEntry(stats, pattern); + } + catch (error) { + if (options.errorFilter(error)) { + return null; + } + throw error; + } + } + _getStat(filepath) { + return this._statSync(filepath, this._fsStatSettings); + } +} +exports.default = ReaderSync; -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ +/***/ }), +/* 331 */ +/***/ (function(module, exports, __webpack_require__) { -function readPackageJson(cwd) { - return read_pkg__WEBPACK_IMPORTED_MODULE_0___default()({ - cwd, - normalize: false - }); -} -function writePackageJson(path, json) { - return write_pkg__WEBPACK_IMPORTED_MODULE_1___default()(path, json); -} -const createProductionPackageJson = pkgJson => _objectSpread(_objectSpread({}, pkgJson), {}, { - dependencies: transformDependencies(pkgJson.dependencies) -}); -const isLinkDependency = depVersion => depVersion.startsWith('link:'); -const isBazelPackageDependency = depVersion => depVersion.startsWith('link:bazel-bin/'); -/** - * Replaces `link:` dependencies with `file:` dependencies. When installing - * dependencies, these `file:` dependencies will be copied into `node_modules` - * instead of being symlinked. - * - * This will allow us to copy packages into the build and run `yarn`, which - * will then _copy_ the `file:` dependencies into `node_modules` instead of - * symlinking like we do in development. - * - * Additionally it also taken care of replacing `link:bazel-bin/` with - * `file:` so we can also support the copy of the Bazel packages dist already into - * build/packages to be copied into the node_modules - */ +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DEFAULT_FILE_SYSTEM_ADAPTER = void 0; +const fs = __webpack_require__(132); +const os = __webpack_require__(122); +/** + * The `os.cpus` method can return zero. We expect the number of cores to be greater than zero. + * https://github.com/nodejs/node/blob/7faeddf23a98c53896f8b574a6e66589e8fb1eb8/lib/os.js#L106-L107 + */ +const CPU_COUNT = Math.max(os.cpus().length, 1); +exports.DEFAULT_FILE_SYSTEM_ADAPTER = { + lstat: fs.lstat, + lstatSync: fs.lstatSync, + stat: fs.stat, + statSync: fs.statSync, + readdir: fs.readdir, + readdirSync: fs.readdirSync +}; +class Settings { + constructor(_options = {}) { + this._options = _options; + this.absolute = this._getValue(this._options.absolute, false); + this.baseNameMatch = this._getValue(this._options.baseNameMatch, false); + this.braceExpansion = this._getValue(this._options.braceExpansion, true); + this.caseSensitiveMatch = this._getValue(this._options.caseSensitiveMatch, true); + this.concurrency = this._getValue(this._options.concurrency, CPU_COUNT); + this.cwd = this._getValue(this._options.cwd, process.cwd()); + this.deep = this._getValue(this._options.deep, Infinity); + this.dot = this._getValue(this._options.dot, false); + this.extglob = this._getValue(this._options.extglob, true); + this.followSymbolicLinks = this._getValue(this._options.followSymbolicLinks, true); + this.fs = this._getFileSystemMethods(this._options.fs); + this.globstar = this._getValue(this._options.globstar, true); + this.ignore = this._getValue(this._options.ignore, []); + this.markDirectories = this._getValue(this._options.markDirectories, false); + this.objectMode = this._getValue(this._options.objectMode, false); + this.onlyDirectories = this._getValue(this._options.onlyDirectories, false); + this.onlyFiles = this._getValue(this._options.onlyFiles, true); + this.stats = this._getValue(this._options.stats, false); + this.suppressErrors = this._getValue(this._options.suppressErrors, false); + this.throwErrorOnBrokenSymbolicLink = this._getValue(this._options.throwErrorOnBrokenSymbolicLink, false); + this.unique = this._getValue(this._options.unique, true); + if (this.onlyDirectories) { + this.onlyFiles = false; + } + if (this.stats) { + this.objectMode = true; + } + } + _getValue(option, value) { + return option === undefined ? value : option; + } + _getFileSystemMethods(methods = {}) { + return Object.assign(Object.assign({}, exports.DEFAULT_FILE_SYSTEM_ADAPTER), methods); + } +} +exports.default = Settings; -function transformDependencies(dependencies = {}) { - const newDeps = {}; - for (const name of Object.keys(dependencies)) { - const depVersion = dependencies[name]; +/***/ }), +/* 332 */ +/***/ (function(module, exports, __webpack_require__) { - if (!isLinkDependency(depVersion)) { - newDeps[name] = depVersion; - continue; - } +"use strict"; - if (isBazelPackageDependency(depVersion)) { - newDeps[name] = depVersion.replace('link:bazel-bin/', 'file:'); - continue; - } +const path = __webpack_require__(4); +const pathType = __webpack_require__(333); - newDeps[name] = depVersion.replace('link:', 'file:'); - } +const getExtensions = extensions => extensions.length > 1 ? `{${extensions.join(',')}}` : extensions[0]; - return newDeps; -} +const getPath = (filepath, cwd) => { + const pth = filepath[0] === '!' ? filepath.slice(1) : filepath; + return path.isAbsolute(pth) ? pth : path.join(cwd, pth); +}; -/***/ }), -/* 344 */ -/***/ (function(module, exports, __webpack_require__) { +const addExtensions = (file, extensions) => { + if (path.extname(file)) { + return `**/${file}`; + } -"use strict"; + return `**/${file}.${getExtensions(extensions)}`; +}; -const {promisify} = __webpack_require__(113); -const fs = __webpack_require__(132); -const path = __webpack_require__(4); -const parseJson = __webpack_require__(345); +const getGlob = (directory, options) => { + if (options.files && !Array.isArray(options.files)) { + throw new TypeError(`Expected \`files\` to be of type \`Array\` but received type \`${typeof options.files}\``); + } -const readFileAsync = promisify(fs.readFile); + if (options.extensions && !Array.isArray(options.extensions)) { + throw new TypeError(`Expected \`extensions\` to be of type \`Array\` but received type \`${typeof options.extensions}\``); + } -module.exports = async options => { + if (options.files && options.extensions) { + return options.files.map(x => path.posix.join(directory, addExtensions(x, options.extensions))); + } + + if (options.files) { + return options.files.map(x => path.posix.join(directory, `**/${x}`)); + } + + if (options.extensions) { + return [path.posix.join(directory, `**/*.${getExtensions(options.extensions)}`)]; + } + + return [path.posix.join(directory, '**')]; +}; + +module.exports = async (input, options) => { options = { cwd: process.cwd(), - normalize: true, ...options }; - const filePath = path.resolve(options.cwd, 'package.json'); - const json = parseJson(await readFileAsync(filePath, 'utf8')); - - if (options.normalize) { - __webpack_require__(366)(json); + if (typeof options.cwd !== 'string') { + throw new TypeError(`Expected \`cwd\` to be of type \`string\` but received type \`${typeof options.cwd}\``); } - return json; + const globs = await Promise.all([].concat(input).map(async x => { + const isDirectory = await pathType.isDirectory(getPath(x, options.cwd)); + return isDirectory ? getGlob(x, options) : x; + })); + + return [].concat.apply([], globs); // eslint-disable-line prefer-spread }; -module.exports.sync = options => { +module.exports.sync = (input, options) => { options = { cwd: process.cwd(), - normalize: true, ...options }; - const filePath = path.resolve(options.cwd, 'package.json'); - const json = parseJson(fs.readFileSync(filePath, 'utf8')); - - if (options.normalize) { - __webpack_require__(366)(json); + if (typeof options.cwd !== 'string') { + throw new TypeError(`Expected \`cwd\` to be of type \`string\` but received type \`${typeof options.cwd}\``); } - return json; + const globs = [].concat(input).map(x => pathType.isDirectorySync(getPath(x, options.cwd)) ? getGlob(x, options) : x); + + return [].concat.apply([], globs); // eslint-disable-line prefer-spread }; /***/ }), -/* 345 */ +/* 333 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const errorEx = __webpack_require__(346); -const fallback = __webpack_require__(348); -const {default: LinesAndColumns} = __webpack_require__(349); -const {codeFrameColumns} = __webpack_require__(350); - -const JSONError = errorEx('JSONError', { - fileName: errorEx.append('in %s'), - codeFrame: errorEx.append('\n\n%s\n') -}); +const {promisify} = __webpack_require__(113); +const fs = __webpack_require__(132); -module.exports = (string, reviver, filename) => { - if (typeof reviver === 'string') { - filename = reviver; - reviver = null; +async function isType(fsStatType, statsMethodName, filePath) { + if (typeof filePath !== 'string') { + throw new TypeError(`Expected a string, got ${typeof filePath}`); } try { - try { - return JSON.parse(string, reviver); - } catch (error) { - fallback(string, reviver); - throw error; - } + const stats = await promisify(fs[fsStatType])(filePath); + return stats[statsMethodName](); } catch (error) { - error.message = error.message.replace(/\n/g, ''); - const indexMatch = error.message.match(/in JSON at position (\d+) while parsing near/); - - const jsonError = new JSONError(error); - if (filename) { - jsonError.fileName = filename; + if (error.code === 'ENOENT') { + return false; } - if (indexMatch && indexMatch.length > 0) { - const lines = new LinesAndColumns(string); - const index = Number(indexMatch[1]); - const location = lines.locationForIndex(index); + throw error; + } +} - const codeFrame = codeFrameColumns( - string, - {start: {line: location.line + 1, column: location.column + 1}}, - {highlightCode: true} - ); +function isTypeSync(fsStatType, statsMethodName, filePath) { + if (typeof filePath !== 'string') { + throw new TypeError(`Expected a string, got ${typeof filePath}`); + } - jsonError.codeFrame = codeFrame; + try { + return fs[fsStatType](filePath)[statsMethodName](); + } catch (error) { + if (error.code === 'ENOENT') { + return false; } - throw jsonError; + throw error; } -}; +} + +exports.isFile = isType.bind(null, 'stat', 'isFile'); +exports.isDirectory = isType.bind(null, 'stat', 'isDirectory'); +exports.isSymlink = isType.bind(null, 'lstat', 'isSymbolicLink'); +exports.isFileSync = isTypeSync.bind(null, 'statSync', 'isFile'); +exports.isDirectorySync = isTypeSync.bind(null, 'statSync', 'isDirectory'); +exports.isSymlinkSync = isTypeSync.bind(null, 'lstatSync', 'isSymbolicLink'); /***/ }), -/* 346 */ +/* 334 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; +const {promisify} = __webpack_require__(113); +const fs = __webpack_require__(132); +const path = __webpack_require__(4); +const fastGlob = __webpack_require__(257); +const gitIgnore = __webpack_require__(335); +const slash = __webpack_require__(336); -var util = __webpack_require__(113); -var isArrayish = __webpack_require__(347); - -var errorEx = function errorEx(name, properties) { - if (!name || name.constructor !== String) { - properties = name || {}; - name = Error.name; - } - - var errorExError = function ErrorEXError(message) { - if (!this) { - return new ErrorEXError(message); - } - - message = message instanceof Error - ? message.message - : (message || this.message); - - Error.call(this, message); - Error.captureStackTrace(this, errorExError); - - this.name = name; +const DEFAULT_IGNORE = [ + '**/node_modules/**', + '**/flow-typed/**', + '**/coverage/**', + '**/.git' +]; - Object.defineProperty(this, 'message', { - configurable: true, - enumerable: false, - get: function () { - var newMessage = message.split(/\r?\n/g); +const readFileP = promisify(fs.readFile); - for (var key in properties) { - if (!properties.hasOwnProperty(key)) { - continue; - } +const mapGitIgnorePatternTo = base => ignore => { + if (ignore.startsWith('!')) { + return '!' + path.posix.join(base, ignore.slice(1)); + } - var modifier = properties[key]; + return path.posix.join(base, ignore); +}; - if ('message' in modifier) { - newMessage = modifier.message(this[key], newMessage) || newMessage; - if (!isArrayish(newMessage)) { - newMessage = [newMessage]; - } - } - } +const parseGitIgnore = (content, options) => { + const base = slash(path.relative(options.cwd, path.dirname(options.fileName))); - return newMessage.join('\n'); - }, - set: function (v) { - message = v; - } - }); + return content + .split(/\r?\n/) + .filter(Boolean) + .filter(line => !line.startsWith('#')) + .map(mapGitIgnorePatternTo(base)); +}; - var stackDescriptor = Object.getOwnPropertyDescriptor(this, 'stack'); - var stackGetter = stackDescriptor.get; - var stackValue = stackDescriptor.value; - delete stackDescriptor.value; - delete stackDescriptor.writable; +const reduceIgnore = files => { + return files.reduce((ignores, file) => { + ignores.add(parseGitIgnore(file.content, { + cwd: file.cwd, + fileName: file.filePath + })); + return ignores; + }, gitIgnore()); +}; - stackDescriptor.get = function () { - var stack = (stackGetter) - ? stackGetter.call(this).split(/\r?\n+/g) - : stackValue.split(/\r?\n+/g); +const ensureAbsolutePathForCwd = (cwd, p) => { + if (path.isAbsolute(p)) { + if (p.startsWith(cwd)) { + return p; + } - // starting in Node 7, the stack builder caches the message. - // just replace it. - stack[0] = this.name + ': ' + this.message; + throw new Error(`Path ${p} is not in cwd ${cwd}`); + } - var lineCount = 1; - for (var key in properties) { - if (!properties.hasOwnProperty(key)) { - continue; - } + return path.join(cwd, p); +}; - var modifier = properties[key]; +const getIsIgnoredPredecate = (ignores, cwd) => { + return p => ignores.ignores(slash(path.relative(cwd, ensureAbsolutePathForCwd(cwd, p)))); +}; - if ('line' in modifier) { - var line = modifier.line(this[key]); - if (line) { - stack.splice(lineCount++, 0, ' ' + line); - } - } +const getFile = async (file, cwd) => { + const filePath = path.join(cwd, file); + const content = await readFileP(filePath, 'utf8'); - if ('stack' in modifier) { - modifier.stack(this[key], stack); - } - } + return { + cwd, + filePath, + content + }; +}; - return stack.join('\n'); - }; +const getFileSync = (file, cwd) => { + const filePath = path.join(cwd, file); + const content = fs.readFileSync(filePath, 'utf8'); - Object.defineProperty(this, 'stack', stackDescriptor); + return { + cwd, + filePath, + content }; +}; - if (Object.setPrototypeOf) { - Object.setPrototypeOf(errorExError.prototype, Error.prototype); - Object.setPrototypeOf(errorExError, Error); - } else { - util.inherits(errorExError, Error); - } - - return errorExError; +const normalizeOptions = ({ + ignore = [], + cwd = slash(process.cwd()) +} = {}) => { + return {ignore, cwd}; }; -errorEx.append = function (str, def) { - return { - message: function (v, message) { - v = v || def; +module.exports = async options => { + options = normalizeOptions(options); - if (v) { - message[0] += ' ' + str.replace('%s', v.toString()); - } + const paths = await fastGlob('**/.gitignore', { + ignore: DEFAULT_IGNORE.concat(options.ignore), + cwd: options.cwd + }); - return message; - } - }; + const files = await Promise.all(paths.map(file => getFile(file, options.cwd))); + const ignores = reduceIgnore(files); + + return getIsIgnoredPredecate(ignores, options.cwd); }; -errorEx.line = function (str, def) { - return { - line: function (v) { - v = v || def; +module.exports.sync = options => { + options = normalizeOptions(options); - if (v) { - return str.replace('%s', v.toString()); - } + const paths = fastGlob.sync('**/.gitignore', { + ignore: DEFAULT_IGNORE.concat(options.ignore), + cwd: options.cwd + }); - return null; - } - }; -}; + const files = paths.map(file => getFileSync(file, options.cwd)); + const ignores = reduceIgnore(files); -module.exports = errorEx; + return getIsIgnoredPredecate(ignores, options.cwd); +}; /***/ }), -/* 347 */ -/***/ (function(module, exports, __webpack_require__) { +/* 335 */ +/***/ (function(module, exports) { -"use strict"; +// A simple implementation of make-array +function makeArray (subject) { + return Array.isArray(subject) + ? subject + : [subject] +} +const EMPTY = '' +const SPACE = ' ' +const ESCAPE = '\\' +const REGEX_TEST_BLANK_LINE = /^\s+$/ +const REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION = /^\\!/ +const REGEX_REPLACE_LEADING_EXCAPED_HASH = /^\\#/ +const REGEX_SPLITALL_CRLF = /\r?\n/g +// /foo, +// ./foo, +// ../foo, +// . +// .. +const REGEX_TEST_INVALID_PATH = /^\.*\/|^\.+$/ -module.exports = function isArrayish(obj) { - if (!obj) { - return false; - } +const SLASH = '/' +const KEY_IGNORE = typeof Symbol !== 'undefined' + ? Symbol.for('node-ignore') + /* istanbul ignore next */ + : 'node-ignore' - return obj instanceof Array || Array.isArray(obj) || - (obj.length >= 0 && obj.splice instanceof Function); -}; +const define = (object, key, value) => + Object.defineProperty(object, key, {value}) +const REGEX_REGEXP_RANGE = /([0-z])-([0-z])/g -/***/ }), -/* 348 */ -/***/ (function(module, exports, __webpack_require__) { +// Sanitize the range of a regular expression +// The cases are complicated, see test cases for details +const sanitizeRange = range => range.replace( + REGEX_REGEXP_RANGE, + (match, from, to) => from.charCodeAt(0) <= to.charCodeAt(0) + ? match + // Invalid range (out of order) which is ok for gitignore rules but + // fatal for JavaScript regular expression, so eliminate it. + : EMPTY +) -"use strict"; +// See fixtures #59 +const cleanRangeBackSlash = slashes => { + const {length} = slashes + return slashes.slice(0, length - length % 2) +} +// > If the pattern ends with a slash, +// > it is removed for the purpose of the following description, +// > but it would only find a match with a directory. +// > In other words, foo/ will match a directory foo and paths underneath it, +// > but will not match a regular file or a symbolic link foo +// > (this is consistent with the way how pathspec works in general in Git). +// '`foo/`' will not match regular file '`foo`' or symbolic link '`foo`' +// -> ignore-rules will not deal with it, because it costs extra `fs.stat` call +// you could use option `mark: true` with `glob` -module.exports = parseJson -function parseJson (txt, reviver, context) { - context = context || 20 - try { - return JSON.parse(txt, reviver) - } catch (e) { - if (typeof txt !== 'string') { - const isEmptyArray = Array.isArray(txt) && txt.length === 0 - const errorMessage = 'Cannot parse ' + - (isEmptyArray ? 'an empty array' : String(txt)) - throw new TypeError(errorMessage) - } - const syntaxErr = e.message.match(/^Unexpected token.*position\s+(\d+)/i) - const errIdx = syntaxErr - ? +syntaxErr[1] - : e.message.match(/^Unexpected end of JSON.*/i) - ? txt.length - 1 - : null - if (errIdx != null) { - const start = errIdx <= context - ? 0 - : errIdx - context - const end = errIdx + context >= txt.length - ? txt.length - : errIdx + context - e.message += ` while parsing near '${ - start === 0 ? '' : '...' - }${txt.slice(start, end)}${ - end === txt.length ? '' : '...' - }'` - } else { - e.message += ` while parsing '${txt.slice(0, context * 2)}'` - } - throw e - } -} +// '`foo/`' should not continue with the '`..`' +const REPLACERS = [ + // > Trailing spaces are ignored unless they are quoted with backslash ("\") + [ + // (a\ ) -> (a ) + // (a ) -> (a) + // (a \ ) -> (a ) + /\\?\s+$/, + match => match.indexOf('\\') === 0 + ? SPACE + : EMPTY + ], -/***/ }), -/* 349 */ -/***/ (function(__webpack_module__, __webpack_exports__, __webpack_require__) { + // replace (\ ) with ' ' + [ + /\\\s/g, + () => SPACE + ], -"use strict"; -__webpack_require__.r(__webpack_exports__); -var LF = '\n'; -var CR = '\r'; -var LinesAndColumns = (function () { - function LinesAndColumns(string) { - this.string = string; - var offsets = [0]; - for (var offset = 0; offset < string.length;) { - switch (string[offset]) { - case LF: - offset += LF.length; - offsets.push(offset); - break; - case CR: - offset += CR.length; - if (string[offset] === LF) { - offset += LF.length; - } - offsets.push(offset); - break; - default: - offset++; - break; - } - } - this.offsets = offsets; - } - LinesAndColumns.prototype.locationForIndex = function (index) { - if (index < 0 || index > this.string.length) { - return null; - } - var line = 0; - var offsets = this.offsets; - while (offsets[line + 1] <= index) { - line++; - } - var column = index - offsets[line]; - return { line: line, column: column }; - }; - LinesAndColumns.prototype.indexForLocation = function (location) { - var line = location.line, column = location.column; - if (line < 0 || line >= this.offsets.length) { - return null; - } - if (column < 0 || column > this.lengthOfLine(line)) { - return null; - } - return this.offsets[line] + column; - }; - LinesAndColumns.prototype.lengthOfLine = function (line) { - var offset = this.offsets[line]; - var nextOffset = line === this.offsets.length - 1 ? this.string.length : this.offsets[line + 1]; - return nextOffset - offset; - }; - return LinesAndColumns; -}()); -/* harmony default export */ __webpack_exports__["default"] = (LinesAndColumns); + // Escape metacharacters + // which is written down by users but means special for regular expressions. + // > There are 12 characters with special meanings: + // > - the backslash \, + // > - the caret ^, + // > - the dollar sign $, + // > - the period or dot ., + // > - the vertical bar or pipe symbol |, + // > - the question mark ?, + // > - the asterisk or star *, + // > - the plus sign +, + // > - the opening parenthesis (, + // > - the closing parenthesis ), + // > - and the opening square bracket [, + // > - the opening curly brace {, + // > These special characters are often called "metacharacters". + [ + /[\\$.|*+(){^]/g, + match => `\\${match}` + ], -/***/ }), -/* 350 */ -/***/ (function(module, exports, __webpack_require__) { + [ + // > a question mark (?) matches a single character + /(?!\\)\?/g, + () => '[^/]' + ], -"use strict"; + // leading slash + [ + // > A leading slash matches the beginning of the pathname. + // > For example, "/*.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c". + // A leading slash matches the beginning of the pathname + /^\//, + () => '^' + ], -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.codeFrameColumns = codeFrameColumns; -exports.default = _default; + // replace special metacharacter slash after the leading slash + [ + /\//g, + () => '\\/' + ], -var _highlight = _interopRequireWildcard(__webpack_require__(351)); + [ + // > A leading "**" followed by a slash means match in all directories. + // > For example, "**/foo" matches file or directory "foo" anywhere, + // > the same as pattern "foo". + // > "**/foo/bar" matches file or directory "bar" anywhere that is directly + // > under directory "foo". + // Notice that the '*'s have been replaced as '\\*' + /^\^*\\\*\\\*\\\//, -function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; } + // '**/foo' <-> 'foo' + () => '^(?:.*\\/)?' + ], -function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } + // starting + [ + // there will be no leading '/' + // (which has been replaced by section "leading slash") + // If starts with '**', adding a '^' to the regular expression also works + /^(?=[^^])/, + function startingReplacer () { + // If has a slash `/` at the beginning or middle + return !/\/(?!$)/.test(this) + // > Prior to 2.22.1 + // > If the pattern does not contain a slash /, + // > Git treats it as a shell glob pattern + // Actually, if there is only a trailing slash, + // git also treats it as a shell glob pattern -let deprecationWarningShown = false; + // After 2.22.1 (compatible but clearer) + // > If there is a separator at the beginning or middle (or both) + // > of the pattern, then the pattern is relative to the directory + // > level of the particular .gitignore file itself. + // > Otherwise the pattern may also match at any level below + // > the .gitignore level. + ? '(?:^|\\/)' -function getDefs(chalk) { - return { - gutter: chalk.grey, - marker: chalk.red.bold, - message: chalk.red.bold - }; -} + // > Otherwise, Git treats the pattern as a shell glob suitable for + // > consumption by fnmatch(3) + : '^' + } + ], -const NEWLINE = /\r\n|[\n\r\u2028\u2029]/; + // two globstars + [ + // Use lookahead assertions so that we could match more than one `'/**'` + /\\\/\\\*\\\*(?=\\\/|$)/g, -function getMarkerLines(loc, source, opts) { - const startLoc = Object.assign({ - column: 0, - line: -1 - }, loc.start); - const endLoc = Object.assign({}, startLoc, loc.end); - const { - linesAbove = 2, - linesBelow = 3 - } = opts || {}; - const startLine = startLoc.line; - const startColumn = startLoc.column; - const endLine = endLoc.line; - const endColumn = endLoc.column; - let start = Math.max(startLine - (linesAbove + 1), 0); - let end = Math.min(source.length, endLine + linesBelow); + // Zero, one or several directories + // should not use '*', or it will be replaced by the next replacer - if (startLine === -1) { - start = 0; - } + // Check if it is not the last `'/**'` + (_, index, str) => index + 6 < str.length - if (endLine === -1) { - end = source.length; - } + // case: /**/ + // > A slash followed by two consecutive asterisks then a slash matches + // > zero or more directories. + // > For example, "a/**/b" matches "a/b", "a/x/b", "a/x/y/b" and so on. + // '/**/' + ? '(?:\\/[^\\/]+)*' - const lineDiff = endLine - startLine; - const markerLines = {}; + // case: /** + // > A trailing `"/**"` matches everything inside. - if (lineDiff) { - for (let i = 0; i <= lineDiff; i++) { - const lineNumber = i + startLine; + // #21: everything inside but it should not include the current folder + : '\\/.+' + ], - if (!startColumn) { - markerLines[lineNumber] = true; - } else if (i === 0) { - const sourceLength = source[lineNumber - 1].length; - markerLines[lineNumber] = [startColumn, sourceLength - startColumn + 1]; - } else if (i === lineDiff) { - markerLines[lineNumber] = [0, endColumn]; - } else { - const sourceLength = source[lineNumber - i].length; - markerLines[lineNumber] = [0, sourceLength]; - } - } - } else { - if (startColumn === endColumn) { - if (startColumn) { - markerLines[startLine] = [startColumn, 0]; - } else { - markerLines[startLine] = true; - } - } else { - markerLines[startLine] = [startColumn, endColumn - startColumn]; - } - } + // intermediate wildcards + [ + // Never replace escaped '*' + // ignore rule '\*' will match the path '*' - return { - start, - end, - markerLines - }; -} + // 'abc.*/' -> go + // 'abc.*' -> skip this rule + /(^|[^\\]+)\\\*(?=.+)/g, -function codeFrameColumns(rawLines, loc, opts = {}) { - const highlighted = (opts.highlightCode || opts.forceColor) && (0, _highlight.shouldHighlight)(opts); - const chalk = (0, _highlight.getChalk)(opts); - const defs = getDefs(chalk); + // '*.js' matches '.js' + // '*.js' doesn't match 'abc' + (_, p1) => `${p1}[^\\/]*` + ], - const maybeHighlight = (chalkFn, string) => { - return highlighted ? chalkFn(string) : string; - }; + [ + // unescape, revert step 3 except for back slash + // For example, if a user escape a '\\*', + // after step 3, the result will be '\\\\\\*' + /\\\\\\(?=[$.|*+(){^])/g, + () => ESCAPE + ], - const lines = rawLines.split(NEWLINE); - const { - start, - end, - markerLines - } = getMarkerLines(loc, lines, opts); - const hasColumns = loc.start && typeof loc.start.column === "number"; - const numberMaxWidth = String(end).length; - const highlightedLines = highlighted ? (0, _highlight.default)(rawLines, opts) : rawLines; - let frame = highlightedLines.split(NEWLINE).slice(start, end).map((line, index) => { - const number = start + 1 + index; - const paddedNumber = ` ${number}`.slice(-numberMaxWidth); - const gutter = ` ${paddedNumber} |`; - const hasMarker = markerLines[number]; - const lastMarkerLine = !markerLines[number + 1]; + [ + // '\\\\' -> '\\' + /\\\\/g, + () => ESCAPE + ], - if (hasMarker) { - let markerLine = ""; + [ + // > The range notation, e.g. [a-zA-Z], + // > can be used to match one of the characters in a range. - if (Array.isArray(hasMarker)) { - const markerSpacing = line.slice(0, Math.max(hasMarker[0] - 1, 0)).replace(/[^\t]/g, " "); - const numberOfMarkers = hasMarker[1] || 1; - markerLine = ["\n ", maybeHighlight(defs.gutter, gutter.replace(/\d/g, " ")), " ", markerSpacing, maybeHighlight(defs.marker, "^").repeat(numberOfMarkers)].join(""); + // `\` is escaped by step 3 + /(\\)?\[([^\]/]*?)(\\*)($|\])/g, + (match, leadEscape, range, endEscape, close) => leadEscape === ESCAPE + // '\\[bar]' -> '\\\\[bar\\]' + ? `\\[${range}${cleanRangeBackSlash(endEscape)}${close}` + : close === ']' + ? endEscape.length % 2 === 0 + // A normal case, and it is a range notation + // '[bar]' + // '[bar\\\\]' + ? `[${sanitizeRange(range)}${endEscape}]` + // Invalid range notaton + // '[bar\\]' -> '[bar\\\\]' + : '[]' + : '[]' + ], - if (lastMarkerLine && opts.message) { - markerLine += " " + maybeHighlight(defs.message, opts.message); - } - } + // ending + [ + // 'js' will not match 'js.' + // 'ab' will not match 'abc' + /(?:[^*])$/, - return [maybeHighlight(defs.marker, ">"), maybeHighlight(defs.gutter, gutter), line.length > 0 ? ` ${line}` : "", markerLine].join(""); - } else { - return ` ${maybeHighlight(defs.gutter, gutter)}${line.length > 0 ? ` ${line}` : ""}`; - } - }).join("\n"); + // WTF! + // https://git-scm.com/docs/gitignore + // changes in [2.22.1](https://git-scm.com/docs/gitignore/2.22.1) + // which re-fixes #24, #38 - if (opts.message && !hasColumns) { - frame = `${" ".repeat(numberMaxWidth + 1)}${opts.message}\n${frame}`; - } + // > If there is a separator at the end of the pattern then the pattern + // > will only match directories, otherwise the pattern can match both + // > files and directories. - if (highlighted) { - return chalk.reset(frame); - } else { - return frame; - } -} + // 'js*' will not match 'a.js' + // 'js/' will not match 'a.js' + // 'js' will match 'a.js' and 'a.js/' + match => /\/$/.test(match) + // foo/ will not match 'foo' + ? `${match}$` + // foo matches 'foo' and 'foo/' + : `${match}(?=$|\\/$)` + ], -function _default(rawLines, lineNumber, colNumber, opts = {}) { - if (!deprecationWarningShown) { - deprecationWarningShown = true; - const message = "Passing lineNumber and colNumber is deprecated to @babel/code-frame. Please use `codeFrameColumns`."; + // trailing wildcard + [ + /(\^|\\\/)?\\\*$/, + (_, p1) => { + const prefix = p1 + // '\^': + // '/*' does not match EMPTY + // '/*' does not match everything - if (process.emitWarning) { - process.emitWarning(message, "DeprecationWarning"); - } else { - const deprecationError = new Error(message); - deprecationError.name = "DeprecationWarning"; - console.warn(new Error(message)); + // '\\\/': + // 'abc/*' does not match 'abc/' + ? `${p1}[^/]+` + + // 'a*' matches 'a' + // 'a*' matches 'aa' + : '[^/]*' + + return `${prefix}(?=$|\\/$)` } + ], +] + +// A simple cache, because an ignore rule only has only one certain meaning +const regexCache = Object.create(null) + +// @param {pattern} +const makeRegex = (pattern, negative, ignorecase) => { + const r = regexCache[pattern] + if (r) { + return r } - colNumber = Math.max(colNumber, 0); - const location = { - start: { - column: colNumber, - line: lineNumber - } - }; - return codeFrameColumns(rawLines, location, opts); -} + // const replacers = negative + // ? NEGATIVE_REPLACERS + // : POSITIVE_REPLACERS -/***/ }), -/* 351 */ -/***/ (function(module, exports, __webpack_require__) { + const source = REPLACERS.reduce( + (prev, current) => prev.replace(current[0], current[1].bind(pattern)), + pattern + ) -"use strict"; + return regexCache[pattern] = ignorecase + ? new RegExp(source, 'i') + : new RegExp(source) +} +const isString = subject => typeof subject === 'string' -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.shouldHighlight = shouldHighlight; -exports.getChalk = getChalk; -exports.default = highlight; +// > A blank line matches no files, so it can serve as a separator for readability. +const checkPattern = pattern => pattern + && isString(pattern) + && !REGEX_TEST_BLANK_LINE.test(pattern) -var jsTokensNs = _interopRequireWildcard(__webpack_require__(352)); + // > A line starting with # serves as a comment. + && pattern.indexOf('#') !== 0 -var _helperValidatorIdentifier = __webpack_require__(353); +const splitPattern = pattern => pattern.split(REGEX_SPLITALL_CRLF) -var _chalk = _interopRequireDefault(__webpack_require__(356)); +class IgnoreRule { + constructor ( + origin, + pattern, + negative, + regex + ) { + this.origin = origin + this.pattern = pattern + this.negative = negative + this.regex = regex + } +} -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +const createRule = (pattern, ignorecase) => { + const origin = pattern + let negative = false -function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; } + // > An optional prefix "!" which negates the pattern; + if (pattern.indexOf('!') === 0) { + negative = true + pattern = pattern.substr(1) + } -function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } + pattern = pattern + // > Put a backslash ("\") in front of the first "!" for patterns that + // > begin with a literal "!", for example, `"\!important!.txt"`. + .replace(REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION, '!') + // > Put a backslash ("\") in front of the first hash for patterns that + // > begin with a hash. + .replace(REGEX_REPLACE_LEADING_EXCAPED_HASH, '#') -const sometimesKeywords = new Set(["as", "async", "from", "get", "of", "set"]); + const regex = makeRegex(pattern, negative, ignorecase) -function getDefs(chalk) { - return { - keyword: chalk.cyan, - capitalized: chalk.yellow, - jsxIdentifier: chalk.yellow, - punctuator: chalk.yellow, - number: chalk.magenta, - string: chalk.green, - regex: chalk.magenta, - comment: chalk.grey, - invalid: chalk.white.bgRed.bold - }; + return new IgnoreRule( + origin, + pattern, + negative, + regex + ) } -const NEWLINE = /\r\n|[\n\r\u2028\u2029]/; -const BRACKET = /^[()[\]{}]$/; -let tokenize; -{ - const { - matchToToken - } = jsTokensNs; - const JSX_TAG = /^[a-z][\w-]*$/i; - - const getTokenType = function (token, offset, text) { - if (token.type === "name") { - if ((0, _helperValidatorIdentifier.isKeyword)(token.value) || (0, _helperValidatorIdentifier.isStrictReservedWord)(token.value, true) || sometimesKeywords.has(token.value)) { - return "keyword"; - } +const throwError = (message, Ctor) => { + throw new Ctor(message) +} - if (JSX_TAG.test(token.value) && (text[offset - 1] === "<" || text.substr(offset - 2, 2) == " { + if (!isString(path)) { + return doThrow( + `path must be a string, but got \`${originalPath}\``, + TypeError + ) + } - if (token.value[0] !== token.value[0].toLowerCase()) { - return "capitalized"; - } - } + // We don't know if we should ignore EMPTY, so throw + if (!path) { + return doThrow(`path must not be empty`, TypeError) + } - if (token.type === "punctuator" && BRACKET.test(token.value)) { - return "bracket"; - } + // Check if it is a relative path + if (checkPath.isNotRelative(path)) { + const r = '`path.relative()`d' + return doThrow( + `path should be a ${r} string, but got "${originalPath}"`, + RangeError + ) + } - if (token.type === "invalid" && (token.value === "@" || token.value === "#")) { - return "punctuator"; - } + return true +} - return token.type; - }; +const isNotRelative = path => REGEX_TEST_INVALID_PATH.test(path) - tokenize = function* (text) { - let match; +checkPath.isNotRelative = isNotRelative +checkPath.convert = p => p - while (match = jsTokensNs.default.exec(text)) { - const token = matchToToken(match); - yield { - type: getTokenType(token, match.index, text), - value: token.value - }; - } - }; -} +class Ignore { + constructor ({ + ignorecase = true + } = {}) { + this._rules = [] + this._ignorecase = ignorecase + define(this, KEY_IGNORE, true) + this._initCache() + } -function highlightTokens(defs, text) { - let highlighted = ""; + _initCache () { + this._ignoreCache = Object.create(null) + this._testCache = Object.create(null) + } - for (const { - type, - value - } of tokenize(text)) { - const colorize = defs[type]; + _addPattern (pattern) { + // #32 + if (pattern && pattern[KEY_IGNORE]) { + this._rules = this._rules.concat(pattern._rules) + this._added = true + return + } - if (colorize) { - highlighted += value.split(NEWLINE).map(str => colorize(str)).join("\n"); - } else { - highlighted += value; + if (checkPattern(pattern)) { + const rule = createRule(pattern, this._ignorecase) + this._added = true + this._rules.push(rule) } } - return highlighted; -} + // @param {Array | string | Ignore} pattern + add (pattern) { + this._added = false -function shouldHighlight(options) { - return !!_chalk.default.supportsColor || options.forceColor; -} + makeArray( + isString(pattern) + ? splitPattern(pattern) + : pattern + ).forEach(this._addPattern, this) -function getChalk(options) { - return options.forceColor ? new _chalk.default.constructor({ - enabled: true, - level: 1 - }) : _chalk.default; -} + // Some rules have just added to the ignore, + // making the behavior changed. + if (this._added) { + this._initCache() + } -function highlight(code, options = {}) { - if (shouldHighlight(options)) { - const chalk = getChalk(options); - const defs = getDefs(chalk); - return highlightTokens(defs, code); - } else { - return code; + return this } -} - -/***/ }), -/* 352 */ -/***/ (function(module, exports) { -// Copyright 2014, 2015, 2016, 2017, 2018 Simon Lydell -// License: MIT. (See LICENSE.) + // legacy + addPattern (pattern) { + return this.add(pattern) + } -Object.defineProperty(exports, "__esModule", { - value: true -}) + // | ignored : unignored + // negative | 0:0 | 0:1 | 1:0 | 1:1 + // -------- | ------- | ------- | ------- | -------- + // 0 | TEST | TEST | SKIP | X + // 1 | TESTIF | SKIP | TEST | X -// This regex comes from regex.coffee, and is inserted here by generate-index.js -// (run `npm run build`). -exports.default = /((['"])(?:(?!\2|\\).|\\(?:\r\n|[\s\S]))*(\2)?|`(?:[^`\\$]|\\[\s\S]|\$(?!\{)|\$\{(?:[^{}]|\{[^}]*\}?)*\}?)*(`)?)|(\/\/.*)|(\/\*(?:[^*]|\*(?!\/))*(\*\/)?)|(\/(?!\*)(?:\[(?:(?![\]\\]).|\\.)*\]|(?![\/\]\\]).|\\.)+\/(?:(?!\s*(?:\b|[\u0080-\uFFFF$\\'"~({]|[+\-!](?!=)|\.?\d))|[gmiyus]{1,6}\b(?![\u0080-\uFFFF$\\]|\s*(?:[+\-*%&|^<>!=?({]|\/(?![\/*])))))|(0[xX][\da-fA-F]+|0[oO][0-7]+|0[bB][01]+|(?:\d*\.\d+|\d+\.?)(?:[eE][+-]?\d+)?)|((?!\d)(?:(?!\s)[$\w\u0080-\uFFFF]|\\u[\da-fA-F]{4}|\\u\{[\da-fA-F]+\})+)|(--|\+\+|&&|\|\||=>|\.{3}|(?:[+\-\/%&|^]|\*{1,2}|<{1,2}|>{1,3}|!=?|={1,2})=?|[?~.,:;[\](){}])|(\s+)|(^$|[\s\S])/g + // - SKIP: always skip + // - TEST: always test + // - TESTIF: only test if checkUnignored + // - X: that never happen -exports.matchToToken = function(match) { - var token = {type: "invalid", value: match[0], closed: undefined} - if (match[ 1]) token.type = "string" , token.closed = !!(match[3] || match[4]) - else if (match[ 5]) token.type = "comment" - else if (match[ 6]) token.type = "comment", token.closed = !!match[7] - else if (match[ 8]) token.type = "regex" - else if (match[ 9]) token.type = "number" - else if (match[10]) token.type = "name" - else if (match[11]) token.type = "punctuator" - else if (match[12]) token.type = "whitespace" - return token -} + // @param {boolean} whether should check if the path is unignored, + // setting `checkUnignored` to `false` could reduce additional + // path matching. + // @returns {TestResult} true if a file is ignored + _testOne (path, checkUnignored) { + let ignored = false + let unignored = false -/***/ }), -/* 353 */ -/***/ (function(module, exports, __webpack_require__) { + this._rules.forEach(rule => { + const {negative} = rule + if ( + unignored === negative && ignored !== unignored + || negative && !ignored && !unignored && !checkUnignored + ) { + return + } -"use strict"; + const matched = rule.regex.test(path) + if (matched) { + ignored = !negative + unignored = negative + } + }) -Object.defineProperty(exports, "__esModule", { - value: true -}); -Object.defineProperty(exports, "isIdentifierName", { - enumerable: true, - get: function () { - return _identifier.isIdentifierName; - } -}); -Object.defineProperty(exports, "isIdentifierChar", { - enumerable: true, - get: function () { - return _identifier.isIdentifierChar; - } -}); -Object.defineProperty(exports, "isIdentifierStart", { - enumerable: true, - get: function () { - return _identifier.isIdentifierStart; - } -}); -Object.defineProperty(exports, "isReservedWord", { - enumerable: true, - get: function () { - return _keyword.isReservedWord; - } -}); -Object.defineProperty(exports, "isStrictBindOnlyReservedWord", { - enumerable: true, - get: function () { - return _keyword.isStrictBindOnlyReservedWord; - } -}); -Object.defineProperty(exports, "isStrictBindReservedWord", { - enumerable: true, - get: function () { - return _keyword.isStrictBindReservedWord; - } -}); -Object.defineProperty(exports, "isStrictReservedWord", { - enumerable: true, - get: function () { - return _keyword.isStrictReservedWord; - } -}); -Object.defineProperty(exports, "isKeyword", { - enumerable: true, - get: function () { - return _keyword.isKeyword; + return { + ignored, + unignored + } } -}); -var _identifier = __webpack_require__(354); + // @returns {TestResult} + _test (originalPath, cache, checkUnignored, slices) { + const path = originalPath + // Supports nullable path + && checkPath.convert(originalPath) + + checkPath(path, originalPath, throwError) -var _keyword = __webpack_require__(355); + return this._t(path, cache, checkUnignored, slices) + } -/***/ }), -/* 354 */ -/***/ (function(module, exports, __webpack_require__) { + _t (path, cache, checkUnignored, slices) { + if (path in cache) { + return cache[path] + } -"use strict"; + if (!slices) { + // path/to/a.js + // ['path', 'to', 'a.js'] + slices = path.split(SLASH) + } + slices.pop() -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.isIdentifierStart = isIdentifierStart; -exports.isIdentifierChar = isIdentifierChar; -exports.isIdentifierName = isIdentifierName; -let nonASCIIidentifierStartChars = "\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u037f\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u052f\u0531-\u0556\u0559\u0560-\u0588\u05d0-\u05ea\u05ef-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u0860-\u086a\u08a0-\u08b4\u08b6-\u08c7\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u09fc\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0af9\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d\u0c58-\u0c5a\u0c60\u0c61\u0c80\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d04-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d54-\u0d56\u0d5f-\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e86-\u0e8a\u0e8c-\u0ea3\u0ea5\u0ea7-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f5\u13f8-\u13fd\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f8\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1878\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191e\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1c80-\u1c88\u1c90-\u1cba\u1cbd-\u1cbf\u1ce9-\u1cec\u1cee-\u1cf3\u1cf5\u1cf6\u1cfa\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2118-\u211d\u2124\u2126\u2128\u212a-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309b-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312f\u3131-\u318e\u31a0-\u31bf\u31f0-\u31ff\u3400-\u4dbf\u4e00-\u9ffc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua69d\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua7bf\ua7c2-\ua7ca\ua7f5-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua8fd\ua8fe\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\ua9e0-\ua9e4\ua9e6-\ua9ef\ua9fa-\ua9fe\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa7e-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab5a\uab5c-\uab69\uab70-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc"; -let nonASCIIidentifierChars = "\u200c\u200d\xb7\u0300-\u036f\u0387\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u0669\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7\u06e8\u06ea-\u06ed\u06f0-\u06f9\u0711\u0730-\u074a\u07a6-\u07b0\u07c0-\u07c9\u07eb-\u07f3\u07fd\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u08d3-\u08e1\u08e3-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09cb-\u09cd\u09d7\u09e2\u09e3\u09e6-\u09ef\u09fe\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2\u0ae3\u0ae6-\u0aef\u0afa-\u0aff\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b55-\u0b57\u0b62\u0b63\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c00-\u0c04\u0c3e-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0c66-\u0c6f\u0c81-\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0ce6-\u0cef\u0d00-\u0d03\u0d3b\u0d3c\u0d3e-\u0d44\u0d46-\u0d48\u0d4a-\u0d4d\u0d57\u0d62\u0d63\u0d66-\u0d6f\u0d81-\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0de6-\u0def\u0df2\u0df3\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0e50-\u0e59\u0eb1\u0eb4-\u0ebc\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e\u0f3f\u0f71-\u0f84\u0f86\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102b-\u103e\u1040-\u1049\u1056-\u1059\u105e-\u1060\u1062-\u1064\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u1369-\u1371\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b4-\u17d3\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u18a9\u1920-\u192b\u1930-\u193b\u1946-\u194f\u19d0-\u19da\u1a17-\u1a1b\u1a55-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1ab0-\u1abd\u1abf\u1ac0\u1b00-\u1b04\u1b34-\u1b44\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1b82\u1ba1-\u1bad\u1bb0-\u1bb9\u1be6-\u1bf3\u1c24-\u1c37\u1c40-\u1c49\u1c50-\u1c59\u1cd0-\u1cd2\u1cd4-\u1ce8\u1ced\u1cf4\u1cf7-\u1cf9\u1dc0-\u1df9\u1dfb-\u1dff\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua620-\ua629\ua66f\ua674-\ua67d\ua69e\ua69f\ua6f0\ua6f1\ua802\ua806\ua80b\ua823-\ua827\ua82c\ua880\ua881\ua8b4-\ua8c5\ua8d0-\ua8d9\ua8e0-\ua8f1\ua8ff-\ua909\ua926-\ua92d\ua947-\ua953\ua980-\ua983\ua9b3-\ua9c0\ua9d0-\ua9d9\ua9e5\ua9f0-\ua9f9\uaa29-\uaa36\uaa43\uaa4c\uaa4d\uaa50-\uaa59\uaa7b-\uaa7d\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uaaeb-\uaaef\uaaf5\uaaf6\uabe3-\uabea\uabec\uabed\uabf0-\uabf9\ufb1e\ufe00-\ufe0f\ufe20-\ufe2f\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f"; -const nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]"); -const nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]"); -nonASCIIidentifierStartChars = nonASCIIidentifierChars = null; -const astralIdentifierStartCodes = [0, 11, 2, 25, 2, 18, 2, 1, 2, 14, 3, 13, 35, 122, 70, 52, 268, 28, 4, 48, 48, 31, 14, 29, 6, 37, 11, 29, 3, 35, 5, 7, 2, 4, 43, 157, 19, 35, 5, 35, 5, 39, 9, 51, 157, 310, 10, 21, 11, 7, 153, 5, 3, 0, 2, 43, 2, 1, 4, 0, 3, 22, 11, 22, 10, 30, 66, 18, 2, 1, 11, 21, 11, 25, 71, 55, 7, 1, 65, 0, 16, 3, 2, 2, 2, 28, 43, 28, 4, 28, 36, 7, 2, 27, 28, 53, 11, 21, 11, 18, 14, 17, 111, 72, 56, 50, 14, 50, 14, 35, 349, 41, 7, 1, 79, 28, 11, 0, 9, 21, 107, 20, 28, 22, 13, 52, 76, 44, 33, 24, 27, 35, 30, 0, 3, 0, 9, 34, 4, 0, 13, 47, 15, 3, 22, 0, 2, 0, 36, 17, 2, 24, 85, 6, 2, 0, 2, 3, 2, 14, 2, 9, 8, 46, 39, 7, 3, 1, 3, 21, 2, 6, 2, 1, 2, 4, 4, 0, 19, 0, 13, 4, 159, 52, 19, 3, 21, 2, 31, 47, 21, 1, 2, 0, 185, 46, 42, 3, 37, 47, 21, 0, 60, 42, 14, 0, 72, 26, 230, 43, 117, 63, 32, 7, 3, 0, 3, 7, 2, 1, 2, 23, 16, 0, 2, 0, 95, 7, 3, 38, 17, 0, 2, 0, 29, 0, 11, 39, 8, 0, 22, 0, 12, 45, 20, 0, 35, 56, 264, 8, 2, 36, 18, 0, 50, 29, 113, 6, 2, 1, 2, 37, 22, 0, 26, 5, 2, 1, 2, 31, 15, 0, 328, 18, 190, 0, 80, 921, 103, 110, 18, 195, 2749, 1070, 4050, 582, 8634, 568, 8, 30, 114, 29, 19, 47, 17, 3, 32, 20, 6, 18, 689, 63, 129, 74, 6, 0, 67, 12, 65, 1, 2, 0, 29, 6135, 9, 1237, 43, 8, 8952, 286, 50, 2, 18, 3, 9, 395, 2309, 106, 6, 12, 4, 8, 8, 9, 5991, 84, 2, 70, 2, 1, 3, 0, 3, 1, 3, 3, 2, 11, 2, 0, 2, 6, 2, 64, 2, 3, 3, 7, 2, 6, 2, 27, 2, 3, 2, 4, 2, 0, 4, 6, 2, 339, 3, 24, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 7, 2357, 44, 11, 6, 17, 0, 370, 43, 1301, 196, 60, 67, 8, 0, 1205, 3, 2, 26, 2, 1, 2, 0, 3, 0, 2, 9, 2, 3, 2, 0, 2, 0, 7, 0, 5, 0, 2, 0, 2, 0, 2, 2, 2, 1, 2, 0, 3, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 1, 2, 0, 3, 3, 2, 6, 2, 3, 2, 3, 2, 0, 2, 9, 2, 16, 6, 2, 2, 4, 2, 16, 4421, 42717, 35, 4148, 12, 221, 3, 5761, 15, 7472, 3104, 541, 1507, 4938]; -const astralIdentifierCodes = [509, 0, 227, 0, 150, 4, 294, 9, 1368, 2, 2, 1, 6, 3, 41, 2, 5, 0, 166, 1, 574, 3, 9, 9, 370, 1, 154, 10, 176, 2, 54, 14, 32, 9, 16, 3, 46, 10, 54, 9, 7, 2, 37, 13, 2, 9, 6, 1, 45, 0, 13, 2, 49, 13, 9, 3, 2, 11, 83, 11, 7, 0, 161, 11, 6, 9, 7, 3, 56, 1, 2, 6, 3, 1, 3, 2, 10, 0, 11, 1, 3, 6, 4, 4, 193, 17, 10, 9, 5, 0, 82, 19, 13, 9, 214, 6, 3, 8, 28, 1, 83, 16, 16, 9, 82, 12, 9, 9, 84, 14, 5, 9, 243, 14, 166, 9, 71, 5, 2, 1, 3, 3, 2, 0, 2, 1, 13, 9, 120, 6, 3, 6, 4, 0, 29, 9, 41, 6, 2, 3, 9, 0, 10, 10, 47, 15, 406, 7, 2, 7, 17, 9, 57, 21, 2, 13, 123, 5, 4, 0, 2, 1, 2, 6, 2, 0, 9, 9, 49, 4, 2, 1, 2, 4, 9, 9, 330, 3, 19306, 9, 135, 4, 60, 6, 26, 9, 1014, 0, 2, 54, 8, 3, 82, 0, 12, 1, 19628, 1, 5319, 4, 4, 5, 9, 7, 3, 6, 31, 3, 149, 2, 1418, 49, 513, 54, 5, 49, 9, 0, 15, 0, 23, 4, 2, 14, 1361, 6, 2, 16, 3, 6, 2, 1, 2, 4, 262, 6, 10, 9, 419, 13, 1495, 6, 110, 6, 6, 9, 4759, 9, 787719, 239]; + // If the path has no parent directory, just test it + if (!slices.length) { + return cache[path] = this._testOne(path, checkUnignored) + } -function isInAstralSet(code, set) { - let pos = 0x10000; + const parent = this._t( + slices.join(SLASH) + SLASH, + cache, + checkUnignored, + slices + ) - for (let i = 0, length = set.length; i < length; i += 2) { - pos += set[i]; - if (pos > code) return false; - pos += set[i + 1]; - if (pos >= code) return true; + // If the path contains a parent directory, check the parent first + return cache[path] = parent.ignored + // > It is not possible to re-include a file if a parent directory of + // > that file is excluded. + ? parent + : this._testOne(path, checkUnignored) } - return false; -} + ignores (path) { + return this._test(path, this._ignoreCache, false).ignored + } -function isIdentifierStart(code) { - if (code < 65) return code === 36; - if (code <= 90) return true; - if (code < 97) return code === 95; - if (code <= 122) return true; + createFilter () { + return path => !this.ignores(path) + } - if (code <= 0xffff) { - return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code)); + filter (paths) { + return makeArray(paths).filter(this.createFilter()) } - return isInAstralSet(code, astralIdentifierStartCodes); + // @returns {TestResult} + test (path) { + return this._test(path, this._testCache, true) + } } -function isIdentifierChar(code) { - if (code < 48) return code === 36; - if (code < 58) return true; - if (code < 65) return false; - if (code <= 90) return true; - if (code < 97) return code === 95; - if (code <= 122) return true; - - if (code <= 0xffff) { - return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code)); - } +const factory = options => new Ignore(options) - return isInAstralSet(code, astralIdentifierStartCodes) || isInAstralSet(code, astralIdentifierCodes); -} +const returnFalse = () => false -function isIdentifierName(name) { - let isFirst = true; +const isPathValid = path => + checkPath(path && checkPath.convert(path), path, returnFalse) - for (let i = 0; i < name.length; i++) { - let cp = name.charCodeAt(i); +factory.isPathValid = isPathValid - if ((cp & 0xfc00) === 0xd800 && i + 1 < name.length) { - const trail = name.charCodeAt(++i); +// Fixes typescript +factory.default = factory - if ((trail & 0xfc00) === 0xdc00) { - cp = 0x10000 + ((cp & 0x3ff) << 10) + (trail & 0x3ff); - } - } +module.exports = factory - if (isFirst) { - isFirst = false; +// Windows +// -------------------------------------------------------------- +/* istanbul ignore if */ +if ( + // Detect `process` so that it can run in browsers. + typeof process !== 'undefined' + && ( + process.env && process.env.IGNORE_TEST_WIN32 + || process.platform === 'win32' + ) +) { + /* eslint no-control-regex: "off" */ + const makePosix = str => /^\\\\\?\\/.test(str) + || /["<>|\u0000-\u001F]+/u.test(str) + ? str + : str.replace(/\\/g, '/') - if (!isIdentifierStart(cp)) { - return false; - } - } else if (!isIdentifierChar(cp)) { - return false; - } - } + checkPath.convert = makePosix - return !isFirst; + // 'C:\\foo' <- 'C:\\foo' has been converted to 'C:/' + // 'd:\\foo' + const REGIX_IS_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i + checkPath.isNotRelative = path => + REGIX_IS_WINDOWS_PATH_ABSOLUTE.test(path) + || isNotRelative(path) } + /***/ }), -/* 355 */ +/* 336 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; +module.exports = path => { + const isExtendedLengthPath = /^\\\\\?\\/.test(path); + const hasNonAscii = /[^\u0000-\u0080]+/.test(path); // eslint-disable-line no-control-regex -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.isReservedWord = isReservedWord; -exports.isStrictReservedWord = isStrictReservedWord; -exports.isStrictBindOnlyReservedWord = isStrictBindOnlyReservedWord; -exports.isStrictBindReservedWord = isStrictBindReservedWord; -exports.isKeyword = isKeyword; -const reservedWords = { - keyword: ["break", "case", "catch", "continue", "debugger", "default", "do", "else", "finally", "for", "function", "if", "return", "switch", "throw", "try", "var", "const", "while", "with", "new", "this", "super", "class", "extends", "export", "import", "null", "true", "false", "in", "instanceof", "typeof", "void", "delete"], - strict: ["implements", "interface", "let", "package", "private", "protected", "public", "static", "yield"], - strictBind: ["eval", "arguments"] + if (isExtendedLengthPath || hasNonAscii) { + return path; + } + + return path.replace(/\\/g, '/'); }; -const keywords = new Set(reservedWords.keyword); -const reservedWordsStrictSet = new Set(reservedWords.strict); -const reservedWordsStrictBindSet = new Set(reservedWords.strictBind); -function isReservedWord(word, inModule) { - return inModule && word === "await" || word === "enum"; -} -function isStrictReservedWord(word, inModule) { - return isReservedWord(word, inModule) || reservedWordsStrictSet.has(word); -} +/***/ }), +/* 337 */ +/***/ (function(module, exports, __webpack_require__) { -function isStrictBindOnlyReservedWord(word) { - return reservedWordsStrictBindSet.has(word); +"use strict"; + +const {Transform} = __webpack_require__(173); + +class ObjectTransform extends Transform { + constructor() { + super({ + objectMode: true + }); + } } -function isStrictBindReservedWord(word, inModule) { - return isStrictReservedWord(word, inModule) || isStrictBindOnlyReservedWord(word); +class FilterStream extends ObjectTransform { + constructor(filter) { + super(); + this._filter = filter; + } + + _transform(data, encoding, callback) { + if (this._filter(data)) { + this.push(data); + } + + callback(); + } } -function isKeyword(word) { - return keywords.has(word); +class UniqueStream extends ObjectTransform { + constructor() { + super(); + this._pushed = new Set(); + } + + _transform(data, encoding, callback) { + if (!this._pushed.has(data)) { + this.push(data); + this._pushed.add(data); + } + + callback(); + } } +module.exports = { + FilterStream, + UniqueStream +}; + + /***/ }), -/* 356 */ +/* 338 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const escapeStringRegexp = __webpack_require__(357); -const ansiStyles = __webpack_require__(358); -const stdoutColor = __webpack_require__(363).stdout; +const path = __webpack_require__(4); -const template = __webpack_require__(365); +module.exports = path_ => { + let cwd = process.cwd(); -const isSimpleWindowsTerm = process.platform === 'win32' && !(process.env.TERM || '').toLowerCase().startsWith('xterm'); + path_ = path.resolve(path_); -// `supportsColor.level` → `ansiStyles.color[name]` mapping -const levelMapping = ['ansi', 'ansi', 'ansi256', 'ansi16m']; + if (process.platform === 'win32') { + cwd = cwd.toLowerCase(); + path_ = path_.toLowerCase(); + } -// `color-convert` models to exclude from the Chalk API due to conflicts and such -const skipModels = new Set(['gray']); + return path_ === cwd; +}; -const styles = Object.create(null); -function applyOptions(obj, options) { - options = options || {}; +/***/ }), +/* 339 */ +/***/ (function(module, exports, __webpack_require__) { - // Detect level if not set manually - const scLevel = stdoutColor ? stdoutColor.level : 0; - obj.level = options.level === undefined ? scLevel : options.level; - obj.enabled = 'enabled' in options ? options.enabled : obj.level > 0; -} +"use strict"; -function Chalk(options) { - // We check for this.template here since calling `chalk.constructor()` - // by itself will have a `this` of a previously constructed chalk object - if (!this || !(this instanceof Chalk) || this.template) { - const chalk = {}; - applyOptions(chalk, options); - - chalk.template = function () { - const args = [].slice.call(arguments); - return chalkTag.apply(null, [chalk.template].concat(args)); - }; +const path = __webpack_require__(4); - Object.setPrototypeOf(chalk, Chalk.prototype); - Object.setPrototypeOf(chalk.template, chalk); +module.exports = (childPath, parentPath) => { + childPath = path.resolve(childPath); + parentPath = path.resolve(parentPath); - chalk.template.constructor = Chalk; + if (process.platform === 'win32') { + childPath = childPath.toLowerCase(); + parentPath = parentPath.toLowerCase(); + } - return chalk.template; + if (childPath === parentPath) { + return false; } - applyOptions(this, options); + childPath += path.sep; + parentPath += path.sep; + + return childPath.startsWith(parentPath); +}; + + +/***/ }), +/* 340 */ +/***/ (function(module, exports, __webpack_require__) { + +const assert = __webpack_require__(162) +const path = __webpack_require__(4) +const fs = __webpack_require__(132) +let glob = undefined +try { + glob = __webpack_require__(244) +} catch (_err) { + // treat glob as optional. } -// Use bright blue on Windows as the normal blue color is illegible -if (isSimpleWindowsTerm) { - ansiStyles.blue.open = '\u001B[94m'; +const defaultGlobOpts = { + nosort: true, + silent: true } -for (const key of Object.keys(ansiStyles)) { - ansiStyles[key].closeRe = new RegExp(escapeStringRegexp(ansiStyles[key].close), 'g'); +// for EMFILE handling +let timeout = 0 - styles[key] = { - get() { - const codes = ansiStyles[key]; - return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, key); - } - }; +const isWindows = (process.platform === "win32") + +const defaults = options => { + const methods = [ + 'unlink', + 'chmod', + 'stat', + 'lstat', + 'rmdir', + 'readdir' + ] + methods.forEach(m => { + options[m] = options[m] || fs[m] + m = m + 'Sync' + options[m] = options[m] || fs[m] + }) + + options.maxBusyTries = options.maxBusyTries || 3 + options.emfileWait = options.emfileWait || 1000 + if (options.glob === false) { + options.disableGlob = true + } + if (options.disableGlob !== true && glob === undefined) { + throw Error('glob dependency not found, set `options.disableGlob = true` if intentional') + } + options.disableGlob = options.disableGlob || false + options.glob = options.glob || defaultGlobOpts } -styles.visible = { - get() { - return build.call(this, this._styles || [], true, 'visible'); - } -}; +const rimraf = (p, options, cb) => { + if (typeof options === 'function') { + cb = options + options = {} + } -ansiStyles.color.closeRe = new RegExp(escapeStringRegexp(ansiStyles.color.close), 'g'); -for (const model of Object.keys(ansiStyles.color.ansi)) { - if (skipModels.has(model)) { - continue; - } + assert(p, 'rimraf: missing path') + assert.equal(typeof p, 'string', 'rimraf: path should be a string') + assert.equal(typeof cb, 'function', 'rimraf: callback function required') + assert(options, 'rimraf: invalid options argument provided') + assert.equal(typeof options, 'object', 'rimraf: options should be object') - styles[model] = { - get() { - const level = this.level; - return function () { - const open = ansiStyles.color[levelMapping[level]][model].apply(null, arguments); - const codes = { - open, - close: ansiStyles.color.close, - closeRe: ansiStyles.color.closeRe - }; - return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, model); - }; - } - }; -} + defaults(options) -ansiStyles.bgColor.closeRe = new RegExp(escapeStringRegexp(ansiStyles.bgColor.close), 'g'); -for (const model of Object.keys(ansiStyles.bgColor.ansi)) { - if (skipModels.has(model)) { - continue; - } + let busyTries = 0 + let errState = null + let n = 0 - const bgModel = 'bg' + model[0].toUpperCase() + model.slice(1); - styles[bgModel] = { - get() { - const level = this.level; - return function () { - const open = ansiStyles.bgColor[levelMapping[level]][model].apply(null, arguments); - const codes = { - open, - close: ansiStyles.bgColor.close, - closeRe: ansiStyles.bgColor.closeRe - }; - return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, model); - }; - } - }; -} + const next = (er) => { + errState = errState || er + if (--n === 0) + cb(errState) + } -const proto = Object.defineProperties(() => {}, styles); + const afterGlob = (er, results) => { + if (er) + return cb(er) -function build(_styles, _empty, key) { - const builder = function () { - return applyStyle.apply(builder, arguments); - }; + n = results.length + if (n === 0) + return cb() - builder._styles = _styles; - builder._empty = _empty; + results.forEach(p => { + const CB = (er) => { + if (er) { + if ((er.code === "EBUSY" || er.code === "ENOTEMPTY" || er.code === "EPERM") && + busyTries < options.maxBusyTries) { + busyTries ++ + // try again, with the same exact callback as this one. + return setTimeout(() => rimraf_(p, options, CB), busyTries * 100) + } - const self = this; + // this one won't happen if graceful-fs is used. + if (er.code === "EMFILE" && timeout < options.emfileWait) { + return setTimeout(() => rimraf_(p, options, CB), timeout ++) + } - Object.defineProperty(builder, 'level', { - enumerable: true, - get() { - return self.level; - }, - set(level) { - self.level = level; - } - }); + // already gone + if (er.code === "ENOENT") er = null + } - Object.defineProperty(builder, 'enabled', { - enumerable: true, - get() { - return self.enabled; - }, - set(enabled) { - self.enabled = enabled; - } - }); + timeout = 0 + next(er) + } + rimraf_(p, options, CB) + }) + } - // See below for fix regarding invisible grey/dim combination on Windows - builder.hasGrey = this.hasGrey || key === 'gray' || key === 'grey'; + if (options.disableGlob || !glob.hasMagic(p)) + return afterGlob(null, [p]) - // `__proto__` is used because we must return a function, but there is - // no way to create a function with a different prototype - builder.__proto__ = proto; // eslint-disable-line no-proto + options.lstat(p, (er, stat) => { + if (!er) + return afterGlob(null, [p]) + + glob(p, options.glob, afterGlob) + }) - return builder; } -function applyStyle() { - // Support varags, but simply cast to string in case there's only one arg - const args = arguments; - const argsLen = args.length; - let str = String(arguments[0]); +// Two possible strategies. +// 1. Assume it's a file. unlink it, then do the dir stuff on EPERM or EISDIR +// 2. Assume it's a directory. readdir, then do the file stuff on ENOTDIR +// +// Both result in an extra syscall when you guess wrong. However, there +// are likely far more normal files in the world than directories. This +// is based on the assumption that a the average number of files per +// directory is >= 1. +// +// If anyone ever complains about this, then I guess the strategy could +// be made configurable somehow. But until then, YAGNI. +const rimraf_ = (p, options, cb) => { + assert(p) + assert(options) + assert(typeof cb === 'function') - if (argsLen === 0) { - return ''; - } + // sunos lets the root user unlink directories, which is... weird. + // so we have to lstat here and make sure it's not a dir. + options.lstat(p, (er, st) => { + if (er && er.code === "ENOENT") + return cb(null) - if (argsLen > 1) { - // Don't slice `arguments`, it prevents V8 optimizations - for (let a = 1; a < argsLen; a++) { - str += ' ' + args[a]; - } - } + // Windows can EPERM on stat. Life is suffering. + if (er && er.code === "EPERM" && isWindows) + fixWinEPERM(p, options, er, cb) - if (!this.enabled || this.level <= 0 || !str) { - return this._empty ? '' : str; - } + if (st && st.isDirectory()) + return rmdir(p, options, er, cb) - // Turns out that on Windows dimmed gray text becomes invisible in cmd.exe, - // see https://github.com/chalk/chalk/issues/58 - // If we're on Windows and we're dealing with a gray color, temporarily make 'dim' a noop. - const originalDim = ansiStyles.dim.open; - if (isSimpleWindowsTerm && this.hasGrey) { - ansiStyles.dim.open = ''; - } + options.unlink(p, er => { + if (er) { + if (er.code === "ENOENT") + return cb(null) + if (er.code === "EPERM") + return (isWindows) + ? fixWinEPERM(p, options, er, cb) + : rmdir(p, options, er, cb) + if (er.code === "EISDIR") + return rmdir(p, options, er, cb) + } + return cb(er) + }) + }) +} - for (const code of this._styles.slice().reverse()) { - // Replace any instances already present with a re-opening code - // otherwise only the part of the string until said closing code - // will be colored, and the rest will simply be 'plain'. - str = code.open + str.replace(code.closeRe, code.open) + code.close; +const fixWinEPERM = (p, options, er, cb) => { + assert(p) + assert(options) + assert(typeof cb === 'function') - // Close the styling before a linebreak and reopen - // after next line to fix a bleed issue on macOS - // https://github.com/chalk/chalk/pull/92 - str = str.replace(/\r?\n/g, `${code.close}$&${code.open}`); - } + options.chmod(p, 0o666, er2 => { + if (er2) + cb(er2.code === "ENOENT" ? null : er) + else + options.stat(p, (er3, stats) => { + if (er3) + cb(er3.code === "ENOENT" ? null : er) + else if (stats.isDirectory()) + rmdir(p, options, er, cb) + else + options.unlink(p, cb) + }) + }) +} - // Reset the original `dim` if we changed it to work around the Windows dimmed gray issue - ansiStyles.dim.open = originalDim; +const fixWinEPERMSync = (p, options, er) => { + assert(p) + assert(options) - return str; + try { + options.chmodSync(p, 0o666) + } catch (er2) { + if (er2.code === "ENOENT") + return + else + throw er + } + + let stats + try { + stats = options.statSync(p) + } catch (er3) { + if (er3.code === "ENOENT") + return + else + throw er + } + + if (stats.isDirectory()) + rmdirSync(p, options, er) + else + options.unlinkSync(p) } -function chalkTag(chalk, strings) { - if (!Array.isArray(strings)) { - // If chalk() was called by itself or with a string, - // return the string itself as a string. - return [].slice.call(arguments, 1).join(' '); - } +const rmdir = (p, options, originalEr, cb) => { + assert(p) + assert(options) + assert(typeof cb === 'function') - const args = [].slice.call(arguments, 2); - const parts = [strings.raw[0]]; + // try to rmdir first, and only readdir on ENOTEMPTY or EEXIST (SunOS) + // if we guessed wrong, and it's not a directory, then + // raise the original error. + options.rmdir(p, er => { + if (er && (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM")) + rmkids(p, options, cb) + else if (er && er.code === "ENOTDIR") + cb(originalEr) + else + cb(er) + }) +} - for (let i = 1; i < strings.length; i++) { - parts.push(String(args[i - 1]).replace(/[{}\\]/g, '\\$&')); - parts.push(String(strings.raw[i])); - } +const rmkids = (p, options, cb) => { + assert(p) + assert(options) + assert(typeof cb === 'function') - return template(chalk, parts.join('')); + options.readdir(p, (er, files) => { + if (er) + return cb(er) + let n = files.length + if (n === 0) + return options.rmdir(p, cb) + let errState + files.forEach(f => { + rimraf(path.join(p, f), options, er => { + if (errState) + return + if (er) + return cb(errState = er) + if (--n === 0) + options.rmdir(p, cb) + }) + }) + }) } -Object.defineProperties(Chalk.prototype, styles); +// this looks simpler, and is strictly *faster*, but will +// tie up the JavaScript thread and fail on excessively +// deep directory trees. +const rimrafSync = (p, options) => { + options = options || {} + defaults(options) -module.exports = Chalk(); // eslint-disable-line new-cap -module.exports.supportsColor = stdoutColor; -module.exports.default = module.exports; // For TypeScript + assert(p, 'rimraf: missing path') + assert.equal(typeof p, 'string', 'rimraf: path should be a string') + assert(options, 'rimraf: missing options') + assert.equal(typeof options, 'object', 'rimraf: options should be object') + let results -/***/ }), -/* 357 */ -/***/ (function(module, exports, __webpack_require__) { + if (options.disableGlob || !glob.hasMagic(p)) { + results = [p] + } else { + try { + options.lstatSync(p) + results = [p] + } catch (er) { + results = glob.sync(p, options.glob) + } + } -"use strict"; + if (!results.length) + return + for (let i = 0; i < results.length; i++) { + const p = results[i] -var matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g; + let st + try { + st = options.lstatSync(p) + } catch (er) { + if (er.code === "ENOENT") + return -module.exports = function (str) { - if (typeof str !== 'string') { - throw new TypeError('Expected a string'); - } + // Windows can EPERM on stat. Life is suffering. + if (er.code === "EPERM" && isWindows) + fixWinEPERMSync(p, options, er) + } - return str.replace(matchOperatorsRe, '\\$&'); -}; + try { + // sunos lets the root user unlink directories, which is... weird. + if (st && st.isDirectory()) + rmdirSync(p, options, null) + else + options.unlinkSync(p) + } catch (er) { + if (er.code === "ENOENT") + return + if (er.code === "EPERM") + return isWindows ? fixWinEPERMSync(p, options, er) : rmdirSync(p, options, er) + if (er.code !== "EISDIR") + throw er + + rmdirSync(p, options, er) + } + } +} + +const rmdirSync = (p, options, originalEr) => { + assert(p) + assert(options) + + try { + options.rmdirSync(p) + } catch (er) { + if (er.code === "ENOENT") + return + if (er.code === "ENOTDIR") + throw originalEr + if (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM") + rmkidsSync(p, options) + } +} + +const rmkidsSync = (p, options) => { + assert(p) + assert(options) + options.readdirSync(p).forEach(f => rimrafSync(path.join(p, f), options)) + + // We only end up here once we got ENOTEMPTY at least once, and + // at this point, we are guaranteed to have removed all the kids. + // So, we know that it won't be ENOENT or ENOTDIR or anything else. + // try really hard to delete stuff on windows, because it has a + // PROFOUNDLY annoying habit of not closing handles promptly when + // files are deleted, resulting in spurious ENOTEMPTY errors. + const retries = isWindows ? 100 : 1 + let i = 0 + do { + let threw = true + try { + const ret = options.rmdirSync(p, options) + threw = false + return ret + } finally { + if (++i < retries && threw) + continue + } + } while (true) +} + +module.exports = rimraf +rimraf.sync = rimrafSync /***/ }), -/* 358 */ +/* 341 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -/* WEBPACK VAR INJECTION */(function(module) { -const colorConvert = __webpack_require__(359); -const wrapAnsi16 = (fn, offset) => function () { - const code = fn.apply(colorConvert, arguments); - return `\u001B[${code + offset}m`; -}; +const AggregateError = __webpack_require__(342); -const wrapAnsi256 = (fn, offset) => function () { - const code = fn.apply(colorConvert, arguments); - return `\u001B[${38 + offset};5;${code}m`; -}; +module.exports = async ( + iterable, + mapper, + { + concurrency = Infinity, + stopOnError = true + } = {} +) => { + return new Promise((resolve, reject) => { + if (typeof mapper !== 'function') { + throw new TypeError('Mapper function is required'); + } -const wrapAnsi16m = (fn, offset) => function () { - const rgb = fn.apply(colorConvert, arguments); - return `\u001B[${38 + offset};2;${rgb[0]};${rgb[1]};${rgb[2]}m`; -}; + if (!(typeof concurrency === 'number' && concurrency >= 1)) { + throw new TypeError(`Expected \`concurrency\` to be a number from 1 and up, got \`${concurrency}\` (${typeof concurrency})`); + } -function assembleStyles() { - const codes = new Map(); - const styles = { - modifier: { - reset: [0, 0], - // 21 isn't widely supported and 22 does the same thing - bold: [1, 22], - dim: [2, 22], - italic: [3, 23], - underline: [4, 24], - inverse: [7, 27], - hidden: [8, 28], - strikethrough: [9, 29] - }, - color: { - black: [30, 39], - red: [31, 39], - green: [32, 39], - yellow: [33, 39], - blue: [34, 39], - magenta: [35, 39], - cyan: [36, 39], - white: [37, 39], - gray: [90, 39], + const ret = []; + const errors = []; + const iterator = iterable[Symbol.iterator](); + let isRejected = false; + let isIterableDone = false; + let resolvingCount = 0; + let currentIndex = 0; - // Bright color - redBright: [91, 39], - greenBright: [92, 39], - yellowBright: [93, 39], - blueBright: [94, 39], - magentaBright: [95, 39], - cyanBright: [96, 39], - whiteBright: [97, 39] - }, - bgColor: { - bgBlack: [40, 49], - bgRed: [41, 49], - bgGreen: [42, 49], - bgYellow: [43, 49], - bgBlue: [44, 49], - bgMagenta: [45, 49], - bgCyan: [46, 49], - bgWhite: [47, 49], + const next = () => { + if (isRejected) { + return; + } - // Bright color - bgBlackBright: [100, 49], - bgRedBright: [101, 49], - bgGreenBright: [102, 49], - bgYellowBright: [103, 49], - bgBlueBright: [104, 49], - bgMagentaBright: [105, 49], - bgCyanBright: [106, 49], - bgWhiteBright: [107, 49] - } - }; + const nextItem = iterator.next(); + const i = currentIndex; + currentIndex++; - // Fix humans - styles.color.grey = styles.color.gray; + if (nextItem.done) { + isIterableDone = true; - for (const groupName of Object.keys(styles)) { - const group = styles[groupName]; + if (resolvingCount === 0) { + if (!stopOnError && errors.length !== 0) { + reject(new AggregateError(errors)); + } else { + resolve(ret); + } + } - for (const styleName of Object.keys(group)) { - const style = group[styleName]; + return; + } - styles[styleName] = { - open: `\u001B[${style[0]}m`, - close: `\u001B[${style[1]}m` - }; + resolvingCount++; - group[styleName] = styles[styleName]; + (async () => { + try { + const element = await nextItem.value; + ret[i] = await mapper(element, i); + resolvingCount--; + next(); + } catch (error) { + if (stopOnError) { + isRejected = true; + reject(error); + } else { + errors.push(error); + resolvingCount--; + next(); + } + } + })(); + }; - codes.set(style[0], style[1]); - } + for (let i = 0; i < concurrency; i++) { + next(); - Object.defineProperty(styles, groupName, { - value: group, - enumerable: false - }); + if (isIterableDone) { + break; + } + } + }); +}; - Object.defineProperty(styles, 'codes', { - value: codes, - enumerable: false - }); - } - const ansi2ansi = n => n; - const rgb2rgb = (r, g, b) => [r, g, b]; +/***/ }), +/* 342 */ +/***/ (function(module, exports, __webpack_require__) { - styles.color.close = '\u001B[39m'; - styles.bgColor.close = '\u001B[49m'; +"use strict"; - styles.color.ansi = { - ansi: wrapAnsi16(ansi2ansi, 0) - }; - styles.color.ansi256 = { - ansi256: wrapAnsi256(ansi2ansi, 0) - }; - styles.color.ansi16m = { - rgb: wrapAnsi16m(rgb2rgb, 0) - }; +const indentString = __webpack_require__(343); +const cleanStack = __webpack_require__(344); - styles.bgColor.ansi = { - ansi: wrapAnsi16(ansi2ansi, 10) - }; - styles.bgColor.ansi256 = { - ansi256: wrapAnsi256(ansi2ansi, 10) - }; - styles.bgColor.ansi16m = { - rgb: wrapAnsi16m(rgb2rgb, 10) - }; +const cleanInternalStack = stack => stack.replace(/\s+at .*aggregate-error\/index.js:\d+:\d+\)?/g, ''); - for (let key of Object.keys(colorConvert)) { - if (typeof colorConvert[key] !== 'object') { - continue; +class AggregateError extends Error { + constructor(errors) { + if (!Array.isArray(errors)) { + throw new TypeError(`Expected input to be an Array, got ${typeof errors}`); } - const suite = colorConvert[key]; + errors = [...errors].map(error => { + if (error instanceof Error) { + return error; + } - if (key === 'ansi16') { - key = 'ansi'; - } + if (error !== null && typeof error === 'object') { + // Handle plain error objects with message property and/or possibly other metadata + return Object.assign(new Error(error.message), error); + } - if ('ansi16' in suite) { - styles.color.ansi[key] = wrapAnsi16(suite.ansi16, 0); - styles.bgColor.ansi[key] = wrapAnsi16(suite.ansi16, 10); - } + return new Error(error); + }); - if ('ansi256' in suite) { - styles.color.ansi256[key] = wrapAnsi256(suite.ansi256, 0); - styles.bgColor.ansi256[key] = wrapAnsi256(suite.ansi256, 10); - } + let message = errors + .map(error => { + // The `stack` property is not standardized, so we can't assume it exists + return typeof error.stack === 'string' ? cleanInternalStack(cleanStack(error.stack)) : String(error); + }) + .join('\n'); + message = '\n' + indentString(message, 4); + super(message); - if ('rgb' in suite) { - styles.color.ansi16m[key] = wrapAnsi16m(suite.rgb, 0); - styles.bgColor.ansi16m[key] = wrapAnsi16m(suite.rgb, 10); - } + this.name = 'AggregateError'; + + Object.defineProperty(this, '_errors', {value: errors}); } - return styles; + * [Symbol.iterator]() { + for (const error of this._errors) { + yield error; + } + } } -// Make the export immutable -Object.defineProperty(module, 'exports', { - enumerable: true, - get: assembleStyles -}); +module.exports = AggregateError; -/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(116)(module))) /***/ }), -/* 359 */ +/* 343 */ /***/ (function(module, exports, __webpack_require__) { -var conversions = __webpack_require__(360); -var route = __webpack_require__(362); - -var convert = {}; - -var models = Object.keys(conversions); - -function wrapRaw(fn) { - var wrappedFn = function (args) { - if (args === undefined || args === null) { - return args; - } +"use strict"; - if (arguments.length > 1) { - args = Array.prototype.slice.call(arguments); - } - return fn(args); +module.exports = (string, count = 1, options) => { + options = { + indent: ' ', + includeEmptyLines: false, + ...options }; - // preserve .conversion property if there is one - if ('conversion' in fn) { - wrappedFn.conversion = fn.conversion; + if (typeof string !== 'string') { + throw new TypeError( + `Expected \`input\` to be a \`string\`, got \`${typeof string}\`` + ); } - return wrappedFn; -} - -function wrapRounded(fn) { - var wrappedFn = function (args) { - if (args === undefined || args === null) { - return args; - } - - if (arguments.length > 1) { - args = Array.prototype.slice.call(arguments); - } - - var result = fn(args); - - // we're assuming the result is an array here. - // see notice in conversions.js; don't use box types - // in conversion functions. - if (typeof result === 'object') { - for (var len = result.length, i = 0; i < len; i++) { - result[i] = Math.round(result[i]); - } - } + if (typeof count !== 'number') { + throw new TypeError( + `Expected \`count\` to be a \`number\`, got \`${typeof count}\`` + ); + } - return result; - }; + if (typeof options.indent !== 'string') { + throw new TypeError( + `Expected \`options.indent\` to be a \`string\`, got \`${typeof options.indent}\`` + ); + } - // preserve .conversion property if there is one - if ('conversion' in fn) { - wrappedFn.conversion = fn.conversion; + if (count === 0) { + return string; } - return wrappedFn; -} + const regex = options.includeEmptyLines ? /^/gm : /^(?!\s*$)/gm; -models.forEach(function (fromModel) { - convert[fromModel] = {}; + return string.replace(regex, options.indent.repeat(count)); +}; - Object.defineProperty(convert[fromModel], 'channels', {value: conversions[fromModel].channels}); - Object.defineProperty(convert[fromModel], 'labels', {value: conversions[fromModel].labels}); - var routes = route(fromModel); - var routeModels = Object.keys(routes); +/***/ }), +/* 344 */ +/***/ (function(module, exports, __webpack_require__) { - routeModels.forEach(function (toModel) { - var fn = routes[toModel]; +"use strict"; - convert[fromModel][toModel] = wrapRounded(fn); - convert[fromModel][toModel].raw = wrapRaw(fn); - }); -}); +const os = __webpack_require__(122); -module.exports = convert; +const extractPathRegex = /\s+at.*(?:\(|\s)(.*)\)?/; +const pathRegex = /^(?:(?:(?:node|(?:internal\/[\w/]*|.*node_modules\/(?:babel-polyfill|pirates)\/.*)?\w+)\.js:\d+:\d+)|native)/; +const homeDir = typeof os.homedir === 'undefined' ? '' : os.homedir(); +module.exports = (stack, options) => { + options = Object.assign({pretty: false}, options); -/***/ }), -/* 360 */ -/***/ (function(module, exports, __webpack_require__) { + return stack.replace(/\\/g, '/') + .split('\n') + .filter(line => { + const pathMatches = line.match(extractPathRegex); + if (pathMatches === null || !pathMatches[1]) { + return true; + } -/* MIT license */ -var cssKeywords = __webpack_require__(361); + const match = pathMatches[1]; -// NOTE: conversions should only return primitive values (i.e. arrays, or -// values that give correct `typeof` results). -// do not use box values types (i.e. Number(), String(), etc.) + // Electron + if ( + match.includes('.app/Contents/Resources/electron.asar') || + match.includes('.app/Contents/Resources/default_app.asar') + ) { + return false; + } -var reverseKeywords = {}; -for (var key in cssKeywords) { - if (cssKeywords.hasOwnProperty(key)) { - reverseKeywords[cssKeywords[key]] = key; - } -} + return !pathRegex.test(match); + }) + .filter(line => line.trim() !== '') + .map(line => { + if (options.pretty) { + return line.replace(extractPathRegex, (m, p1) => m.replace(p1, p1.replace(homeDir, '~'))); + } -var convert = module.exports = { - rgb: {channels: 3, labels: 'rgb'}, - hsl: {channels: 3, labels: 'hsl'}, - hsv: {channels: 3, labels: 'hsv'}, - hwb: {channels: 3, labels: 'hwb'}, - cmyk: {channels: 4, labels: 'cmyk'}, - xyz: {channels: 3, labels: 'xyz'}, - lab: {channels: 3, labels: 'lab'}, - lch: {channels: 3, labels: 'lch'}, - hex: {channels: 1, labels: ['hex']}, - keyword: {channels: 1, labels: ['keyword']}, - ansi16: {channels: 1, labels: ['ansi16']}, - ansi256: {channels: 1, labels: ['ansi256']}, - hcg: {channels: 3, labels: ['h', 'c', 'g']}, - apple: {channels: 3, labels: ['r16', 'g16', 'b16']}, - gray: {channels: 1, labels: ['gray']} + return line; + }) + .join('\n'); }; -// hide .channels and .labels properties -for (var model in convert) { - if (convert.hasOwnProperty(model)) { - if (!('channels' in convert[model])) { - throw new Error('missing channels property: ' + model); - } - - if (!('labels' in convert[model])) { - throw new Error('missing channel labels property: ' + model); - } - if (convert[model].labels.length !== convert[model].channels) { - throw new Error('channel and label counts mismatch: ' + model); - } +/***/ }), +/* 345 */ +/***/ (function(module, exports, __webpack_require__) { - var channels = convert[model].channels; - var labels = convert[model].labels; - delete convert[model].channels; - delete convert[model].labels; - Object.defineProperty(convert[model], 'channels', {value: channels}); - Object.defineProperty(convert[model], 'labels', {value: labels}); - } -} +var fs = __webpack_require__(132), + path = __webpack_require__(4); -convert.rgb.hsl = function (rgb) { - var r = rgb[0] / 255; - var g = rgb[1] / 255; - var b = rgb[2] / 255; - var min = Math.min(r, g, b); - var max = Math.max(r, g, b); - var delta = max - min; - var h; - var s; - var l; +module.exports = ncp; +ncp.ncp = ncp; - if (max === min) { - h = 0; - } else if (r === max) { - h = (g - b) / delta; - } else if (g === max) { - h = 2 + (b - r) / delta; - } else if (b === max) { - h = 4 + (r - g) / delta; - } +function ncp (source, dest, options, callback) { + var cback = callback; - h = Math.min(h * 60, 360); + if (!callback) { + cback = options; + options = {}; + } - if (h < 0) { - h += 360; - } + var basePath = process.cwd(), + currentPath = path.resolve(basePath, source), + targetPath = path.resolve(basePath, dest), + filter = options.filter, + rename = options.rename, + transform = options.transform, + clobber = options.clobber !== false, + modified = options.modified, + dereference = options.dereference, + errs = null, + started = 0, + finished = 0, + running = 0, + limit = options.limit || ncp.limit || 16; - l = (min + max) / 2; + limit = (limit < 1) ? 1 : (limit > 512) ? 512 : limit; - if (max === min) { - s = 0; - } else if (l <= 0.5) { - s = delta / (max + min); - } else { - s = delta / (2 - max - min); - } + startCopy(currentPath); + + function startCopy(source) { + started++; + if (filter) { + if (filter instanceof RegExp) { + if (!filter.test(source)) { + return cb(true); + } + } + else if (typeof filter === 'function') { + if (!filter(source)) { + return cb(true); + } + } + } + return getStats(source); + } - return [h, s * 100, l * 100]; -}; + function getStats(source) { + var stat = dereference ? fs.stat : fs.lstat; + if (running >= limit) { + return setImmediate(function () { + getStats(source); + }); + } + running++; + stat(source, function (err, stats) { + var item = {}; + if (err) { + return onError(err); + } -convert.rgb.hsv = function (rgb) { - var rdif; - var gdif; - var bdif; - var h; - var s; + // We need to get the mode from the stats object and preserve it. + item.name = source; + item.mode = stats.mode; + item.mtime = stats.mtime; //modified time + item.atime = stats.atime; //access time - var r = rgb[0] / 255; - var g = rgb[1] / 255; - var b = rgb[2] / 255; - var v = Math.max(r, g, b); - var diff = v - Math.min(r, g, b); - var diffc = function (c) { - return (v - c) / 6 / diff + 1 / 2; - }; + if (stats.isDirectory()) { + return onDir(item); + } + else if (stats.isFile()) { + return onFile(item); + } + else if (stats.isSymbolicLink()) { + // Symlinks don't really need to know about the mode. + return onLink(source); + } + }); + } - if (diff === 0) { - h = s = 0; - } else { - s = diff / v; - rdif = diffc(r); - gdif = diffc(g); - bdif = diffc(b); + function onFile(file) { + var target = file.name.replace(currentPath, targetPath); + if(rename) { + target = rename(target); + } + isWritable(target, function (writable) { + if (writable) { + return copyFile(file, target); + } + if(clobber) { + rmFile(target, function () { + copyFile(file, target); + }); + } + if (modified) { + var stat = dereference ? fs.stat : fs.lstat; + stat(target, function(err, stats) { + //if souce modified time greater to target modified time copy file + if (file.mtime.getTime()>stats.mtime.getTime()) + copyFile(file, target); + else return cb(); + }); + } + else { + return cb(); + } + }); + } - if (r === v) { - h = bdif - gdif; - } else if (g === v) { - h = (1 / 3) + rdif - bdif; - } else if (b === v) { - h = (2 / 3) + gdif - rdif; - } - if (h < 0) { - h += 1; - } else if (h > 1) { - h -= 1; - } - } + function copyFile(file, target) { + var readStream = fs.createReadStream(file.name), + writeStream = fs.createWriteStream(target, { mode: file.mode }); + + readStream.on('error', onError); + writeStream.on('error', onError); + + if(transform) { + transform(readStream, writeStream, file); + } else { + writeStream.on('open', function() { + readStream.pipe(writeStream); + }); + } + writeStream.once('finish', function() { + if (modified) { + //target file modified date sync. + fs.utimesSync(target, file.atime, file.mtime); + cb(); + } + else cb(); + }); + } - return [ - h * 360, - s * 100, - v * 100 - ]; -}; + function rmFile(file, done) { + fs.unlink(file, function (err) { + if (err) { + return onError(err); + } + return done(); + }); + } -convert.rgb.hwb = function (rgb) { - var r = rgb[0]; - var g = rgb[1]; - var b = rgb[2]; - var h = convert.rgb.hsl(rgb)[0]; - var w = 1 / 255 * Math.min(r, Math.min(g, b)); + function onDir(dir) { + var target = dir.name.replace(currentPath, targetPath); + isWritable(target, function (writable) { + if (writable) { + return mkDir(dir, target); + } + copyDir(dir.name); + }); + } - b = 1 - 1 / 255 * Math.max(r, Math.max(g, b)); + function mkDir(dir, target) { + fs.mkdir(target, dir.mode, function (err) { + if (err) { + return onError(err); + } + copyDir(dir.name); + }); + } - return [h, w * 100, b * 100]; -}; + function copyDir(dir) { + fs.readdir(dir, function (err, items) { + if (err) { + return onError(err); + } + items.forEach(function (item) { + startCopy(path.join(dir, item)); + }); + return cb(); + }); + } -convert.rgb.cmyk = function (rgb) { - var r = rgb[0] / 255; - var g = rgb[1] / 255; - var b = rgb[2] / 255; - var c; - var m; - var y; - var k; + function onLink(link) { + var target = link.replace(currentPath, targetPath); + fs.readlink(link, function (err, resolvedPath) { + if (err) { + return onError(err); + } + checkLink(resolvedPath, target); + }); + } - k = Math.min(1 - r, 1 - g, 1 - b); - c = (1 - r - k) / (1 - k) || 0; - m = (1 - g - k) / (1 - k) || 0; - y = (1 - b - k) / (1 - k) || 0; + function checkLink(resolvedPath, target) { + if (dereference) { + resolvedPath = path.resolve(basePath, resolvedPath); + } + isWritable(target, function (writable) { + if (writable) { + return makeLink(resolvedPath, target); + } + fs.readlink(target, function (err, targetDest) { + if (err) { + return onError(err); + } + if (dereference) { + targetDest = path.resolve(basePath, targetDest); + } + if (targetDest === resolvedPath) { + return cb(); + } + return rmFile(target, function () { + makeLink(resolvedPath, target); + }); + }); + }); + } - return [c * 100, m * 100, y * 100, k * 100]; -}; + function makeLink(linkPath, target) { + fs.symlink(linkPath, target, function (err) { + if (err) { + return onError(err); + } + return cb(); + }); + } -/** - * See https://en.m.wikipedia.org/wiki/Euclidean_distance#Squared_Euclidean_distance - * */ -function comparativeDistance(x, y) { - return ( - Math.pow(x[0] - y[0], 2) + - Math.pow(x[1] - y[1], 2) + - Math.pow(x[2] - y[2], 2) - ); -} + function isWritable(path, done) { + fs.lstat(path, function (err) { + if (err) { + if (err.code === 'ENOENT') return done(true); + return done(false); + } + return done(false); + }); + } -convert.rgb.keyword = function (rgb) { - var reversed = reverseKeywords[rgb]; - if (reversed) { - return reversed; - } + function onError(err) { + if (options.stopOnError) { + return cback(err); + } + else if (!errs && options.errs) { + errs = fs.createWriteStream(options.errs); + } + else if (!errs) { + errs = []; + } + if (typeof errs.write === 'undefined') { + errs.push(err); + } + else { + errs.write(err.stack + '\n\n'); + } + return cb(); + } - var currentClosestDistance = Infinity; - var currentClosestKeyword; + function cb(skipped) { + if (!skipped) running--; + finished++; + if ((started === finished) && (running === 0)) { + if (cback !== undefined ) { + return errs ? cback(errs) : cback(null); + } + } + } +} - for (var keyword in cssKeywords) { - if (cssKeywords.hasOwnProperty(keyword)) { - var value = cssKeywords[keyword]; - // Compute comparative distance - var distance = comparativeDistance(rgb, value); - // Check if its less, if so set as closest - if (distance < currentClosestDistance) { - currentClosestDistance = distance; - currentClosestKeyword = keyword; - } - } - } - return currentClosestKeyword; -}; +/***/ }), +/* 346 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { -convert.keyword.rgb = function (keyword) { - return cssKeywords[keyword]; -}; +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getProjects", function() { return getProjects; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getNonBazelProjectsOnly", function() { return getNonBazelProjectsOnly; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getBazelProjectsOnly", function() { return getBazelProjectsOnly; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "buildProjectGraph", function() { return buildProjectGraph; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "topologicallyBatchProjects", function() { return topologicallyBatchProjects; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "includeTransitiveProjects", function() { return includeTransitiveProjects; }); +/* harmony import */ var glob__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(244); +/* harmony import */ var glob__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(glob__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(113); +/* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(util__WEBPACK_IMPORTED_MODULE_2__); +/* harmony import */ var _errors__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(347); +/* harmony import */ var _project__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(348); +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ -convert.rgb.xyz = function (rgb) { - var r = rgb[0] / 255; - var g = rgb[1] / 255; - var b = rgb[2] / 255; - // assume sRGB - r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92); - g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92); - b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92); - var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805); - var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722); - var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505); - return [x * 100, y * 100, z * 100]; -}; -convert.rgb.lab = function (rgb) { - var xyz = convert.rgb.xyz(rgb); - var x = xyz[0]; - var y = xyz[1]; - var z = xyz[2]; - var l; - var a; - var b; +const glob = Object(util__WEBPACK_IMPORTED_MODULE_2__["promisify"])(glob__WEBPACK_IMPORTED_MODULE_0___default.a); +/** a Map of project names to Project instances */ - x /= 95.047; - y /= 100; - z /= 108.883; +async function getProjects(rootPath, projectsPathsPatterns, { + include = [], + exclude = [] +} = {}, bazelOnly = false) { + const projects = new Map(); - x = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116); - y = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116); - z = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116); + for (const pattern of projectsPathsPatterns) { + const pathsToProcess = await packagesFromGlobPattern({ + pattern, + rootPath + }); - l = (116 * y) - 16; - a = 500 * (x - y); - b = 200 * (y - z); + for (const filePath of pathsToProcess) { + const projectConfigPath = normalize(filePath); + const projectDir = path__WEBPACK_IMPORTED_MODULE_1___default.a.dirname(projectConfigPath); + const project = await _project__WEBPACK_IMPORTED_MODULE_4__["Project"].fromPath(projectDir); + const excludeProject = exclude.includes(project.name) || include.length > 0 && !include.includes(project.name) || bazelOnly && !project.isBazelPackage(); - return [l, a, b]; -}; + if (excludeProject) { + continue; + } -convert.hsl.rgb = function (hsl) { - var h = hsl[0] / 360; - var s = hsl[1] / 100; - var l = hsl[2] / 100; - var t1; - var t2; - var t3; - var rgb; - var val; + if (projects.has(project.name)) { + throw new _errors__WEBPACK_IMPORTED_MODULE_3__["CliError"](`There are multiple projects with the same name [${project.name}]`, { + name: project.name, + paths: [project.path, projects.get(project.name).path] + }); + } - if (s === 0) { - val = l * 255; - return [val, val, val]; - } + projects.set(project.name, project); + } + } - if (l < 0.5) { - t2 = l * (1 + s); - } else { - t2 = l + s - l * s; - } + return projects; +} +async function getNonBazelProjectsOnly(projects) { + const bazelProjectsOnly = new Map(); - t1 = 2 * l - t2; + for (const project of projects.values()) { + if (!project.isBazelPackage()) { + bazelProjectsOnly.set(project.name, project); + } + } - rgb = [0, 0, 0]; - for (var i = 0; i < 3; i++) { - t3 = h + 1 / 3 * -(i - 1); - if (t3 < 0) { - t3++; - } - if (t3 > 1) { - t3--; - } + return bazelProjectsOnly; +} +async function getBazelProjectsOnly(projects) { + const bazelProjectsOnly = new Map(); - if (6 * t3 < 1) { - val = t1 + (t2 - t1) * 6 * t3; - } else if (2 * t3 < 1) { - val = t2; - } else if (3 * t3 < 2) { - val = t1 + (t2 - t1) * (2 / 3 - t3) * 6; - } else { - val = t1; - } + for (const project of projects.values()) { + if (project.isBazelPackage()) { + bazelProjectsOnly.set(project.name, project); + } + } - rgb[i] = val * 255; - } + return bazelProjectsOnly; +} - return rgb; -}; +function packagesFromGlobPattern({ + pattern, + rootPath +}) { + const globOptions = { + cwd: rootPath, + // Should throw in case of unusual errors when reading the file system + strict: true, + // Always returns absolute paths for matched files + absolute: true, + // Do not match ** against multiple filenames + // (This is only specified because we currently don't have a need for it.) + noglobstar: true + }; + return glob(path__WEBPACK_IMPORTED_MODULE_1___default.a.join(pattern, 'package.json'), globOptions); +} // https://github.com/isaacs/node-glob/blob/master/common.js#L104 +// glob always returns "\\" as "/" in windows, so everyone +// gets normalized because we can't have nice things. -convert.hsl.hsv = function (hsl) { - var h = hsl[0]; - var s = hsl[1] / 100; - var l = hsl[2] / 100; - var smin = s; - var lmin = Math.max(l, 0.01); - var sv; - var v; - l *= 2; - s *= (l <= 1) ? l : 2 - l; - smin *= lmin <= 1 ? lmin : 2 - lmin; - v = (l + s) / 2; - sv = l === 0 ? (2 * smin) / (lmin + smin) : (2 * s) / (l + s); +function normalize(dir) { + return path__WEBPACK_IMPORTED_MODULE_1___default.a.normalize(dir); +} - return [h, sv * 100, v * 100]; -}; +function buildProjectGraph(projects) { + const projectGraph = new Map(); -convert.hsv.rgb = function (hsv) { - var h = hsv[0] / 60; - var s = hsv[1] / 100; - var v = hsv[2] / 100; - var hi = Math.floor(h) % 6; + for (const project of projects.values()) { + const projectDeps = []; + const dependencies = project.allDependencies; - var f = h - Math.floor(h); - var p = 255 * v * (1 - s); - var q = 255 * v * (1 - (s * f)); - var t = 255 * v * (1 - (s * (1 - f))); - v *= 255; + for (const depName of Object.keys(dependencies)) { + if (projects.has(depName)) { + const dep = projects.get(depName); + project.ensureValidProjectDependency(dep); + projectDeps.push(dep); + } + } - switch (hi) { - case 0: - return [v, t, p]; - case 1: - return [q, v, p]; - case 2: - return [p, v, t]; - case 3: - return [p, q, v]; - case 4: - return [t, p, v]; - case 5: - return [v, p, q]; - } -}; + projectGraph.set(project.name, projectDeps); + } -convert.hsv.hsl = function (hsv) { - var h = hsv[0]; - var s = hsv[1] / 100; - var v = hsv[2] / 100; - var vmin = Math.max(v, 0.01); - var lmin; - var sl; - var l; + return projectGraph; +} +function topologicallyBatchProjects(projectsToBatch, projectGraph) { + // We're going to be chopping stuff out of this list, so copy it. + const projectsLeftToBatch = new Set(projectsToBatch.keys()); + const batches = []; - l = (2 - s) * v; - lmin = (2 - s) * vmin; - sl = s * vmin; - sl /= (lmin <= 1) ? lmin : 2 - lmin; - sl = sl || 0; - l /= 2; + while (projectsLeftToBatch.size > 0) { + // Get all projects that have no remaining dependencies within the repo + // that haven't yet been picked. + const batch = []; - return [h, sl * 100, l * 100]; -}; + for (const projectName of projectsLeftToBatch) { + const projectDeps = projectGraph.get(projectName); + const needsDependenciesBatched = projectDeps.some(dep => projectsLeftToBatch.has(dep.name)); -// http://dev.w3.org/csswg/css-color/#hwb-to-rgb -convert.hwb.rgb = function (hwb) { - var h = hwb[0] / 360; - var wh = hwb[1] / 100; - var bl = hwb[2] / 100; - var ratio = wh + bl; - var i; - var v; - var f; - var n; - - // wh + bl cant be > 1 - if (ratio > 1) { - wh /= ratio; - bl /= ratio; - } - - i = Math.floor(6 * h); - v = 1 - bl; - f = 6 * h - i; - - if ((i & 0x01) !== 0) { - f = 1 - f; - } + if (!needsDependenciesBatched) { + batch.push(projectsToBatch.get(projectName)); + } + } // If we weren't able to find a project with no remaining dependencies, + // then we've encountered a cycle in the dependency graph. - n = wh + f * (v - wh); // linear interpolation - var r; - var g; - var b; - switch (i) { - default: - case 6: - case 0: r = v; g = n; b = wh; break; - case 1: r = n; g = v; b = wh; break; - case 2: r = wh; g = v; b = n; break; - case 3: r = wh; g = n; b = v; break; - case 4: r = n; g = wh; b = v; break; - case 5: r = v; g = wh; b = n; break; - } + const hasCycles = batch.length === 0; - return [r * 255, g * 255, b * 255]; -}; + if (hasCycles) { + const cycleProjectNames = [...projectsLeftToBatch]; + const message = 'Encountered a cycle in the dependency graph. Projects in cycle are:\n' + cycleProjectNames.join(', '); + throw new _errors__WEBPACK_IMPORTED_MODULE_3__["CliError"](message); + } -convert.cmyk.rgb = function (cmyk) { - var c = cmyk[0] / 100; - var m = cmyk[1] / 100; - var y = cmyk[2] / 100; - var k = cmyk[3] / 100; - var r; - var g; - var b; + batches.push(batch); + batch.forEach(project => projectsLeftToBatch.delete(project.name)); + } - r = 1 - Math.min(1, c * (1 - k) + k); - g = 1 - Math.min(1, m * (1 - k) + k); - b = 1 - Math.min(1, y * (1 - k) + k); + return batches; +} +function includeTransitiveProjects(subsetOfProjects, allProjects, { + onlyProductionDependencies = false +} = {}) { + const projectsWithDependents = new Map(); // the current list of packages we are expanding using breadth-first-search - return [r * 255, g * 255, b * 255]; -}; + const toProcess = [...subsetOfProjects]; -convert.xyz.rgb = function (xyz) { - var x = xyz[0] / 100; - var y = xyz[1] / 100; - var z = xyz[2] / 100; - var r; - var g; - var b; + while (toProcess.length > 0) { + const project = toProcess.shift(); + const dependencies = onlyProductionDependencies ? project.productionDependencies : project.allDependencies; + Object.keys(dependencies).forEach(dep => { + if (allProjects.has(dep)) { + toProcess.push(allProjects.get(dep)); + } + }); + projectsWithDependents.set(project.name, project); + } - r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986); - g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415); - b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570); + return projectsWithDependents; +} - // assume sRGB - r = r > 0.0031308 - ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055) - : r * 12.92; +/***/ }), +/* 347 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { - g = g > 0.0031308 - ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055) - : g * 12.92; +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "CliError", function() { return CliError; }); +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +class CliError extends Error { + constructor(message, meta = {}) { + super(message); + this.meta = meta; + } - b = b > 0.0031308 - ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055) - : b * 12.92; +} - r = Math.min(Math.max(0, r), 1); - g = Math.min(Math.max(0, g), 1); - b = Math.min(Math.max(0, b), 1); +/***/ }), +/* 348 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { - return [r * 255, g * 255, b * 255]; -}; +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Project", function() { return Project; }); +/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(132); +/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(fs__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(113); +/* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(util__WEBPACK_IMPORTED_MODULE_2__); +/* harmony import */ var _errors__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(347); +/* harmony import */ var _log__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(220); +/* harmony import */ var _package_json__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(349); +/* harmony import */ var _scripts__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(413); +function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } -convert.xyz.lab = function (xyz) { - var x = xyz[0]; - var y = xyz[1]; - var z = xyz[2]; - var l; - var a; - var b; +function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } - x /= 95.047; - y /= 100; - z /= 108.883; +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } - x = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116); - y = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116); - z = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116); +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ - l = (116 * y) - 16; - a = 500 * (x - y); - b = 200 * (y - z); - return [l, a, b]; -}; -convert.lab.xyz = function (lab) { - var l = lab[0]; - var a = lab[1]; - var b = lab[2]; - var x; - var y; - var z; - y = (l + 16) / 116; - x = a / 500 + y; - z = y - b / 200; - var y2 = Math.pow(y, 3); - var x2 = Math.pow(x, 3); - var z2 = Math.pow(z, 3); - y = y2 > 0.008856 ? y2 : (y - 16 / 116) / 7.787; - x = x2 > 0.008856 ? x2 : (x - 16 / 116) / 7.787; - z = z2 > 0.008856 ? z2 : (z - 16 / 116) / 7.787; - x *= 95.047; - y *= 100; - z *= 108.883; - return [x, y, z]; -}; +class Project { + static async fromPath(path) { + const pkgJson = await Object(_package_json__WEBPACK_IMPORTED_MODULE_5__["readPackageJson"])(path); + return new Project(pkgJson, path); + } + /** parsed package.json */ -convert.lab.lch = function (lab) { - var l = lab[0]; - var a = lab[1]; - var b = lab[2]; - var hr; - var h; - var c; - hr = Math.atan2(b, a); - h = hr * 360 / 2 / Math.PI; + constructor(packageJson, projectPath) { + _defineProperty(this, "json", void 0); - if (h < 0) { - h += 360; - } + _defineProperty(this, "packageJsonLocation", void 0); - c = Math.sqrt(a * a + b * b); + _defineProperty(this, "nodeModulesLocation", void 0); - return [l, c, h]; -}; + _defineProperty(this, "targetLocation", void 0); -convert.lch.lab = function (lch) { - var l = lch[0]; - var c = lch[1]; - var h = lch[2]; - var a; - var b; - var hr; + _defineProperty(this, "path", void 0); - hr = h / 360 * 2 * Math.PI; - a = c * Math.cos(hr); - b = c * Math.sin(hr); + _defineProperty(this, "version", void 0); - return [l, a, b]; -}; + _defineProperty(this, "allDependencies", void 0); -convert.rgb.ansi16 = function (args) { - var r = args[0]; - var g = args[1]; - var b = args[2]; - var value = 1 in arguments ? arguments[1] : convert.rgb.hsv(args)[2]; // hsv -> ansi16 optimization + _defineProperty(this, "productionDependencies", void 0); - value = Math.round(value / 50); + _defineProperty(this, "devDependencies", void 0); - if (value === 0) { - return 30; - } + _defineProperty(this, "scripts", void 0); - var ansi = 30 - + ((Math.round(b / 255) << 2) - | (Math.round(g / 255) << 1) - | Math.round(r / 255)); + _defineProperty(this, "bazelPackage", void 0); - if (value === 2) { - ansi += 60; - } + _defineProperty(this, "isSinglePackageJsonProject", false); - return ansi; -}; + this.json = Object.freeze(packageJson); + this.path = projectPath; + this.packageJsonLocation = path__WEBPACK_IMPORTED_MODULE_1___default.a.resolve(this.path, 'package.json'); + this.nodeModulesLocation = path__WEBPACK_IMPORTED_MODULE_1___default.a.resolve(this.path, 'node_modules'); + this.targetLocation = path__WEBPACK_IMPORTED_MODULE_1___default.a.resolve(this.path, 'target'); + this.version = this.json.version; + this.productionDependencies = this.json.dependencies || {}; + this.devDependencies = this.json.devDependencies || {}; + this.allDependencies = _objectSpread(_objectSpread({}, this.devDependencies), this.productionDependencies); + this.isSinglePackageJsonProject = this.json.name === 'kibana'; + this.scripts = this.json.scripts || {}; + this.bazelPackage = !this.isSinglePackageJsonProject && fs__WEBPACK_IMPORTED_MODULE_0___default.a.existsSync(path__WEBPACK_IMPORTED_MODULE_1___default.a.resolve(this.path, 'BUILD.bazel')); + } -convert.hsv.ansi16 = function (args) { - // optimization here; we already know the value and don't need to get - // it converted for us. - return convert.rgb.ansi16(convert.hsv.rgb(args), args[2]); -}; + get name() { + return this.json.name; + } -convert.rgb.ansi256 = function (args) { - var r = args[0]; - var g = args[1]; - var b = args[2]; + ensureValidProjectDependency(project) { + const relativePathToProject = normalizePath(path__WEBPACK_IMPORTED_MODULE_1___default.a.relative(this.path, project.path)); + const relativePathToProjectIfBazelPkg = normalizePath(path__WEBPACK_IMPORTED_MODULE_1___default.a.relative(this.path, `${__dirname}/../../../bazel-bin/packages/${path__WEBPACK_IMPORTED_MODULE_1___default.a.basename(project.path)}`)); + const versionInPackageJson = this.allDependencies[project.name]; + const expectedVersionInPackageJson = `link:${relativePathToProject}`; + const expectedVersionInPackageJsonIfBazelPkg = `link:${relativePathToProjectIfBazelPkg}`; // TODO: after introduce bazel to build all the packages and completely remove the support for kbn packages + // do not allow child projects to hold dependencies, unless they are meant to be published externally - // we use the extended greyscale palette here, with the exception of - // black and white. normal palette only has 4 greyscale shades. - if (r === g && g === b) { - if (r < 8) { - return 16; - } + if (versionInPackageJson === expectedVersionInPackageJson || versionInPackageJson === expectedVersionInPackageJsonIfBazelPkg) { + return; + } - if (r > 248) { - return 231; - } + const updateMsg = 'Update its package.json to the expected value below.'; + const meta = { + actual: `"${project.name}": "${versionInPackageJson}"`, + expected: `"${project.name}": "${expectedVersionInPackageJson}" or "${project.name}": "${expectedVersionInPackageJsonIfBazelPkg}"`, + package: `${this.name} (${this.packageJsonLocation})` + }; - return Math.round(((r - 8) / 247) * 24) + 232; - } + if (Object(_package_json__WEBPACK_IMPORTED_MODULE_5__["isLinkDependency"])(versionInPackageJson)) { + throw new _errors__WEBPACK_IMPORTED_MODULE_3__["CliError"](`[${this.name}] depends on [${project.name}] using 'link:', but the path is wrong. ${updateMsg}`, meta); + } - var ansi = 16 - + (36 * Math.round(r / 255 * 5)) - + (6 * Math.round(g / 255 * 5)) - + Math.round(b / 255 * 5); + throw new _errors__WEBPACK_IMPORTED_MODULE_3__["CliError"](`[${this.name}] depends on [${project.name}] but it's not using the local package. ${updateMsg}`, meta); + } - return ansi; -}; + getBuildConfig() { + return this.json.kibana && this.json.kibana.build || {}; + } + /** + * Returns the directory that should be copied into the Kibana build artifact. + * This config can be specified to only include the project's build artifacts + * instead of everything located in the project directory. + */ -convert.ansi16.rgb = function (args) { - var color = args % 10; - // handle greyscale - if (color === 0 || color === 7) { - if (args > 50) { - color += 3.5; - } + getIntermediateBuildDirectory() { + return path__WEBPACK_IMPORTED_MODULE_1___default.a.resolve(this.path, this.getBuildConfig().intermediateBuildDirectory || '.'); + } - color = color / 10.5 * 255; + getCleanConfig() { + return this.json.kibana && this.json.kibana.clean || {}; + } - return [color, color, color]; - } + isBazelPackage() { + return this.bazelPackage; + } - var mult = (~~(args > 50) + 1) * 0.5; - var r = ((color & 1) * mult) * 255; - var g = (((color >> 1) & 1) * mult) * 255; - var b = (((color >> 2) & 1) * mult) * 255; + isFlaggedAsDevOnly() { + return !!(this.json.kibana && this.json.kibana.devOnly); + } - return [r, g, b]; -}; + hasScript(name) { + return name in this.scripts; + } -convert.ansi256.rgb = function (args) { - // handle greyscale - if (args >= 232) { - var c = (args - 232) * 10 + 8; - return [c, c, c]; - } + getExecutables() { + const raw = this.json.bin; - args -= 16; + if (!raw) { + return {}; + } - var rem; - var r = Math.floor(args / 36) / 5 * 255; - var g = Math.floor((rem = args % 36) / 6) / 5 * 255; - var b = (rem % 6) / 5 * 255; + if (typeof raw === 'string') { + return { + [this.name]: path__WEBPACK_IMPORTED_MODULE_1___default.a.resolve(this.path, raw) + }; + } - return [r, g, b]; -}; + if (typeof raw === 'object') { + const binsConfig = {}; -convert.rgb.hex = function (args) { - var integer = ((Math.round(args[0]) & 0xFF) << 16) - + ((Math.round(args[1]) & 0xFF) << 8) - + (Math.round(args[2]) & 0xFF); + for (const binName of Object.keys(raw)) { + binsConfig[binName] = path__WEBPACK_IMPORTED_MODULE_1___default.a.resolve(this.path, raw[binName]); + } - var string = integer.toString(16).toUpperCase(); - return '000000'.substring(string.length) + string; -}; + return binsConfig; + } -convert.hex.rgb = function (args) { - var match = args.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i); - if (!match) { - return [0, 0, 0]; - } + throw new _errors__WEBPACK_IMPORTED_MODULE_3__["CliError"](`[${this.name}] has an invalid "bin" field in its package.json, ` + `expected an object or a string`, { + binConfig: Object(util__WEBPACK_IMPORTED_MODULE_2__["inspect"])(raw), + package: `${this.name} (${this.packageJsonLocation})` + }); + } - var colorString = match[0]; + async runScript(scriptName, args = []) { + _log__WEBPACK_IMPORTED_MODULE_4__["log"].info(`Running script [${scriptName}] in [${this.name}]:`); + return Object(_scripts__WEBPACK_IMPORTED_MODULE_6__["runScriptInPackage"])(scriptName, args, this); + } - if (match[0].length === 3) { - colorString = colorString.split('').map(function (char) { - return char + char; - }).join(''); - } + runScriptStreaming(scriptName, options = {}) { + return Object(_scripts__WEBPACK_IMPORTED_MODULE_6__["runScriptInPackageStreaming"])({ + script: scriptName, + args: options.args || [], + pkg: this, + debug: options.debug + }); + } - var integer = parseInt(colorString, 16); - var r = (integer >> 16) & 0xFF; - var g = (integer >> 8) & 0xFF; - var b = integer & 0xFF; + hasDependencies() { + return Object.keys(this.allDependencies).length > 0; + } - return [r, g, b]; -}; + isEveryDependencyLocal() { + return Object.values(this.allDependencies).every(dep => Object(_package_json__WEBPACK_IMPORTED_MODULE_5__["isLinkDependency"])(dep)); + } -convert.rgb.hcg = function (rgb) { - var r = rgb[0] / 255; - var g = rgb[1] / 255; - var b = rgb[2] / 255; - var max = Math.max(Math.max(r, g), b); - var min = Math.min(Math.min(r, g), b); - var chroma = (max - min); - var grayscale; - var hue; + async installDependencies(options = {}) { + _log__WEBPACK_IMPORTED_MODULE_4__["log"].info(`[${this.name}] running yarn`); + _log__WEBPACK_IMPORTED_MODULE_4__["log"].write(''); + await Object(_scripts__WEBPACK_IMPORTED_MODULE_6__["installInDir"])(this.path, options === null || options === void 0 ? void 0 : options.extraArgs); + _log__WEBPACK_IMPORTED_MODULE_4__["log"].write(''); + } - if (chroma < 1) { - grayscale = min / (1 - chroma); - } else { - grayscale = 0; - } +} // We normalize all path separators to `/` in generated files - if (chroma <= 0) { - hue = 0; - } else - if (max === r) { - hue = ((g - b) / chroma) % 6; - } else - if (max === g) { - hue = 2 + (b - r) / chroma; - } else { - hue = 4 + (r - g) / chroma + 4; - } +function normalizePath(path) { + return path.replace(/[\\\/]+/g, '/'); +} - hue /= 6; - hue %= 1; +/***/ }), +/* 349 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { - return [hue * 360, chroma * 100, grayscale * 100]; -}; +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "readPackageJson", function() { return readPackageJson; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "writePackageJson", function() { return writePackageJson; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "createProductionPackageJson", function() { return createProductionPackageJson; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isLinkDependency", function() { return isLinkDependency; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isBazelPackageDependency", function() { return isBazelPackageDependency; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "transformDependencies", function() { return transformDependencies; }); +/* harmony import */ var read_pkg__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(350); +/* harmony import */ var read_pkg__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(read_pkg__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var write_pkg__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(402); +/* harmony import */ var write_pkg__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(write_pkg__WEBPACK_IMPORTED_MODULE_1__); +function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } -convert.hsl.hcg = function (hsl) { - var s = hsl[1] / 100; - var l = hsl[2] / 100; - var c = 1; - var f = 0; +function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } - if (l < 0.5) { - c = 2.0 * s * l; - } else { - c = 2.0 * s * (1.0 - l); - } +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } - if (c < 1.0) { - f = (l - 0.5 * c) / (1.0 - c); - } +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ - return [hsl[0], c * 100, f * 100]; -}; -convert.hsv.hcg = function (hsv) { - var s = hsv[1] / 100; - var v = hsv[2] / 100; +function readPackageJson(cwd) { + return read_pkg__WEBPACK_IMPORTED_MODULE_0___default()({ + cwd, + normalize: false + }); +} +function writePackageJson(path, json) { + return write_pkg__WEBPACK_IMPORTED_MODULE_1___default()(path, json); +} +const createProductionPackageJson = pkgJson => _objectSpread(_objectSpread({}, pkgJson), {}, { + dependencies: transformDependencies(pkgJson.dependencies) +}); +const isLinkDependency = depVersion => depVersion.startsWith('link:'); +const isBazelPackageDependency = depVersion => depVersion.startsWith('link:bazel-bin/'); +/** + * Replaces `link:` dependencies with `file:` dependencies. When installing + * dependencies, these `file:` dependencies will be copied into `node_modules` + * instead of being symlinked. + * + * This will allow us to copy packages into the build and run `yarn`, which + * will then _copy_ the `file:` dependencies into `node_modules` instead of + * symlinking like we do in development. + * + * Additionally it also taken care of replacing `link:bazel-bin/` with + * `file:` so we can also support the copy of the Bazel packages dist already into + * build/packages to be copied into the node_modules + */ - var c = s * v; - var f = 0; +function transformDependencies(dependencies = {}) { + const newDeps = {}; - if (c < 1.0) { - f = (v - c) / (1 - c); - } + for (const name of Object.keys(dependencies)) { + const depVersion = dependencies[name]; - return [hsv[0], c * 100, f * 100]; -}; + if (!isLinkDependency(depVersion)) { + newDeps[name] = depVersion; + continue; + } -convert.hcg.rgb = function (hcg) { - var h = hcg[0] / 360; - var c = hcg[1] / 100; - var g = hcg[2] / 100; + if (isBazelPackageDependency(depVersion)) { + newDeps[name] = depVersion.replace('link:bazel-bin/', 'file:'); + continue; + } - if (c === 0.0) { - return [g * 255, g * 255, g * 255]; - } + newDeps[name] = depVersion.replace('link:', 'file:'); + } - var pure = [0, 0, 0]; - var hi = (h % 1) * 6; - var v = hi % 1; - var w = 1 - v; - var mg = 0; + return newDeps; +} - switch (Math.floor(hi)) { - case 0: - pure[0] = 1; pure[1] = v; pure[2] = 0; break; - case 1: - pure[0] = w; pure[1] = 1; pure[2] = 0; break; - case 2: - pure[0] = 0; pure[1] = 1; pure[2] = v; break; - case 3: - pure[0] = 0; pure[1] = w; pure[2] = 1; break; - case 4: - pure[0] = v; pure[1] = 0; pure[2] = 1; break; - default: - pure[0] = 1; pure[1] = 0; pure[2] = w; - } +/***/ }), +/* 350 */ +/***/ (function(module, exports, __webpack_require__) { - mg = (1.0 - c) * g; +"use strict"; - return [ - (c * pure[0] + mg) * 255, - (c * pure[1] + mg) * 255, - (c * pure[2] + mg) * 255 - ]; -}; +const {promisify} = __webpack_require__(113); +const fs = __webpack_require__(132); +const path = __webpack_require__(4); +const parseJson = __webpack_require__(351); -convert.hcg.hsv = function (hcg) { - var c = hcg[1] / 100; - var g = hcg[2] / 100; +const readFileAsync = promisify(fs.readFile); - var v = c + g * (1.0 - c); - var f = 0; +module.exports = async options => { + options = { + cwd: process.cwd(), + normalize: true, + ...options + }; - if (v > 0.0) { - f = c / v; + const filePath = path.resolve(options.cwd, 'package.json'); + const json = parseJson(await readFileAsync(filePath, 'utf8')); + + if (options.normalize) { + __webpack_require__(372)(json); } - return [hcg[0], f * 100, v * 100]; + return json; }; -convert.hcg.hsl = function (hcg) { - var c = hcg[1] / 100; - var g = hcg[2] / 100; +module.exports.sync = options => { + options = { + cwd: process.cwd(), + normalize: true, + ...options + }; - var l = g * (1.0 - c) + 0.5 * c; - var s = 0; + const filePath = path.resolve(options.cwd, 'package.json'); + const json = parseJson(fs.readFileSync(filePath, 'utf8')); - if (l > 0.0 && l < 0.5) { - s = c / (2 * l); - } else - if (l >= 0.5 && l < 1.0) { - s = c / (2 * (1 - l)); + if (options.normalize) { + __webpack_require__(372)(json); } - return [hcg[0], s * 100, l * 100]; -}; - -convert.hcg.hwb = function (hcg) { - var c = hcg[1] / 100; - var g = hcg[2] / 100; - var v = c + g * (1.0 - c); - return [hcg[0], (v - c) * 100, (1 - v) * 100]; + return json; }; -convert.hwb.hcg = function (hwb) { - var w = hwb[1] / 100; - var b = hwb[2] / 100; - var v = 1 - b; - var c = v - w; - var g = 0; - - if (c < 1) { - g = (v - c) / (1 - c); - } - return [hwb[0], c * 100, g * 100]; -}; +/***/ }), +/* 351 */ +/***/ (function(module, exports, __webpack_require__) { -convert.apple.rgb = function (apple) { - return [(apple[0] / 65535) * 255, (apple[1] / 65535) * 255, (apple[2] / 65535) * 255]; -}; +"use strict"; -convert.rgb.apple = function (rgb) { - return [(rgb[0] / 255) * 65535, (rgb[1] / 255) * 65535, (rgb[2] / 255) * 65535]; -}; +const errorEx = __webpack_require__(352); +const fallback = __webpack_require__(354); +const {default: LinesAndColumns} = __webpack_require__(355); +const {codeFrameColumns} = __webpack_require__(356); -convert.gray.rgb = function (args) { - return [args[0] / 100 * 255, args[0] / 100 * 255, args[0] / 100 * 255]; -}; +const JSONError = errorEx('JSONError', { + fileName: errorEx.append('in %s'), + codeFrame: errorEx.append('\n\n%s\n') +}); -convert.gray.hsl = convert.gray.hsv = function (args) { - return [0, 0, args[0]]; -}; +module.exports = (string, reviver, filename) => { + if (typeof reviver === 'string') { + filename = reviver; + reviver = null; + } -convert.gray.hwb = function (gray) { - return [0, 100, gray[0]]; -}; + try { + try { + return JSON.parse(string, reviver); + } catch (error) { + fallback(string, reviver); + throw error; + } + } catch (error) { + error.message = error.message.replace(/\n/g, ''); + const indexMatch = error.message.match(/in JSON at position (\d+) while parsing near/); -convert.gray.cmyk = function (gray) { - return [0, 0, 0, gray[0]]; -}; + const jsonError = new JSONError(error); + if (filename) { + jsonError.fileName = filename; + } -convert.gray.lab = function (gray) { - return [gray[0], 0, 0]; -}; + if (indexMatch && indexMatch.length > 0) { + const lines = new LinesAndColumns(string); + const index = Number(indexMatch[1]); + const location = lines.locationForIndex(index); -convert.gray.hex = function (gray) { - var val = Math.round(gray[0] / 100 * 255) & 0xFF; - var integer = (val << 16) + (val << 8) + val; + const codeFrame = codeFrameColumns( + string, + {start: {line: location.line + 1, column: location.column + 1}}, + {highlightCode: true} + ); - var string = integer.toString(16).toUpperCase(); - return '000000'.substring(string.length) + string; -}; + jsonError.codeFrame = codeFrame; + } -convert.rgb.gray = function (rgb) { - var val = (rgb[0] + rgb[1] + rgb[2]) / 3; - return [val / 255 * 100]; + throw jsonError; + } }; /***/ }), -/* 361 */ +/* 352 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - - -module.exports = { - "aliceblue": [240, 248, 255], - "antiquewhite": [250, 235, 215], - "aqua": [0, 255, 255], - "aquamarine": [127, 255, 212], - "azure": [240, 255, 255], - "beige": [245, 245, 220], - "bisque": [255, 228, 196], - "black": [0, 0, 0], - "blanchedalmond": [255, 235, 205], - "blue": [0, 0, 255], - "blueviolet": [138, 43, 226], - "brown": [165, 42, 42], - "burlywood": [222, 184, 135], - "cadetblue": [95, 158, 160], - "chartreuse": [127, 255, 0], - "chocolate": [210, 105, 30], - "coral": [255, 127, 80], - "cornflowerblue": [100, 149, 237], - "cornsilk": [255, 248, 220], - "crimson": [220, 20, 60], - "cyan": [0, 255, 255], - "darkblue": [0, 0, 139], - "darkcyan": [0, 139, 139], - "darkgoldenrod": [184, 134, 11], - "darkgray": [169, 169, 169], - "darkgreen": [0, 100, 0], - "darkgrey": [169, 169, 169], - "darkkhaki": [189, 183, 107], - "darkmagenta": [139, 0, 139], - "darkolivegreen": [85, 107, 47], - "darkorange": [255, 140, 0], - "darkorchid": [153, 50, 204], - "darkred": [139, 0, 0], - "darksalmon": [233, 150, 122], - "darkseagreen": [143, 188, 143], - "darkslateblue": [72, 61, 139], - "darkslategray": [47, 79, 79], - "darkslategrey": [47, 79, 79], - "darkturquoise": [0, 206, 209], - "darkviolet": [148, 0, 211], - "deeppink": [255, 20, 147], - "deepskyblue": [0, 191, 255], - "dimgray": [105, 105, 105], - "dimgrey": [105, 105, 105], - "dodgerblue": [30, 144, 255], - "firebrick": [178, 34, 34], - "floralwhite": [255, 250, 240], - "forestgreen": [34, 139, 34], - "fuchsia": [255, 0, 255], - "gainsboro": [220, 220, 220], - "ghostwhite": [248, 248, 255], - "gold": [255, 215, 0], - "goldenrod": [218, 165, 32], - "gray": [128, 128, 128], - "green": [0, 128, 0], - "greenyellow": [173, 255, 47], - "grey": [128, 128, 128], - "honeydew": [240, 255, 240], - "hotpink": [255, 105, 180], - "indianred": [205, 92, 92], - "indigo": [75, 0, 130], - "ivory": [255, 255, 240], - "khaki": [240, 230, 140], - "lavender": [230, 230, 250], - "lavenderblush": [255, 240, 245], - "lawngreen": [124, 252, 0], - "lemonchiffon": [255, 250, 205], - "lightblue": [173, 216, 230], - "lightcoral": [240, 128, 128], - "lightcyan": [224, 255, 255], - "lightgoldenrodyellow": [250, 250, 210], - "lightgray": [211, 211, 211], - "lightgreen": [144, 238, 144], - "lightgrey": [211, 211, 211], - "lightpink": [255, 182, 193], - "lightsalmon": [255, 160, 122], - "lightseagreen": [32, 178, 170], - "lightskyblue": [135, 206, 250], - "lightslategray": [119, 136, 153], - "lightslategrey": [119, 136, 153], - "lightsteelblue": [176, 196, 222], - "lightyellow": [255, 255, 224], - "lime": [0, 255, 0], - "limegreen": [50, 205, 50], - "linen": [250, 240, 230], - "magenta": [255, 0, 255], - "maroon": [128, 0, 0], - "mediumaquamarine": [102, 205, 170], - "mediumblue": [0, 0, 205], - "mediumorchid": [186, 85, 211], - "mediumpurple": [147, 112, 219], - "mediumseagreen": [60, 179, 113], - "mediumslateblue": [123, 104, 238], - "mediumspringgreen": [0, 250, 154], - "mediumturquoise": [72, 209, 204], - "mediumvioletred": [199, 21, 133], - "midnightblue": [25, 25, 112], - "mintcream": [245, 255, 250], - "mistyrose": [255, 228, 225], - "moccasin": [255, 228, 181], - "navajowhite": [255, 222, 173], - "navy": [0, 0, 128], - "oldlace": [253, 245, 230], - "olive": [128, 128, 0], - "olivedrab": [107, 142, 35], - "orange": [255, 165, 0], - "orangered": [255, 69, 0], - "orchid": [218, 112, 214], - "palegoldenrod": [238, 232, 170], - "palegreen": [152, 251, 152], - "paleturquoise": [175, 238, 238], - "palevioletred": [219, 112, 147], - "papayawhip": [255, 239, 213], - "peachpuff": [255, 218, 185], - "peru": [205, 133, 63], - "pink": [255, 192, 203], - "plum": [221, 160, 221], - "powderblue": [176, 224, 230], - "purple": [128, 0, 128], - "rebeccapurple": [102, 51, 153], - "red": [255, 0, 0], - "rosybrown": [188, 143, 143], - "royalblue": [65, 105, 225], - "saddlebrown": [139, 69, 19], - "salmon": [250, 128, 114], - "sandybrown": [244, 164, 96], - "seagreen": [46, 139, 87], - "seashell": [255, 245, 238], - "sienna": [160, 82, 45], - "silver": [192, 192, 192], - "skyblue": [135, 206, 235], - "slateblue": [106, 90, 205], - "slategray": [112, 128, 144], - "slategrey": [112, 128, 144], - "snow": [255, 250, 250], - "springgreen": [0, 255, 127], - "steelblue": [70, 130, 180], - "tan": [210, 180, 140], - "teal": [0, 128, 128], - "thistle": [216, 191, 216], - "tomato": [255, 99, 71], - "turquoise": [64, 224, 208], - "violet": [238, 130, 238], - "wheat": [245, 222, 179], - "white": [255, 255, 255], - "whitesmoke": [245, 245, 245], - "yellow": [255, 255, 0], - "yellowgreen": [154, 205, 50] -}; -/***/ }), -/* 362 */ -/***/ (function(module, exports, __webpack_require__) { +var util = __webpack_require__(113); +var isArrayish = __webpack_require__(353); -var conversions = __webpack_require__(360); +var errorEx = function errorEx(name, properties) { + if (!name || name.constructor !== String) { + properties = name || {}; + name = Error.name; + } -/* - this function routes a model to all other models. + var errorExError = function ErrorEXError(message) { + if (!this) { + return new ErrorEXError(message); + } - all functions that are routed have a property `.conversion` attached - to the returned synthetic function. This property is an array - of strings, each with the steps in between the 'from' and 'to' - color models (inclusive). + message = message instanceof Error + ? message.message + : (message || this.message); - conversions that are not possible simply are not included. -*/ + Error.call(this, message); + Error.captureStackTrace(this, errorExError); -function buildGraph() { - var graph = {}; - // https://jsperf.com/object-keys-vs-for-in-with-closure/3 - var models = Object.keys(conversions); + this.name = name; - for (var len = models.length, i = 0; i < len; i++) { - graph[models[i]] = { - // http://jsperf.com/1-vs-infinity - // micro-opt, but this is simple. - distance: -1, - parent: null - }; - } + Object.defineProperty(this, 'message', { + configurable: true, + enumerable: false, + get: function () { + var newMessage = message.split(/\r?\n/g); - return graph; -} + for (var key in properties) { + if (!properties.hasOwnProperty(key)) { + continue; + } -// https://en.wikipedia.org/wiki/Breadth-first_search -function deriveBFS(fromModel) { - var graph = buildGraph(); - var queue = [fromModel]; // unshift -> queue -> pop + var modifier = properties[key]; - graph[fromModel].distance = 0; + if ('message' in modifier) { + newMessage = modifier.message(this[key], newMessage) || newMessage; + if (!isArrayish(newMessage)) { + newMessage = [newMessage]; + } + } + } - while (queue.length) { - var current = queue.pop(); - var adjacents = Object.keys(conversions[current]); + return newMessage.join('\n'); + }, + set: function (v) { + message = v; + } + }); - for (var len = adjacents.length, i = 0; i < len; i++) { - var adjacent = adjacents[i]; - var node = graph[adjacent]; + var stackDescriptor = Object.getOwnPropertyDescriptor(this, 'stack'); + var stackGetter = stackDescriptor.get; + var stackValue = stackDescriptor.value; + delete stackDescriptor.value; + delete stackDescriptor.writable; - if (node.distance === -1) { - node.distance = graph[current].distance + 1; - node.parent = current; - queue.unshift(adjacent); - } - } - } + stackDescriptor.get = function () { + var stack = (stackGetter) + ? stackGetter.call(this).split(/\r?\n+/g) + : stackValue.split(/\r?\n+/g); - return graph; -} + // starting in Node 7, the stack builder caches the message. + // just replace it. + stack[0] = this.name + ': ' + this.message; -function link(from, to) { - return function (args) { - return to(from(args)); - }; -} + var lineCount = 1; + for (var key in properties) { + if (!properties.hasOwnProperty(key)) { + continue; + } -function wrapConversion(toModel, graph) { - var path = [graph[toModel].parent, toModel]; - var fn = conversions[graph[toModel].parent][toModel]; + var modifier = properties[key]; - var cur = graph[toModel].parent; - while (graph[cur].parent) { - path.unshift(graph[cur].parent); - fn = link(conversions[graph[cur].parent][cur], fn); - cur = graph[cur].parent; - } + if ('line' in modifier) { + var line = modifier.line(this[key]); + if (line) { + stack.splice(lineCount++, 0, ' ' + line); + } + } - fn.conversion = path; - return fn; -} + if ('stack' in modifier) { + modifier.stack(this[key], stack); + } + } -module.exports = function (fromModel) { - var graph = deriveBFS(fromModel); - var conversion = {}; + return stack.join('\n'); + }; - var models = Object.keys(graph); - for (var len = models.length, i = 0; i < len; i++) { - var toModel = models[i]; - var node = graph[toModel]; + Object.defineProperty(this, 'stack', stackDescriptor); + }; - if (node.parent === null) { - // no possible conversion, or this node is the source model. - continue; + if (Object.setPrototypeOf) { + Object.setPrototypeOf(errorExError.prototype, Error.prototype); + Object.setPrototypeOf(errorExError, Error); + } else { + util.inherits(errorExError, Error); + } + + return errorExError; +}; + +errorEx.append = function (str, def) { + return { + message: function (v, message) { + v = v || def; + + if (v) { + message[0] += ' ' + str.replace('%s', v.toString()); + } + + return message; } + }; +}; - conversion[toModel] = wrapConversion(toModel, graph); - } +errorEx.line = function (str, def) { + return { + line: function (v) { + v = v || def; - return conversion; + if (v) { + return str.replace('%s', v.toString()); + } + + return null; + } + }; }; +module.exports = errorEx; /***/ }), -/* 363 */ +/* 353 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const os = __webpack_require__(122); -const hasFlag = __webpack_require__(364); - -const env = process.env; - -let forceColor; -if (hasFlag('no-color') || - hasFlag('no-colors') || - hasFlag('color=false')) { - forceColor = false; -} else if (hasFlag('color') || - hasFlag('colors') || - hasFlag('color=true') || - hasFlag('color=always')) { - forceColor = true; -} -if ('FORCE_COLOR' in env) { - forceColor = env.FORCE_COLOR.length === 0 || parseInt(env.FORCE_COLOR, 10) !== 0; -} -function translateLevel(level) { - if (level === 0) { +module.exports = function isArrayish(obj) { + if (!obj) { return false; } - return { - level, - hasBasic: true, - has256: level >= 2, - has16m: level >= 3 - }; -} + return obj instanceof Array || Array.isArray(obj) || + (obj.length >= 0 && obj.splice instanceof Function); +}; -function supportsColor(stream) { - if (forceColor === false) { - return 0; - } - if (hasFlag('color=16m') || - hasFlag('color=full') || - hasFlag('color=truecolor')) { - return 3; - } +/***/ }), +/* 354 */ +/***/ (function(module, exports, __webpack_require__) { - if (hasFlag('color=256')) { - return 2; - } +"use strict"; - if (stream && !stream.isTTY && forceColor !== true) { - return 0; - } - const min = forceColor ? 1 : 0; +module.exports = parseJson +function parseJson (txt, reviver, context) { + context = context || 20 + try { + return JSON.parse(txt, reviver) + } catch (e) { + if (typeof txt !== 'string') { + const isEmptyArray = Array.isArray(txt) && txt.length === 0 + const errorMessage = 'Cannot parse ' + + (isEmptyArray ? 'an empty array' : String(txt)) + throw new TypeError(errorMessage) + } + const syntaxErr = e.message.match(/^Unexpected token.*position\s+(\d+)/i) + const errIdx = syntaxErr + ? +syntaxErr[1] + : e.message.match(/^Unexpected end of JSON.*/i) + ? txt.length - 1 + : null + if (errIdx != null) { + const start = errIdx <= context + ? 0 + : errIdx - context + const end = errIdx + context >= txt.length + ? txt.length + : errIdx + context + e.message += ` while parsing near '${ + start === 0 ? '' : '...' + }${txt.slice(start, end)}${ + end === txt.length ? '' : '...' + }'` + } else { + e.message += ` while parsing '${txt.slice(0, context * 2)}'` + } + throw e + } +} - if (process.platform === 'win32') { - // Node.js 7.5.0 is the first version of Node.js to include a patch to - // libuv that enables 256 color output on Windows. Anything earlier and it - // won't work. However, here we target Node.js 8 at minimum as it is an LTS - // release, and Node.js 7 is not. Windows 10 build 10586 is the first Windows - // release that supports 256 colors. Windows 10 build 14931 is the first release - // that supports 16m/TrueColor. - const osRelease = os.release().split('.'); - if ( - Number(process.versions.node.split('.')[0]) >= 8 && - Number(osRelease[0]) >= 10 && - Number(osRelease[2]) >= 10586 - ) { - return Number(osRelease[2]) >= 14931 ? 3 : 2; - } - return 1; - } +/***/ }), +/* 355 */ +/***/ (function(__webpack_module__, __webpack_exports__, __webpack_require__) { - if ('CI' in env) { - if (['TRAVIS', 'CIRCLECI', 'APPVEYOR', 'GITLAB_CI'].some(sign => sign in env) || env.CI_NAME === 'codeship') { - return 1; - } +"use strict"; +__webpack_require__.r(__webpack_exports__); +var LF = '\n'; +var CR = '\r'; +var LinesAndColumns = (function () { + function LinesAndColumns(string) { + this.string = string; + var offsets = [0]; + for (var offset = 0; offset < string.length;) { + switch (string[offset]) { + case LF: + offset += LF.length; + offsets.push(offset); + break; + case CR: + offset += CR.length; + if (string[offset] === LF) { + offset += LF.length; + } + offsets.push(offset); + break; + default: + offset++; + break; + } + } + this.offsets = offsets; + } + LinesAndColumns.prototype.locationForIndex = function (index) { + if (index < 0 || index > this.string.length) { + return null; + } + var line = 0; + var offsets = this.offsets; + while (offsets[line + 1] <= index) { + line++; + } + var column = index - offsets[line]; + return { line: line, column: column }; + }; + LinesAndColumns.prototype.indexForLocation = function (location) { + var line = location.line, column = location.column; + if (line < 0 || line >= this.offsets.length) { + return null; + } + if (column < 0 || column > this.lengthOfLine(line)) { + return null; + } + return this.offsets[line] + column; + }; + LinesAndColumns.prototype.lengthOfLine = function (line) { + var offset = this.offsets[line]; + var nextOffset = line === this.offsets.length - 1 ? this.string.length : this.offsets[line + 1]; + return nextOffset - offset; + }; + return LinesAndColumns; +}()); +/* harmony default export */ __webpack_exports__["default"] = (LinesAndColumns); - return min; - } - if ('TEAMCITY_VERSION' in env) { - return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0; - } +/***/ }), +/* 356 */ +/***/ (function(module, exports, __webpack_require__) { - if (env.COLORTERM === 'truecolor') { - return 3; - } +"use strict"; - if ('TERM_PROGRAM' in env) { - const version = parseInt((env.TERM_PROGRAM_VERSION || '').split('.')[0], 10); - switch (env.TERM_PROGRAM) { - case 'iTerm.app': - return version >= 3 ? 3 : 2; - case 'Apple_Terminal': - return 2; - // No default - } - } +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.codeFrameColumns = codeFrameColumns; +exports.default = _default; - if (/-256(color)?$/i.test(env.TERM)) { - return 2; - } +var _highlight = _interopRequireWildcard(__webpack_require__(357)); - if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) { - return 1; - } +function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; } - if ('COLORTERM' in env) { - return 1; - } +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } - if (env.TERM === 'dumb') { - return min; - } +let deprecationWarningShown = false; - return min; +function getDefs(chalk) { + return { + gutter: chalk.grey, + marker: chalk.red.bold, + message: chalk.red.bold + }; } -function getSupportLevel(stream) { - const level = supportsColor(stream); - return translateLevel(level); -} +const NEWLINE = /\r\n|[\n\r\u2028\u2029]/; -module.exports = { - supportsColor: getSupportLevel, - stdout: getSupportLevel(process.stdout), - stderr: getSupportLevel(process.stderr) -}; +function getMarkerLines(loc, source, opts) { + const startLoc = Object.assign({ + column: 0, + line: -1 + }, loc.start); + const endLoc = Object.assign({}, startLoc, loc.end); + const { + linesAbove = 2, + linesBelow = 3 + } = opts || {}; + const startLine = startLoc.line; + const startColumn = startLoc.column; + const endLine = endLoc.line; + const endColumn = endLoc.column; + let start = Math.max(startLine - (linesAbove + 1), 0); + let end = Math.min(source.length, endLine + linesBelow); + if (startLine === -1) { + start = 0; + } -/***/ }), -/* 364 */ -/***/ (function(module, exports, __webpack_require__) { + if (endLine === -1) { + end = source.length; + } -"use strict"; + const lineDiff = endLine - startLine; + const markerLines = {}; -module.exports = (flag, argv) => { - argv = argv || process.argv; - const prefix = flag.startsWith('-') ? '' : (flag.length === 1 ? '-' : '--'); - const pos = argv.indexOf(prefix + flag); - const terminatorPos = argv.indexOf('--'); - return pos !== -1 && (terminatorPos === -1 ? true : pos < terminatorPos); -}; + if (lineDiff) { + for (let i = 0; i <= lineDiff; i++) { + const lineNumber = i + startLine; + if (!startColumn) { + markerLines[lineNumber] = true; + } else if (i === 0) { + const sourceLength = source[lineNumber - 1].length; + markerLines[lineNumber] = [startColumn, sourceLength - startColumn + 1]; + } else if (i === lineDiff) { + markerLines[lineNumber] = [0, endColumn]; + } else { + const sourceLength = source[lineNumber - i].length; + markerLines[lineNumber] = [0, sourceLength]; + } + } + } else { + if (startColumn === endColumn) { + if (startColumn) { + markerLines[startLine] = [startColumn, 0]; + } else { + markerLines[startLine] = true; + } + } else { + markerLines[startLine] = [startColumn, endColumn - startColumn]; + } + } -/***/ }), -/* 365 */ -/***/ (function(module, exports, __webpack_require__) { + return { + start, + end, + markerLines + }; +} -"use strict"; +function codeFrameColumns(rawLines, loc, opts = {}) { + const highlighted = (opts.highlightCode || opts.forceColor) && (0, _highlight.shouldHighlight)(opts); + const chalk = (0, _highlight.getChalk)(opts); + const defs = getDefs(chalk); -const TEMPLATE_REGEX = /(?:\\(u[a-f\d]{4}|x[a-f\d]{2}|.))|(?:\{(~)?(\w+(?:\([^)]*\))?(?:\.\w+(?:\([^)]*\))?)*)(?:[ \t]|(?=\r?\n)))|(\})|((?:.|[\r\n\f])+?)/gi; -const STYLE_REGEX = /(?:^|\.)(\w+)(?:\(([^)]*)\))?/g; -const STRING_REGEX = /^(['"])((?:\\.|(?!\1)[^\\])*)\1$/; -const ESCAPE_REGEX = /\\(u[a-f\d]{4}|x[a-f\d]{2}|.)|([^\\])/gi; + const maybeHighlight = (chalkFn, string) => { + return highlighted ? chalkFn(string) : string; + }; -const ESCAPES = new Map([ - ['n', '\n'], - ['r', '\r'], - ['t', '\t'], - ['b', '\b'], - ['f', '\f'], - ['v', '\v'], - ['0', '\0'], - ['\\', '\\'], - ['e', '\u001B'], - ['a', '\u0007'] -]); + const lines = rawLines.split(NEWLINE); + const { + start, + end, + markerLines + } = getMarkerLines(loc, lines, opts); + const hasColumns = loc.start && typeof loc.start.column === "number"; + const numberMaxWidth = String(end).length; + const highlightedLines = highlighted ? (0, _highlight.default)(rawLines, opts) : rawLines; + let frame = highlightedLines.split(NEWLINE).slice(start, end).map((line, index) => { + const number = start + 1 + index; + const paddedNumber = ` ${number}`.slice(-numberMaxWidth); + const gutter = ` ${paddedNumber} |`; + const hasMarker = markerLines[number]; + const lastMarkerLine = !markerLines[number + 1]; -function unescape(c) { - if ((c[0] === 'u' && c.length === 5) || (c[0] === 'x' && c.length === 3)) { - return String.fromCharCode(parseInt(c.slice(1), 16)); - } + if (hasMarker) { + let markerLine = ""; - return ESCAPES.get(c) || c; + if (Array.isArray(hasMarker)) { + const markerSpacing = line.slice(0, Math.max(hasMarker[0] - 1, 0)).replace(/[^\t]/g, " "); + const numberOfMarkers = hasMarker[1] || 1; + markerLine = ["\n ", maybeHighlight(defs.gutter, gutter.replace(/\d/g, " ")), " ", markerSpacing, maybeHighlight(defs.marker, "^").repeat(numberOfMarkers)].join(""); + + if (lastMarkerLine && opts.message) { + markerLine += " " + maybeHighlight(defs.message, opts.message); + } + } + + return [maybeHighlight(defs.marker, ">"), maybeHighlight(defs.gutter, gutter), line.length > 0 ? ` ${line}` : "", markerLine].join(""); + } else { + return ` ${maybeHighlight(defs.gutter, gutter)}${line.length > 0 ? ` ${line}` : ""}`; + } + }).join("\n"); + + if (opts.message && !hasColumns) { + frame = `${" ".repeat(numberMaxWidth + 1)}${opts.message}\n${frame}`; + } + + if (highlighted) { + return chalk.reset(frame); + } else { + return frame; + } } -function parseArguments(name, args) { - const results = []; - const chunks = args.trim().split(/\s*,\s*/g); - let matches; +function _default(rawLines, lineNumber, colNumber, opts = {}) { + if (!deprecationWarningShown) { + deprecationWarningShown = true; + const message = "Passing lineNumber and colNumber is deprecated to @babel/code-frame. Please use `codeFrameColumns`."; - for (const chunk of chunks) { - if (!isNaN(chunk)) { - results.push(Number(chunk)); - } else if ((matches = chunk.match(STRING_REGEX))) { - results.push(matches[2].replace(ESCAPE_REGEX, (m, escape, chr) => escape ? unescape(escape) : chr)); - } else { - throw new Error(`Invalid Chalk template style argument: ${chunk} (in style '${name}')`); - } - } + if (process.emitWarning) { + process.emitWarning(message, "DeprecationWarning"); + } else { + const deprecationError = new Error(message); + deprecationError.name = "DeprecationWarning"; + console.warn(new Error(message)); + } + } - return results; + colNumber = Math.max(colNumber, 0); + const location = { + start: { + column: colNumber, + line: lineNumber + } + }; + return codeFrameColumns(rawLines, location, opts); } -function parseStyle(style) { - STYLE_REGEX.lastIndex = 0; +/***/ }), +/* 357 */ +/***/ (function(module, exports, __webpack_require__) { - const results = []; - let matches; +"use strict"; - while ((matches = STYLE_REGEX.exec(style)) !== null) { - const name = matches[1]; - if (matches[2]) { - const args = parseArguments(name, matches[2]); - results.push([name].concat(args)); - } else { - results.push([name]); - } - } +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.shouldHighlight = shouldHighlight; +exports.getChalk = getChalk; +exports.default = highlight; - return results; -} +var jsTokensNs = _interopRequireWildcard(__webpack_require__(358)); -function buildStyle(chalk, styles) { - const enabled = {}; +var _helperValidatorIdentifier = __webpack_require__(359); - for (const layer of styles) { - for (const style of layer.styles) { - enabled[style[0]] = layer.inverse ? null : style.slice(1); - } - } +var _chalk = _interopRequireDefault(__webpack_require__(362)); - let current = chalk; - for (const styleName of Object.keys(enabled)) { - if (Array.isArray(enabled[styleName])) { - if (!(styleName in current)) { - throw new Error(`Unknown Chalk style: ${styleName}`); - } +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - if (enabled[styleName].length > 0) { - current = current[styleName].apply(current, enabled[styleName]); - } else { - current = current[styleName]; - } - } - } +function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; } - return current; -} +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } -module.exports = (chalk, tmp) => { - const styles = []; - const chunks = []; - let chunk = []; +const sometimesKeywords = new Set(["as", "async", "from", "get", "of", "set"]); - // eslint-disable-next-line max-params - tmp.replace(TEMPLATE_REGEX, (m, escapeChar, inverse, style, close, chr) => { - if (escapeChar) { - chunk.push(unescape(escapeChar)); - } else if (style) { - const str = chunk.join(''); - chunk = []; - chunks.push(styles.length === 0 ? str : buildStyle(chalk, styles)(str)); - styles.push({inverse, styles: parseStyle(style)}); - } else if (close) { - if (styles.length === 0) { - throw new Error('Found extraneous } in Chalk template literal'); - } +function getDefs(chalk) { + return { + keyword: chalk.cyan, + capitalized: chalk.yellow, + jsxIdentifier: chalk.yellow, + punctuator: chalk.yellow, + number: chalk.magenta, + string: chalk.green, + regex: chalk.magenta, + comment: chalk.grey, + invalid: chalk.white.bgRed.bold + }; +} - chunks.push(buildStyle(chalk, styles)(chunk.join(''))); - chunk = []; - styles.pop(); - } else { - chunk.push(chr); - } - }); +const NEWLINE = /\r\n|[\n\r\u2028\u2029]/; +const BRACKET = /^[()[\]{}]$/; +let tokenize; +{ + const { + matchToToken + } = jsTokensNs; + const JSX_TAG = /^[a-z][\w-]*$/i; - chunks.push(chunk.join('')); + const getTokenType = function (token, offset, text) { + if (token.type === "name") { + if ((0, _helperValidatorIdentifier.isKeyword)(token.value) || (0, _helperValidatorIdentifier.isStrictReservedWord)(token.value, true) || sometimesKeywords.has(token.value)) { + return "keyword"; + } - if (styles.length > 0) { - const errMsg = `Chalk template literal is missing ${styles.length} closing bracket${styles.length === 1 ? '' : 's'} (\`}\`)`; - throw new Error(errMsg); - } + if (JSX_TAG.test(token.value) && (text[offset - 1] === "<" || text.substr(offset - 2, 2) == " ucFirst(name) + "Field" -// thingsToFix = (ucFirst(name) + "Field" for name in fieldsToFix) -thingsToFix = thingsToFix.concat(otherThingsToFix) + for (const { + type, + value + } of tokenize(text)) { + const colorize = defs[type]; -function normalize (data, warn, strict) { - if(warn === true) warn = null, strict = true - if(!strict) strict = false - if(!warn || data.private) warn = function(msg) { /* noop */ } + if (colorize) { + highlighted += value.split(NEWLINE).map(str => colorize(str)).join("\n"); + } else { + highlighted += value; + } + } - if (data.scripts && - data.scripts.install === "node-gyp rebuild" && - !data.scripts.preinstall) { - data.gypfile = true + return highlighted; +} + +function shouldHighlight(options) { + return !!_chalk.default.supportsColor || options.forceColor; +} + +function getChalk(options) { + return options.forceColor ? new _chalk.default.constructor({ + enabled: true, + level: 1 + }) : _chalk.default; +} + +function highlight(code, options = {}) { + if (shouldHighlight(options)) { + const chalk = getChalk(options); + const defs = getDefs(chalk); + return highlightTokens(defs, code); + } else { + return code; } - fixer.warn = function() { warn(makeWarning.apply(null, arguments)) } - thingsToFix.forEach(function(thingName) { - fixer["fix" + ucFirst(thingName)](data, strict) - }) - data._id = data.name + "@" + data.version } -function ucFirst (string) { - return string.charAt(0).toUpperCase() + string.slice(1); +/***/ }), +/* 358 */ +/***/ (function(module, exports) { + +// Copyright 2014, 2015, 2016, 2017, 2018 Simon Lydell +// License: MIT. (See LICENSE.) + +Object.defineProperty(exports, "__esModule", { + value: true +}) + +// This regex comes from regex.coffee, and is inserted here by generate-index.js +// (run `npm run build`). +exports.default = /((['"])(?:(?!\2|\\).|\\(?:\r\n|[\s\S]))*(\2)?|`(?:[^`\\$]|\\[\s\S]|\$(?!\{)|\$\{(?:[^{}]|\{[^}]*\}?)*\}?)*(`)?)|(\/\/.*)|(\/\*(?:[^*]|\*(?!\/))*(\*\/)?)|(\/(?!\*)(?:\[(?:(?![\]\\]).|\\.)*\]|(?![\/\]\\]).|\\.)+\/(?:(?!\s*(?:\b|[\u0080-\uFFFF$\\'"~({]|[+\-!](?!=)|\.?\d))|[gmiyus]{1,6}\b(?![\u0080-\uFFFF$\\]|\s*(?:[+\-*%&|^<>!=?({]|\/(?![\/*])))))|(0[xX][\da-fA-F]+|0[oO][0-7]+|0[bB][01]+|(?:\d*\.\d+|\d+\.?)(?:[eE][+-]?\d+)?)|((?!\d)(?:(?!\s)[$\w\u0080-\uFFFF]|\\u[\da-fA-F]{4}|\\u\{[\da-fA-F]+\})+)|(--|\+\+|&&|\|\||=>|\.{3}|(?:[+\-\/%&|^]|\*{1,2}|<{1,2}|>{1,3}|!=?|={1,2})=?|[?~.,:;[\](){}])|(\s+)|(^$|[\s\S])/g + +exports.matchToToken = function(match) { + var token = {type: "invalid", value: match[0], closed: undefined} + if (match[ 1]) token.type = "string" , token.closed = !!(match[3] || match[4]) + else if (match[ 5]) token.type = "comment" + else if (match[ 6]) token.type = "comment", token.closed = !!match[7] + else if (match[ 8]) token.type = "regex" + else if (match[ 9]) token.type = "number" + else if (match[10]) token.type = "name" + else if (match[11]) token.type = "punctuator" + else if (match[12]) token.type = "whitespace" + return token } /***/ }), -/* 367 */ +/* 359 */ /***/ (function(module, exports, __webpack_require__) { -var semver = __webpack_require__(368) -var validateLicense = __webpack_require__(369); -var hostedGitInfo = __webpack_require__(374) -var isBuiltinModule = __webpack_require__(377).isCore -var depTypes = ["dependencies","devDependencies","optionalDependencies"] -var extractDescription = __webpack_require__(392) -var url = __webpack_require__(203) -var typos = __webpack_require__(393) - -var fixer = module.exports = { - // default warning function - warn: function() {}, +"use strict"; - fixRepositoryField: function(data) { - if (data.repositories) { - this.warn("repositories"); - data.repository = data.repositories[0] - } - if (!data.repository) return this.warn("missingRepository") - if (typeof data.repository === "string") { - data.repository = { - type: "git", - url: data.repository - } - } - var r = data.repository.url || "" - if (r) { - var hosted = hostedGitInfo.fromUrl(r) - if (hosted) { - r = data.repository.url - = hosted.getDefaultRepresentation() == "shortcut" ? hosted.https() : hosted.toString() - } - } - if (r.match(/github.com\/[^\/]+\/[^\/]+\.git\.git$/)) { - this.warn("brokenGitUrl", r) - } +Object.defineProperty(exports, "__esModule", { + value: true +}); +Object.defineProperty(exports, "isIdentifierName", { + enumerable: true, + get: function () { + return _identifier.isIdentifierName; } - -, fixTypos: function(data) { - Object.keys(typos.topLevel).forEach(function (d) { - if (data.hasOwnProperty(d)) { - this.warn("typo", d, typos.topLevel[d]) - } - }, this) +}); +Object.defineProperty(exports, "isIdentifierChar", { + enumerable: true, + get: function () { + return _identifier.isIdentifierChar; } - -, fixScriptsField: function(data) { - if (!data.scripts) return - if (typeof data.scripts !== "object") { - this.warn("nonObjectScripts") - delete data.scripts - return - } - Object.keys(data.scripts).forEach(function (k) { - if (typeof data.scripts[k] !== "string") { - this.warn("nonStringScript") - delete data.scripts[k] - } else if (typos.script[k] && !data.scripts[typos.script[k]]) { - this.warn("typo", k, typos.script[k], "scripts") - } - }, this) +}); +Object.defineProperty(exports, "isIdentifierStart", { + enumerable: true, + get: function () { + return _identifier.isIdentifierStart; } - -, fixFilesField: function(data) { - var files = data.files - if (files && !Array.isArray(files)) { - this.warn("nonArrayFiles") - delete data.files - } else if (data.files) { - data.files = data.files.filter(function(file) { - if (!file || typeof file !== "string") { - this.warn("invalidFilename", file) - return false - } else { - return true - } - }, this) - } +}); +Object.defineProperty(exports, "isReservedWord", { + enumerable: true, + get: function () { + return _keyword.isReservedWord; } - -, fixBinField: function(data) { - if (!data.bin) return; - if (typeof data.bin === "string") { - var b = {} - var match - if (match = data.name.match(/^@[^/]+[/](.*)$/)) { - b[match[1]] = data.bin - } else { - b[data.name] = data.bin - } - data.bin = b - } +}); +Object.defineProperty(exports, "isStrictBindOnlyReservedWord", { + enumerable: true, + get: function () { + return _keyword.isStrictBindOnlyReservedWord; } - -, fixManField: function(data) { - if (!data.man) return; - if (typeof data.man === "string") { - data.man = [ data.man ] - } +}); +Object.defineProperty(exports, "isStrictBindReservedWord", { + enumerable: true, + get: function () { + return _keyword.isStrictBindReservedWord; } -, fixBundleDependenciesField: function(data) { - var bdd = "bundledDependencies" - var bd = "bundleDependencies" - if (data[bdd] && !data[bd]) { - data[bd] = data[bdd] - delete data[bdd] - } - if (data[bd] && !Array.isArray(data[bd])) { - this.warn("nonArrayBundleDependencies") - delete data[bd] - } else if (data[bd]) { - data[bd] = data[bd].filter(function(bd) { - if (!bd || typeof bd !== 'string') { - this.warn("nonStringBundleDependency", bd) - return false - } else { - if (!data.dependencies) { - data.dependencies = {} - } - if (!data.dependencies.hasOwnProperty(bd)) { - this.warn("nonDependencyBundleDependency", bd) - data.dependencies[bd] = "*" - } - return true - } - }, this) - } +}); +Object.defineProperty(exports, "isStrictReservedWord", { + enumerable: true, + get: function () { + return _keyword.isStrictReservedWord; + } +}); +Object.defineProperty(exports, "isKeyword", { + enumerable: true, + get: function () { + return _keyword.isKeyword; } +}); -, fixDependencies: function(data, strict) { - var loose = !strict - objectifyDeps(data, this.warn) - addOptionalDepsToDeps(data, this.warn) - this.fixBundleDependenciesField(data) +var _identifier = __webpack_require__(360); - ;['dependencies','devDependencies'].forEach(function(deps) { - if (!(deps in data)) return - if (!data[deps] || typeof data[deps] !== "object") { - this.warn("nonObjectDependencies", deps) - delete data[deps] - return - } - Object.keys(data[deps]).forEach(function (d) { - var r = data[deps][d] - if (typeof r !== 'string') { - this.warn("nonStringDependency", d, JSON.stringify(r)) - delete data[deps][d] - } - var hosted = hostedGitInfo.fromUrl(data[deps][d]) - if (hosted) data[deps][d] = hosted.toString() - }, this) - }, this) - } +var _keyword = __webpack_require__(361); -, fixModulesField: function (data) { - if (data.modules) { - this.warn("deprecatedModules") - delete data.modules - } - } +/***/ }), +/* 360 */ +/***/ (function(module, exports, __webpack_require__) { -, fixKeywordsField: function (data) { - if (typeof data.keywords === "string") { - data.keywords = data.keywords.split(/,\s+/) - } - if (data.keywords && !Array.isArray(data.keywords)) { - delete data.keywords - this.warn("nonArrayKeywords") - } else if (data.keywords) { - data.keywords = data.keywords.filter(function(kw) { - if (typeof kw !== "string" || !kw) { - this.warn("nonStringKeyword"); - return false - } else { - return true - } - }, this) - } - } +"use strict"; -, fixVersionField: function(data, strict) { - // allow "loose" semver 1.0 versions in non-strict mode - // enforce strict semver 2.0 compliance in strict mode - var loose = !strict - if (!data.version) { - data.version = "" - return true - } - if (!semver.valid(data.version, loose)) { - throw new Error('Invalid version: "'+ data.version + '"') - } - data.version = semver.clean(data.version, loose) - return true - } -, fixPeople: function(data) { - modifyPeople(data, unParsePerson) - modifyPeople(data, parsePerson) - } +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.isIdentifierStart = isIdentifierStart; +exports.isIdentifierChar = isIdentifierChar; +exports.isIdentifierName = isIdentifierName; +let nonASCIIidentifierStartChars = "\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u037f\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u052f\u0531-\u0556\u0559\u0560-\u0588\u05d0-\u05ea\u05ef-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u0860-\u086a\u08a0-\u08b4\u08b6-\u08c7\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u09fc\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0af9\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d\u0c58-\u0c5a\u0c60\u0c61\u0c80\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d04-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d54-\u0d56\u0d5f-\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e86-\u0e8a\u0e8c-\u0ea3\u0ea5\u0ea7-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f5\u13f8-\u13fd\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f8\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1878\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191e\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1c80-\u1c88\u1c90-\u1cba\u1cbd-\u1cbf\u1ce9-\u1cec\u1cee-\u1cf3\u1cf5\u1cf6\u1cfa\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2118-\u211d\u2124\u2126\u2128\u212a-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309b-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312f\u3131-\u318e\u31a0-\u31bf\u31f0-\u31ff\u3400-\u4dbf\u4e00-\u9ffc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua69d\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua7bf\ua7c2-\ua7ca\ua7f5-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua8fd\ua8fe\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\ua9e0-\ua9e4\ua9e6-\ua9ef\ua9fa-\ua9fe\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa7e-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab5a\uab5c-\uab69\uab70-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc"; +let nonASCIIidentifierChars = "\u200c\u200d\xb7\u0300-\u036f\u0387\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u0669\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7\u06e8\u06ea-\u06ed\u06f0-\u06f9\u0711\u0730-\u074a\u07a6-\u07b0\u07c0-\u07c9\u07eb-\u07f3\u07fd\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u08d3-\u08e1\u08e3-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09cb-\u09cd\u09d7\u09e2\u09e3\u09e6-\u09ef\u09fe\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2\u0ae3\u0ae6-\u0aef\u0afa-\u0aff\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b55-\u0b57\u0b62\u0b63\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c00-\u0c04\u0c3e-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0c66-\u0c6f\u0c81-\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0ce6-\u0cef\u0d00-\u0d03\u0d3b\u0d3c\u0d3e-\u0d44\u0d46-\u0d48\u0d4a-\u0d4d\u0d57\u0d62\u0d63\u0d66-\u0d6f\u0d81-\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0de6-\u0def\u0df2\u0df3\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0e50-\u0e59\u0eb1\u0eb4-\u0ebc\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e\u0f3f\u0f71-\u0f84\u0f86\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102b-\u103e\u1040-\u1049\u1056-\u1059\u105e-\u1060\u1062-\u1064\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u1369-\u1371\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b4-\u17d3\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u18a9\u1920-\u192b\u1930-\u193b\u1946-\u194f\u19d0-\u19da\u1a17-\u1a1b\u1a55-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1ab0-\u1abd\u1abf\u1ac0\u1b00-\u1b04\u1b34-\u1b44\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1b82\u1ba1-\u1bad\u1bb0-\u1bb9\u1be6-\u1bf3\u1c24-\u1c37\u1c40-\u1c49\u1c50-\u1c59\u1cd0-\u1cd2\u1cd4-\u1ce8\u1ced\u1cf4\u1cf7-\u1cf9\u1dc0-\u1df9\u1dfb-\u1dff\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua620-\ua629\ua66f\ua674-\ua67d\ua69e\ua69f\ua6f0\ua6f1\ua802\ua806\ua80b\ua823-\ua827\ua82c\ua880\ua881\ua8b4-\ua8c5\ua8d0-\ua8d9\ua8e0-\ua8f1\ua8ff-\ua909\ua926-\ua92d\ua947-\ua953\ua980-\ua983\ua9b3-\ua9c0\ua9d0-\ua9d9\ua9e5\ua9f0-\ua9f9\uaa29-\uaa36\uaa43\uaa4c\uaa4d\uaa50-\uaa59\uaa7b-\uaa7d\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uaaeb-\uaaef\uaaf5\uaaf6\uabe3-\uabea\uabec\uabed\uabf0-\uabf9\ufb1e\ufe00-\ufe0f\ufe20-\ufe2f\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f"; +const nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]"); +const nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]"); +nonASCIIidentifierStartChars = nonASCIIidentifierChars = null; +const astralIdentifierStartCodes = [0, 11, 2, 25, 2, 18, 2, 1, 2, 14, 3, 13, 35, 122, 70, 52, 268, 28, 4, 48, 48, 31, 14, 29, 6, 37, 11, 29, 3, 35, 5, 7, 2, 4, 43, 157, 19, 35, 5, 35, 5, 39, 9, 51, 157, 310, 10, 21, 11, 7, 153, 5, 3, 0, 2, 43, 2, 1, 4, 0, 3, 22, 11, 22, 10, 30, 66, 18, 2, 1, 11, 21, 11, 25, 71, 55, 7, 1, 65, 0, 16, 3, 2, 2, 2, 28, 43, 28, 4, 28, 36, 7, 2, 27, 28, 53, 11, 21, 11, 18, 14, 17, 111, 72, 56, 50, 14, 50, 14, 35, 349, 41, 7, 1, 79, 28, 11, 0, 9, 21, 107, 20, 28, 22, 13, 52, 76, 44, 33, 24, 27, 35, 30, 0, 3, 0, 9, 34, 4, 0, 13, 47, 15, 3, 22, 0, 2, 0, 36, 17, 2, 24, 85, 6, 2, 0, 2, 3, 2, 14, 2, 9, 8, 46, 39, 7, 3, 1, 3, 21, 2, 6, 2, 1, 2, 4, 4, 0, 19, 0, 13, 4, 159, 52, 19, 3, 21, 2, 31, 47, 21, 1, 2, 0, 185, 46, 42, 3, 37, 47, 21, 0, 60, 42, 14, 0, 72, 26, 230, 43, 117, 63, 32, 7, 3, 0, 3, 7, 2, 1, 2, 23, 16, 0, 2, 0, 95, 7, 3, 38, 17, 0, 2, 0, 29, 0, 11, 39, 8, 0, 22, 0, 12, 45, 20, 0, 35, 56, 264, 8, 2, 36, 18, 0, 50, 29, 113, 6, 2, 1, 2, 37, 22, 0, 26, 5, 2, 1, 2, 31, 15, 0, 328, 18, 190, 0, 80, 921, 103, 110, 18, 195, 2749, 1070, 4050, 582, 8634, 568, 8, 30, 114, 29, 19, 47, 17, 3, 32, 20, 6, 18, 689, 63, 129, 74, 6, 0, 67, 12, 65, 1, 2, 0, 29, 6135, 9, 1237, 43, 8, 8952, 286, 50, 2, 18, 3, 9, 395, 2309, 106, 6, 12, 4, 8, 8, 9, 5991, 84, 2, 70, 2, 1, 3, 0, 3, 1, 3, 3, 2, 11, 2, 0, 2, 6, 2, 64, 2, 3, 3, 7, 2, 6, 2, 27, 2, 3, 2, 4, 2, 0, 4, 6, 2, 339, 3, 24, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 7, 2357, 44, 11, 6, 17, 0, 370, 43, 1301, 196, 60, 67, 8, 0, 1205, 3, 2, 26, 2, 1, 2, 0, 3, 0, 2, 9, 2, 3, 2, 0, 2, 0, 7, 0, 5, 0, 2, 0, 2, 0, 2, 2, 2, 1, 2, 0, 3, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 1, 2, 0, 3, 3, 2, 6, 2, 3, 2, 3, 2, 0, 2, 9, 2, 16, 6, 2, 2, 4, 2, 16, 4421, 42717, 35, 4148, 12, 221, 3, 5761, 15, 7472, 3104, 541, 1507, 4938]; +const astralIdentifierCodes = [509, 0, 227, 0, 150, 4, 294, 9, 1368, 2, 2, 1, 6, 3, 41, 2, 5, 0, 166, 1, 574, 3, 9, 9, 370, 1, 154, 10, 176, 2, 54, 14, 32, 9, 16, 3, 46, 10, 54, 9, 7, 2, 37, 13, 2, 9, 6, 1, 45, 0, 13, 2, 49, 13, 9, 3, 2, 11, 83, 11, 7, 0, 161, 11, 6, 9, 7, 3, 56, 1, 2, 6, 3, 1, 3, 2, 10, 0, 11, 1, 3, 6, 4, 4, 193, 17, 10, 9, 5, 0, 82, 19, 13, 9, 214, 6, 3, 8, 28, 1, 83, 16, 16, 9, 82, 12, 9, 9, 84, 14, 5, 9, 243, 14, 166, 9, 71, 5, 2, 1, 3, 3, 2, 0, 2, 1, 13, 9, 120, 6, 3, 6, 4, 0, 29, 9, 41, 6, 2, 3, 9, 0, 10, 10, 47, 15, 406, 7, 2, 7, 17, 9, 57, 21, 2, 13, 123, 5, 4, 0, 2, 1, 2, 6, 2, 0, 9, 9, 49, 4, 2, 1, 2, 4, 9, 9, 330, 3, 19306, 9, 135, 4, 60, 6, 26, 9, 1014, 0, 2, 54, 8, 3, 82, 0, 12, 1, 19628, 1, 5319, 4, 4, 5, 9, 7, 3, 6, 31, 3, 149, 2, 1418, 49, 513, 54, 5, 49, 9, 0, 15, 0, 23, 4, 2, 14, 1361, 6, 2, 16, 3, 6, 2, 1, 2, 4, 262, 6, 10, 9, 419, 13, 1495, 6, 110, 6, 6, 9, 4759, 9, 787719, 239]; -, fixNameField: function(data, options) { - if (typeof options === "boolean") options = {strict: options} - else if (typeof options === "undefined") options = {} - var strict = options.strict - if (!data.name && !strict) { - data.name = "" - return - } - if (typeof data.name !== "string") { - throw new Error("name field must be a string.") - } - if (!strict) - data.name = data.name.trim() - ensureValidName(data.name, strict, options.allowLegacyCase) - if (isBuiltinModule(data.name)) - this.warn("conflictingName", data.name) +function isInAstralSet(code, set) { + let pos = 0x10000; + + for (let i = 0, length = set.length; i < length; i += 2) { + pos += set[i]; + if (pos > code) return false; + pos += set[i + 1]; + if (pos >= code) return true; } + return false; +} + +function isIdentifierStart(code) { + if (code < 65) return code === 36; + if (code <= 90) return true; + if (code < 97) return code === 95; + if (code <= 122) return true; -, fixDescriptionField: function (data) { - if (data.description && typeof data.description !== 'string') { - this.warn("nonStringDescription") - delete data.description - } - if (data.readme && !data.description) - data.description = extractDescription(data.readme) - if(data.description === undefined) delete data.description; - if (!data.description) this.warn("missingDescription") + if (code <= 0xffff) { + return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code)); } -, fixReadmeField: function (data) { - if (!data.readme) { - this.warn("missingReadme") - data.readme = "ERROR: No README data found!" - } + return isInAstralSet(code, astralIdentifierStartCodes); +} + +function isIdentifierChar(code) { + if (code < 48) return code === 36; + if (code < 58) return true; + if (code < 65) return false; + if (code <= 90) return true; + if (code < 97) return code === 95; + if (code <= 122) return true; + + if (code <= 0xffff) { + return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code)); } -, fixBugsField: function(data) { - if (!data.bugs && data.repository && data.repository.url) { - var hosted = hostedGitInfo.fromUrl(data.repository.url) - if(hosted && hosted.bugs()) { - data.bugs = {url: hosted.bugs()} + return isInAstralSet(code, astralIdentifierStartCodes) || isInAstralSet(code, astralIdentifierCodes); +} + +function isIdentifierName(name) { + let isFirst = true; + + for (let i = 0; i < name.length; i++) { + let cp = name.charCodeAt(i); + + if ((cp & 0xfc00) === 0xd800 && i + 1 < name.length) { + const trail = name.charCodeAt(++i); + + if ((trail & 0xfc00) === 0xdc00) { + cp = 0x10000 + ((cp & 0x3ff) << 10) + (trail & 0x3ff); } } - else if(data.bugs) { - var emailRe = /^.+@.*\..+$/ - if(typeof data.bugs == "string") { - if(emailRe.test(data.bugs)) - data.bugs = {email:data.bugs} - else if(url.parse(data.bugs).protocol) - data.bugs = {url: data.bugs} - else - this.warn("nonEmailUrlBugsString") - } - else { - bugsTypos(data.bugs, this.warn) - var oldBugs = data.bugs - data.bugs = {} - if(oldBugs.url) { - if(typeof(oldBugs.url) == "string" && url.parse(oldBugs.url).protocol) - data.bugs.url = oldBugs.url - else - this.warn("nonUrlBugsUrlField") - } - if(oldBugs.email) { - if(typeof(oldBugs.email) == "string" && emailRe.test(oldBugs.email)) - data.bugs.email = oldBugs.email - else - this.warn("nonEmailBugsEmailField") - } - } - if(!data.bugs.email && !data.bugs.url) { - delete data.bugs - this.warn("emptyNormalizedBugs") + + if (isFirst) { + isFirst = false; + + if (!isIdentifierStart(cp)) { + return false; } + } else if (!isIdentifierChar(cp)) { + return false; } } -, fixHomepageField: function(data) { - if (!data.homepage && data.repository && data.repository.url) { - var hosted = hostedGitInfo.fromUrl(data.repository.url) - if (hosted && hosted.docs()) data.homepage = hosted.docs() - } - if (!data.homepage) return + return !isFirst; +} - if(typeof data.homepage !== "string") { - this.warn("nonUrlHomepage") - return delete data.homepage - } - if(!url.parse(data.homepage).protocol) { - data.homepage = "http://" + data.homepage - } - } +/***/ }), +/* 361 */ +/***/ (function(module, exports, __webpack_require__) { -, fixLicenseField: function(data) { - if (!data.license) { - return this.warn("missingLicense") - } else{ - if ( - typeof(data.license) !== 'string' || - data.license.length < 1 || - data.license.trim() === '' - ) { - this.warn("invalidLicense") - } else { - if (!validateLicense(data.license).validForNewPackages) - this.warn("invalidLicense") - } - } - } -} +"use strict"; -function isValidScopedPackageName(spec) { - if (spec.charAt(0) !== '@') return false - var rest = spec.slice(1).split('/') - if (rest.length !== 2) return false +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.isReservedWord = isReservedWord; +exports.isStrictReservedWord = isStrictReservedWord; +exports.isStrictBindOnlyReservedWord = isStrictBindOnlyReservedWord; +exports.isStrictBindReservedWord = isStrictBindReservedWord; +exports.isKeyword = isKeyword; +const reservedWords = { + keyword: ["break", "case", "catch", "continue", "debugger", "default", "do", "else", "finally", "for", "function", "if", "return", "switch", "throw", "try", "var", "const", "while", "with", "new", "this", "super", "class", "extends", "export", "import", "null", "true", "false", "in", "instanceof", "typeof", "void", "delete"], + strict: ["implements", "interface", "let", "package", "private", "protected", "public", "static", "yield"], + strictBind: ["eval", "arguments"] +}; +const keywords = new Set(reservedWords.keyword); +const reservedWordsStrictSet = new Set(reservedWords.strict); +const reservedWordsStrictBindSet = new Set(reservedWords.strictBind); - return rest[0] && rest[1] && - rest[0] === encodeURIComponent(rest[0]) && - rest[1] === encodeURIComponent(rest[1]) +function isReservedWord(word, inModule) { + return inModule && word === "await" || word === "enum"; } -function isCorrectlyEncodedName(spec) { - return !spec.match(/[\/@\s\+%:]/) && - spec === encodeURIComponent(spec) +function isStrictReservedWord(word, inModule) { + return isReservedWord(word, inModule) || reservedWordsStrictSet.has(word); } -function ensureValidName (name, strict, allowLegacyCase) { - if (name.charAt(0) === "." || - !(isValidScopedPackageName(name) || isCorrectlyEncodedName(name)) || - (strict && (!allowLegacyCase) && name !== name.toLowerCase()) || - name.toLowerCase() === "node_modules" || - name.toLowerCase() === "favicon.ico") { - throw new Error("Invalid name: " + JSON.stringify(name)) - } +function isStrictBindOnlyReservedWord(word) { + return reservedWordsStrictBindSet.has(word); } -function modifyPeople (data, fn) { - if (data.author) data.author = fn(data.author) - ;["maintainers", "contributors"].forEach(function (set) { - if (!Array.isArray(data[set])) return; - data[set] = data[set].map(fn) - }) - return data +function isStrictBindReservedWord(word, inModule) { + return isStrictReservedWord(word, inModule) || isStrictBindOnlyReservedWord(word); } -function unParsePerson (person) { - if (typeof person === "string") return person - var name = person.name || "" - var u = person.url || person.web - var url = u ? (" ("+u+")") : "" - var e = person.email || person.mail - var email = e ? (" <"+e+">") : "" - return name+email+url +function isKeyword(word) { + return keywords.has(word); } -function parsePerson (person) { - if (typeof person !== "string") return person - var name = person.match(/^([^\(<]+)/) - var url = person.match(/\(([^\)]+)\)/) - var email = person.match(/<([^>]+)>/) - var obj = {} - if (name && name[0].trim()) obj.name = name[0].trim() - if (email) obj.email = email[1]; - if (url) obj.url = url[1]; - return obj -} +/***/ }), +/* 362 */ +/***/ (function(module, exports, __webpack_require__) { -function addOptionalDepsToDeps (data, warn) { - var o = data.optionalDependencies - if (!o) return; - var d = data.dependencies || {} - Object.keys(o).forEach(function (k) { - d[k] = o[k] - }) - data.dependencies = d -} +"use strict"; -function depObjectify (deps, type, warn) { - if (!deps) return {} - if (typeof deps === "string") { - deps = deps.trim().split(/[\n\r\s\t ,]+/) - } - if (!Array.isArray(deps)) return deps - warn("deprecatedArrayDependencies", type) - var o = {} - deps.filter(function (d) { - return typeof d === "string" - }).forEach(function(d) { - d = d.trim().split(/(:?[@\s><=])/) - var dn = d.shift() - var dv = d.join("") - dv = dv.trim() - dv = dv.replace(/^@/, "") - o[dn] = dv - }) - return o -} +const escapeStringRegexp = __webpack_require__(363); +const ansiStyles = __webpack_require__(364); +const stdoutColor = __webpack_require__(369).stdout; -function objectifyDeps (data, warn) { - depTypes.forEach(function (type) { - if (!data[type]) return; - data[type] = depObjectify(data[type], type, warn) - }) -} +const template = __webpack_require__(371); -function bugsTypos(bugs, warn) { - if (!bugs) return - Object.keys(bugs).forEach(function (k) { - if (typos.bugs[k]) { - warn("typo", k, typos.bugs[k], "bugs") - bugs[typos.bugs[k]] = bugs[k] - delete bugs[k] - } - }) -} +const isSimpleWindowsTerm = process.platform === 'win32' && !(process.env.TERM || '').toLowerCase().startsWith('xterm'); +// `supportsColor.level` → `ansiStyles.color[name]` mapping +const levelMapping = ['ansi', 'ansi', 'ansi256', 'ansi16m']; -/***/ }), -/* 368 */ -/***/ (function(module, exports) { +// `color-convert` models to exclude from the Chalk API due to conflicts and such +const skipModels = new Set(['gray']); -exports = module.exports = SemVer +const styles = Object.create(null); -var debug -/* istanbul ignore next */ -if (typeof process === 'object' && - process.env && - process.env.NODE_DEBUG && - /\bsemver\b/i.test(process.env.NODE_DEBUG)) { - debug = function () { - var args = Array.prototype.slice.call(arguments, 0) - args.unshift('SEMVER') - console.log.apply(console, args) - } -} else { - debug = function () {} +function applyOptions(obj, options) { + options = options || {}; + + // Detect level if not set manually + const scLevel = stdoutColor ? stdoutColor.level : 0; + obj.level = options.level === undefined ? scLevel : options.level; + obj.enabled = 'enabled' in options ? options.enabled : obj.level > 0; } -// Note: this is the semver.org version of the spec that it implements -// Not necessarily the package version of this code. -exports.SEMVER_SPEC_VERSION = '2.0.0' +function Chalk(options) { + // We check for this.template here since calling `chalk.constructor()` + // by itself will have a `this` of a previously constructed chalk object + if (!this || !(this instanceof Chalk) || this.template) { + const chalk = {}; + applyOptions(chalk, options); -var MAX_LENGTH = 256 -var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || - /* istanbul ignore next */ 9007199254740991 + chalk.template = function () { + const args = [].slice.call(arguments); + return chalkTag.apply(null, [chalk.template].concat(args)); + }; -// Max safe segment length for coercion. -var MAX_SAFE_COMPONENT_LENGTH = 16 + Object.setPrototypeOf(chalk, Chalk.prototype); + Object.setPrototypeOf(chalk.template, chalk); -// The actual regexps go on exports.re -var re = exports.re = [] -var src = exports.src = [] -var R = 0 + chalk.template.constructor = Chalk; -// The following Regular Expressions can be used for tokenizing, -// validating, and parsing SemVer version strings. + return chalk.template; + } -// ## Numeric Identifier -// A single `0`, or a non-zero digit followed by zero or more digits. + applyOptions(this, options); +} -var NUMERICIDENTIFIER = R++ -src[NUMERICIDENTIFIER] = '0|[1-9]\\d*' -var NUMERICIDENTIFIERLOOSE = R++ -src[NUMERICIDENTIFIERLOOSE] = '[0-9]+' +// Use bright blue on Windows as the normal blue color is illegible +if (isSimpleWindowsTerm) { + ansiStyles.blue.open = '\u001B[94m'; +} -// ## Non-numeric Identifier -// Zero or more digits, followed by a letter or hyphen, and then zero or -// more letters, digits, or hyphens. +for (const key of Object.keys(ansiStyles)) { + ansiStyles[key].closeRe = new RegExp(escapeStringRegexp(ansiStyles[key].close), 'g'); -var NONNUMERICIDENTIFIER = R++ -src[NONNUMERICIDENTIFIER] = '\\d*[a-zA-Z-][a-zA-Z0-9-]*' + styles[key] = { + get() { + const codes = ansiStyles[key]; + return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, key); + } + }; +} -// ## Main Version -// Three dot-separated numeric identifiers. +styles.visible = { + get() { + return build.call(this, this._styles || [], true, 'visible'); + } +}; -var MAINVERSION = R++ -src[MAINVERSION] = '(' + src[NUMERICIDENTIFIER] + ')\\.' + - '(' + src[NUMERICIDENTIFIER] + ')\\.' + - '(' + src[NUMERICIDENTIFIER] + ')' +ansiStyles.color.closeRe = new RegExp(escapeStringRegexp(ansiStyles.color.close), 'g'); +for (const model of Object.keys(ansiStyles.color.ansi)) { + if (skipModels.has(model)) { + continue; + } -var MAINVERSIONLOOSE = R++ -src[MAINVERSIONLOOSE] = '(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' + - '(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' + - '(' + src[NUMERICIDENTIFIERLOOSE] + ')' + styles[model] = { + get() { + const level = this.level; + return function () { + const open = ansiStyles.color[levelMapping[level]][model].apply(null, arguments); + const codes = { + open, + close: ansiStyles.color.close, + closeRe: ansiStyles.color.closeRe + }; + return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, model); + }; + } + }; +} -// ## Pre-release Version Identifier -// A numeric identifier, or a non-numeric identifier. +ansiStyles.bgColor.closeRe = new RegExp(escapeStringRegexp(ansiStyles.bgColor.close), 'g'); +for (const model of Object.keys(ansiStyles.bgColor.ansi)) { + if (skipModels.has(model)) { + continue; + } -var PRERELEASEIDENTIFIER = R++ -src[PRERELEASEIDENTIFIER] = '(?:' + src[NUMERICIDENTIFIER] + - '|' + src[NONNUMERICIDENTIFIER] + ')' + const bgModel = 'bg' + model[0].toUpperCase() + model.slice(1); + styles[bgModel] = { + get() { + const level = this.level; + return function () { + const open = ansiStyles.bgColor[levelMapping[level]][model].apply(null, arguments); + const codes = { + open, + close: ansiStyles.bgColor.close, + closeRe: ansiStyles.bgColor.closeRe + }; + return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, model); + }; + } + }; +} -var PRERELEASEIDENTIFIERLOOSE = R++ -src[PRERELEASEIDENTIFIERLOOSE] = '(?:' + src[NUMERICIDENTIFIERLOOSE] + - '|' + src[NONNUMERICIDENTIFIER] + ')' +const proto = Object.defineProperties(() => {}, styles); -// ## Pre-release Version -// Hyphen, followed by one or more dot-separated pre-release version -// identifiers. +function build(_styles, _empty, key) { + const builder = function () { + return applyStyle.apply(builder, arguments); + }; -var PRERELEASE = R++ -src[PRERELEASE] = '(?:-(' + src[PRERELEASEIDENTIFIER] + - '(?:\\.' + src[PRERELEASEIDENTIFIER] + ')*))' + builder._styles = _styles; + builder._empty = _empty; -var PRERELEASELOOSE = R++ -src[PRERELEASELOOSE] = '(?:-?(' + src[PRERELEASEIDENTIFIERLOOSE] + - '(?:\\.' + src[PRERELEASEIDENTIFIERLOOSE] + ')*))' + const self = this; -// ## Build Metadata Identifier -// Any combination of digits, letters, or hyphens. + Object.defineProperty(builder, 'level', { + enumerable: true, + get() { + return self.level; + }, + set(level) { + self.level = level; + } + }); -var BUILDIDENTIFIER = R++ -src[BUILDIDENTIFIER] = '[0-9A-Za-z-]+' + Object.defineProperty(builder, 'enabled', { + enumerable: true, + get() { + return self.enabled; + }, + set(enabled) { + self.enabled = enabled; + } + }); -// ## Build Metadata -// Plus sign, followed by one or more period-separated build metadata -// identifiers. + // See below for fix regarding invisible grey/dim combination on Windows + builder.hasGrey = this.hasGrey || key === 'gray' || key === 'grey'; -var BUILD = R++ -src[BUILD] = '(?:\\+(' + src[BUILDIDENTIFIER] + - '(?:\\.' + src[BUILDIDENTIFIER] + ')*))' + // `__proto__` is used because we must return a function, but there is + // no way to create a function with a different prototype + builder.__proto__ = proto; // eslint-disable-line no-proto -// ## Full Version String -// A main version, followed optionally by a pre-release version and -// build metadata. + return builder; +} -// Note that the only major, minor, patch, and pre-release sections of -// the version string are capturing groups. The build metadata is not a -// capturing group, because it should not ever be used in version -// comparison. +function applyStyle() { + // Support varags, but simply cast to string in case there's only one arg + const args = arguments; + const argsLen = args.length; + let str = String(arguments[0]); -var FULL = R++ -var FULLPLAIN = 'v?' + src[MAINVERSION] + - src[PRERELEASE] + '?' + - src[BUILD] + '?' + if (argsLen === 0) { + return ''; + } -src[FULL] = '^' + FULLPLAIN + '$' + if (argsLen > 1) { + // Don't slice `arguments`, it prevents V8 optimizations + for (let a = 1; a < argsLen; a++) { + str += ' ' + args[a]; + } + } -// like full, but allows v1.2.3 and =1.2.3, which people do sometimes. -// also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty -// common in the npm registry. -var LOOSEPLAIN = '[v=\\s]*' + src[MAINVERSIONLOOSE] + - src[PRERELEASELOOSE] + '?' + - src[BUILD] + '?' + if (!this.enabled || this.level <= 0 || !str) { + return this._empty ? '' : str; + } -var LOOSE = R++ -src[LOOSE] = '^' + LOOSEPLAIN + '$' + // Turns out that on Windows dimmed gray text becomes invisible in cmd.exe, + // see https://github.com/chalk/chalk/issues/58 + // If we're on Windows and we're dealing with a gray color, temporarily make 'dim' a noop. + const originalDim = ansiStyles.dim.open; + if (isSimpleWindowsTerm && this.hasGrey) { + ansiStyles.dim.open = ''; + } -var GTLT = R++ -src[GTLT] = '((?:<|>)?=?)' + for (const code of this._styles.slice().reverse()) { + // Replace any instances already present with a re-opening code + // otherwise only the part of the string until said closing code + // will be colored, and the rest will simply be 'plain'. + str = code.open + str.replace(code.closeRe, code.open) + code.close; -// Something like "2.*" or "1.2.x". -// Note that "x.x" is a valid xRange identifer, meaning "any version" -// Only the first item is strictly required. -var XRANGEIDENTIFIERLOOSE = R++ -src[XRANGEIDENTIFIERLOOSE] = src[NUMERICIDENTIFIERLOOSE] + '|x|X|\\*' -var XRANGEIDENTIFIER = R++ -src[XRANGEIDENTIFIER] = src[NUMERICIDENTIFIER] + '|x|X|\\*' + // Close the styling before a linebreak and reopen + // after next line to fix a bleed issue on macOS + // https://github.com/chalk/chalk/pull/92 + str = str.replace(/\r?\n/g, `${code.close}$&${code.open}`); + } -var XRANGEPLAIN = R++ -src[XRANGEPLAIN] = '[v=\\s]*(' + src[XRANGEIDENTIFIER] + ')' + - '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' + - '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' + - '(?:' + src[PRERELEASE] + ')?' + - src[BUILD] + '?' + - ')?)?' + // Reset the original `dim` if we changed it to work around the Windows dimmed gray issue + ansiStyles.dim.open = originalDim; -var XRANGEPLAINLOOSE = R++ -src[XRANGEPLAINLOOSE] = '[v=\\s]*(' + src[XRANGEIDENTIFIERLOOSE] + ')' + - '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' + - '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' + - '(?:' + src[PRERELEASELOOSE] + ')?' + - src[BUILD] + '?' + - ')?)?' + return str; +} -var XRANGE = R++ -src[XRANGE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAIN] + '$' -var XRANGELOOSE = R++ -src[XRANGELOOSE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAINLOOSE] + '$' +function chalkTag(chalk, strings) { + if (!Array.isArray(strings)) { + // If chalk() was called by itself or with a string, + // return the string itself as a string. + return [].slice.call(arguments, 1).join(' '); + } -// Coercion. -// Extract anything that could conceivably be a part of a valid semver -var COERCE = R++ -src[COERCE] = '(?:^|[^\\d])' + - '(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '})' + - '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + - '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + - '(?:$|[^\\d])' + const args = [].slice.call(arguments, 2); + const parts = [strings.raw[0]]; -// Tilde ranges. -// Meaning is "reasonably at or greater than" -var LONETILDE = R++ -src[LONETILDE] = '(?:~>?)' + for (let i = 1; i < strings.length; i++) { + parts.push(String(args[i - 1]).replace(/[{}\\]/g, '\\$&')); + parts.push(String(strings.raw[i])); + } -var TILDETRIM = R++ -src[TILDETRIM] = '(\\s*)' + src[LONETILDE] + '\\s+' -re[TILDETRIM] = new RegExp(src[TILDETRIM], 'g') -var tildeTrimReplace = '$1~' + return template(chalk, parts.join('')); +} -var TILDE = R++ -src[TILDE] = '^' + src[LONETILDE] + src[XRANGEPLAIN] + '$' -var TILDELOOSE = R++ -src[TILDELOOSE] = '^' + src[LONETILDE] + src[XRANGEPLAINLOOSE] + '$' +Object.defineProperties(Chalk.prototype, styles); -// Caret ranges. -// Meaning is "at least and backwards compatible with" -var LONECARET = R++ -src[LONECARET] = '(?:\\^)' +module.exports = Chalk(); // eslint-disable-line new-cap +module.exports.supportsColor = stdoutColor; +module.exports.default = module.exports; // For TypeScript -var CARETTRIM = R++ -src[CARETTRIM] = '(\\s*)' + src[LONECARET] + '\\s+' -re[CARETTRIM] = new RegExp(src[CARETTRIM], 'g') -var caretTrimReplace = '$1^' -var CARET = R++ -src[CARET] = '^' + src[LONECARET] + src[XRANGEPLAIN] + '$' -var CARETLOOSE = R++ -src[CARETLOOSE] = '^' + src[LONECARET] + src[XRANGEPLAINLOOSE] + '$' +/***/ }), +/* 363 */ +/***/ (function(module, exports, __webpack_require__) { -// A simple gt/lt/eq thing, or just "" to indicate "any version" -var COMPARATORLOOSE = R++ -src[COMPARATORLOOSE] = '^' + src[GTLT] + '\\s*(' + LOOSEPLAIN + ')$|^$' -var COMPARATOR = R++ -src[COMPARATOR] = '^' + src[GTLT] + '\\s*(' + FULLPLAIN + ')$|^$' +"use strict"; -// An expression to strip any whitespace between the gtlt and the thing -// it modifies, so that `> 1.2.3` ==> `>1.2.3` -var COMPARATORTRIM = R++ -src[COMPARATORTRIM] = '(\\s*)' + src[GTLT] + - '\\s*(' + LOOSEPLAIN + '|' + src[XRANGEPLAIN] + ')' -// this one has to use the /g flag -re[COMPARATORTRIM] = new RegExp(src[COMPARATORTRIM], 'g') -var comparatorTrimReplace = '$1$2$3' +var matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g; -// Something like `1.2.3 - 1.2.4` -// Note that these all use the loose form, because they'll be -// checked against either the strict or loose comparator form -// later. -var HYPHENRANGE = R++ -src[HYPHENRANGE] = '^\\s*(' + src[XRANGEPLAIN] + ')' + - '\\s+-\\s+' + - '(' + src[XRANGEPLAIN] + ')' + - '\\s*$' +module.exports = function (str) { + if (typeof str !== 'string') { + throw new TypeError('Expected a string'); + } -var HYPHENRANGELOOSE = R++ -src[HYPHENRANGELOOSE] = '^\\s*(' + src[XRANGEPLAINLOOSE] + ')' + - '\\s+-\\s+' + - '(' + src[XRANGEPLAINLOOSE] + ')' + - '\\s*$' + return str.replace(matchOperatorsRe, '\\$&'); +}; -// Star ranges basically just allow anything at all. -var STAR = R++ -src[STAR] = '(<|>)?=?\\s*\\*' -// Compile to actual regexp objects. -// All are flag-free, unless they were created above with a flag. -for (var i = 0; i < R; i++) { - debug(i, src[i]) - if (!re[i]) { - re[i] = new RegExp(src[i]) - } -} +/***/ }), +/* 364 */ +/***/ (function(module, exports, __webpack_require__) { -exports.parse = parse -function parse (version, options) { - if (!options || typeof options !== 'object') { - options = { - loose: !!options, - includePrerelease: false - } - } +"use strict"; +/* WEBPACK VAR INJECTION */(function(module) { +const colorConvert = __webpack_require__(365); - if (version instanceof SemVer) { - return version - } +const wrapAnsi16 = (fn, offset) => function () { + const code = fn.apply(colorConvert, arguments); + return `\u001B[${code + offset}m`; +}; - if (typeof version !== 'string') { - return null - } +const wrapAnsi256 = (fn, offset) => function () { + const code = fn.apply(colorConvert, arguments); + return `\u001B[${38 + offset};5;${code}m`; +}; - if (version.length > MAX_LENGTH) { - return null - } +const wrapAnsi16m = (fn, offset) => function () { + const rgb = fn.apply(colorConvert, arguments); + return `\u001B[${38 + offset};2;${rgb[0]};${rgb[1]};${rgb[2]}m`; +}; - var r = options.loose ? re[LOOSE] : re[FULL] - if (!r.test(version)) { - return null - } +function assembleStyles() { + const codes = new Map(); + const styles = { + modifier: { + reset: [0, 0], + // 21 isn't widely supported and 22 does the same thing + bold: [1, 22], + dim: [2, 22], + italic: [3, 23], + underline: [4, 24], + inverse: [7, 27], + hidden: [8, 28], + strikethrough: [9, 29] + }, + color: { + black: [30, 39], + red: [31, 39], + green: [32, 39], + yellow: [33, 39], + blue: [34, 39], + magenta: [35, 39], + cyan: [36, 39], + white: [37, 39], + gray: [90, 39], - try { - return new SemVer(version, options) - } catch (er) { - return null - } -} + // Bright color + redBright: [91, 39], + greenBright: [92, 39], + yellowBright: [93, 39], + blueBright: [94, 39], + magentaBright: [95, 39], + cyanBright: [96, 39], + whiteBright: [97, 39] + }, + bgColor: { + bgBlack: [40, 49], + bgRed: [41, 49], + bgGreen: [42, 49], + bgYellow: [43, 49], + bgBlue: [44, 49], + bgMagenta: [45, 49], + bgCyan: [46, 49], + bgWhite: [47, 49], -exports.valid = valid -function valid (version, options) { - var v = parse(version, options) - return v ? v.version : null -} + // Bright color + bgBlackBright: [100, 49], + bgRedBright: [101, 49], + bgGreenBright: [102, 49], + bgYellowBright: [103, 49], + bgBlueBright: [104, 49], + bgMagentaBright: [105, 49], + bgCyanBright: [106, 49], + bgWhiteBright: [107, 49] + } + }; -exports.clean = clean -function clean (version, options) { - var s = parse(version.trim().replace(/^[=v]+/, ''), options) - return s ? s.version : null -} + // Fix humans + styles.color.grey = styles.color.gray; -exports.SemVer = SemVer + for (const groupName of Object.keys(styles)) { + const group = styles[groupName]; -function SemVer (version, options) { - if (!options || typeof options !== 'object') { - options = { - loose: !!options, - includePrerelease: false - } - } - if (version instanceof SemVer) { - if (version.loose === options.loose) { - return version - } else { - version = version.version - } - } else if (typeof version !== 'string') { - throw new TypeError('Invalid Version: ' + version) - } + for (const styleName of Object.keys(group)) { + const style = group[styleName]; - if (version.length > MAX_LENGTH) { - throw new TypeError('version is longer than ' + MAX_LENGTH + ' characters') - } + styles[styleName] = { + open: `\u001B[${style[0]}m`, + close: `\u001B[${style[1]}m` + }; - if (!(this instanceof SemVer)) { - return new SemVer(version, options) - } + group[styleName] = styles[styleName]; - debug('SemVer', version, options) - this.options = options - this.loose = !!options.loose + codes.set(style[0], style[1]); + } - var m = version.trim().match(options.loose ? re[LOOSE] : re[FULL]) + Object.defineProperty(styles, groupName, { + value: group, + enumerable: false + }); - if (!m) { - throw new TypeError('Invalid Version: ' + version) - } + Object.defineProperty(styles, 'codes', { + value: codes, + enumerable: false + }); + } - this.raw = version + const ansi2ansi = n => n; + const rgb2rgb = (r, g, b) => [r, g, b]; - // these are actually numbers - this.major = +m[1] - this.minor = +m[2] - this.patch = +m[3] + styles.color.close = '\u001B[39m'; + styles.bgColor.close = '\u001B[49m'; - if (this.major > MAX_SAFE_INTEGER || this.major < 0) { - throw new TypeError('Invalid major version') - } + styles.color.ansi = { + ansi: wrapAnsi16(ansi2ansi, 0) + }; + styles.color.ansi256 = { + ansi256: wrapAnsi256(ansi2ansi, 0) + }; + styles.color.ansi16m = { + rgb: wrapAnsi16m(rgb2rgb, 0) + }; - if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) { - throw new TypeError('Invalid minor version') - } + styles.bgColor.ansi = { + ansi: wrapAnsi16(ansi2ansi, 10) + }; + styles.bgColor.ansi256 = { + ansi256: wrapAnsi256(ansi2ansi, 10) + }; + styles.bgColor.ansi16m = { + rgb: wrapAnsi16m(rgb2rgb, 10) + }; - if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) { - throw new TypeError('Invalid patch version') - } + for (let key of Object.keys(colorConvert)) { + if (typeof colorConvert[key] !== 'object') { + continue; + } - // numberify any prerelease numeric ids - if (!m[4]) { - this.prerelease = [] - } else { - this.prerelease = m[4].split('.').map(function (id) { - if (/^[0-9]+$/.test(id)) { - var num = +id - if (num >= 0 && num < MAX_SAFE_INTEGER) { - return num - } - } - return id - }) - } + const suite = colorConvert[key]; - this.build = m[5] ? m[5].split('.') : [] - this.format() -} + if (key === 'ansi16') { + key = 'ansi'; + } -SemVer.prototype.format = function () { - this.version = this.major + '.' + this.minor + '.' + this.patch - if (this.prerelease.length) { - this.version += '-' + this.prerelease.join('.') - } - return this.version -} + if ('ansi16' in suite) { + styles.color.ansi[key] = wrapAnsi16(suite.ansi16, 0); + styles.bgColor.ansi[key] = wrapAnsi16(suite.ansi16, 10); + } -SemVer.prototype.toString = function () { - return this.version -} + if ('ansi256' in suite) { + styles.color.ansi256[key] = wrapAnsi256(suite.ansi256, 0); + styles.bgColor.ansi256[key] = wrapAnsi256(suite.ansi256, 10); + } -SemVer.prototype.compare = function (other) { - debug('SemVer.compare', this.version, this.options, other) - if (!(other instanceof SemVer)) { - other = new SemVer(other, this.options) - } + if ('rgb' in suite) { + styles.color.ansi16m[key] = wrapAnsi16m(suite.rgb, 0); + styles.bgColor.ansi16m[key] = wrapAnsi16m(suite.rgb, 10); + } + } - return this.compareMain(other) || this.comparePre(other) + return styles; } -SemVer.prototype.compareMain = function (other) { - if (!(other instanceof SemVer)) { - other = new SemVer(other, this.options) - } +// Make the export immutable +Object.defineProperty(module, 'exports', { + enumerable: true, + get: assembleStyles +}); - return compareIdentifiers(this.major, other.major) || - compareIdentifiers(this.minor, other.minor) || - compareIdentifiers(this.patch, other.patch) -} +/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(116)(module))) -SemVer.prototype.comparePre = function (other) { - if (!(other instanceof SemVer)) { - other = new SemVer(other, this.options) - } +/***/ }), +/* 365 */ +/***/ (function(module, exports, __webpack_require__) { - // NOT having a prerelease is > having one - if (this.prerelease.length && !other.prerelease.length) { - return -1 - } else if (!this.prerelease.length && other.prerelease.length) { - return 1 - } else if (!this.prerelease.length && !other.prerelease.length) { - return 0 - } +var conversions = __webpack_require__(366); +var route = __webpack_require__(368); - var i = 0 - do { - var a = this.prerelease[i] - var b = other.prerelease[i] - debug('prerelease compare', i, a, b) - if (a === undefined && b === undefined) { - return 0 - } else if (b === undefined) { - return 1 - } else if (a === undefined) { - return -1 - } else if (a === b) { - continue - } else { - return compareIdentifiers(a, b) - } - } while (++i) -} +var convert = {}; -// preminor will bump the version up to the next minor release, and immediately -// down to pre-release. premajor and prepatch work the same way. -SemVer.prototype.inc = function (release, identifier) { - switch (release) { - case 'premajor': - this.prerelease.length = 0 - this.patch = 0 - this.minor = 0 - this.major++ - this.inc('pre', identifier) - break - case 'preminor': - this.prerelease.length = 0 - this.patch = 0 - this.minor++ - this.inc('pre', identifier) - break - case 'prepatch': - // If this is already a prerelease, it will bump to the next version - // drop any prereleases that might already exist, since they are not - // relevant at this point. - this.prerelease.length = 0 - this.inc('patch', identifier) - this.inc('pre', identifier) - break - // If the input is a non-prerelease version, this acts the same as - // prepatch. - case 'prerelease': - if (this.prerelease.length === 0) { - this.inc('patch', identifier) - } - this.inc('pre', identifier) - break +var models = Object.keys(conversions); - case 'major': - // If this is a pre-major version, bump up to the same major version. - // Otherwise increment major. - // 1.0.0-5 bumps to 1.0.0 - // 1.1.0 bumps to 2.0.0 - if (this.minor !== 0 || - this.patch !== 0 || - this.prerelease.length === 0) { - this.major++ - } - this.minor = 0 - this.patch = 0 - this.prerelease = [] - break - case 'minor': - // If this is a pre-minor version, bump up to the same minor version. - // Otherwise increment minor. - // 1.2.0-5 bumps to 1.2.0 - // 1.2.1 bumps to 1.3.0 - if (this.patch !== 0 || this.prerelease.length === 0) { - this.minor++ - } - this.patch = 0 - this.prerelease = [] - break - case 'patch': - // If this is not a pre-release version, it will increment the patch. - // If it is a pre-release it will bump up to the same patch version. - // 1.2.0-5 patches to 1.2.0 - // 1.2.0 patches to 1.2.1 - if (this.prerelease.length === 0) { - this.patch++ - } - this.prerelease = [] - break - // This probably shouldn't be used publicly. - // 1.0.0 "pre" would become 1.0.0-0 which is the wrong direction. - case 'pre': - if (this.prerelease.length === 0) { - this.prerelease = [0] - } else { - var i = this.prerelease.length - while (--i >= 0) { - if (typeof this.prerelease[i] === 'number') { - this.prerelease[i]++ - i = -2 - } - } - if (i === -1) { - // didn't increment anything - this.prerelease.push(0) - } - } - if (identifier) { - // 1.2.0-beta.1 bumps to 1.2.0-beta.2, - // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0 - if (this.prerelease[0] === identifier) { - if (isNaN(this.prerelease[1])) { - this.prerelease = [identifier, 0] - } - } else { - this.prerelease = [identifier, 0] - } - } - break +function wrapRaw(fn) { + var wrappedFn = function (args) { + if (args === undefined || args === null) { + return args; + } - default: - throw new Error('invalid increment argument: ' + release) - } - this.format() - this.raw = this.version - return this -} + if (arguments.length > 1) { + args = Array.prototype.slice.call(arguments); + } -exports.inc = inc -function inc (version, release, loose, identifier) { - if (typeof (loose) === 'string') { - identifier = loose - loose = undefined - } + return fn(args); + }; - try { - return new SemVer(version, loose).inc(release, identifier).version - } catch (er) { - return null - } -} + // preserve .conversion property if there is one + if ('conversion' in fn) { + wrappedFn.conversion = fn.conversion; + } -exports.diff = diff -function diff (version1, version2) { - if (eq(version1, version2)) { - return null - } else { - var v1 = parse(version1) - var v2 = parse(version2) - var prefix = '' - if (v1.prerelease.length || v2.prerelease.length) { - prefix = 'pre' - var defaultResult = 'prerelease' - } - for (var key in v1) { - if (key === 'major' || key === 'minor' || key === 'patch') { - if (v1[key] !== v2[key]) { - return prefix + key - } - } - } - return defaultResult // may be undefined - } + return wrappedFn; } -exports.compareIdentifiers = compareIdentifiers +function wrapRounded(fn) { + var wrappedFn = function (args) { + if (args === undefined || args === null) { + return args; + } -var numeric = /^[0-9]+$/ -function compareIdentifiers (a, b) { - var anum = numeric.test(a) - var bnum = numeric.test(b) + if (arguments.length > 1) { + args = Array.prototype.slice.call(arguments); + } - if (anum && bnum) { - a = +a - b = +b - } + var result = fn(args); - return a === b ? 0 - : (anum && !bnum) ? -1 - : (bnum && !anum) ? 1 - : a < b ? -1 - : 1 -} - -exports.rcompareIdentifiers = rcompareIdentifiers -function rcompareIdentifiers (a, b) { - return compareIdentifiers(b, a) -} + // we're assuming the result is an array here. + // see notice in conversions.js; don't use box types + // in conversion functions. + if (typeof result === 'object') { + for (var len = result.length, i = 0; i < len; i++) { + result[i] = Math.round(result[i]); + } + } -exports.major = major -function major (a, loose) { - return new SemVer(a, loose).major -} + return result; + }; -exports.minor = minor -function minor (a, loose) { - return new SemVer(a, loose).minor -} + // preserve .conversion property if there is one + if ('conversion' in fn) { + wrappedFn.conversion = fn.conversion; + } -exports.patch = patch -function patch (a, loose) { - return new SemVer(a, loose).patch + return wrappedFn; } -exports.compare = compare -function compare (a, b, loose) { - return new SemVer(a, loose).compare(new SemVer(b, loose)) -} +models.forEach(function (fromModel) { + convert[fromModel] = {}; -exports.compareLoose = compareLoose -function compareLoose (a, b) { - return compare(a, b, true) -} + Object.defineProperty(convert[fromModel], 'channels', {value: conversions[fromModel].channels}); + Object.defineProperty(convert[fromModel], 'labels', {value: conversions[fromModel].labels}); -exports.rcompare = rcompare -function rcompare (a, b, loose) { - return compare(b, a, loose) -} + var routes = route(fromModel); + var routeModels = Object.keys(routes); -exports.sort = sort -function sort (list, loose) { - return list.sort(function (a, b) { - return exports.compare(a, b, loose) - }) -} + routeModels.forEach(function (toModel) { + var fn = routes[toModel]; -exports.rsort = rsort -function rsort (list, loose) { - return list.sort(function (a, b) { - return exports.rcompare(a, b, loose) - }) -} + convert[fromModel][toModel] = wrapRounded(fn); + convert[fromModel][toModel].raw = wrapRaw(fn); + }); +}); -exports.gt = gt -function gt (a, b, loose) { - return compare(a, b, loose) > 0 -} +module.exports = convert; -exports.lt = lt -function lt (a, b, loose) { - return compare(a, b, loose) < 0 -} -exports.eq = eq -function eq (a, b, loose) { - return compare(a, b, loose) === 0 -} +/***/ }), +/* 366 */ +/***/ (function(module, exports, __webpack_require__) { -exports.neq = neq -function neq (a, b, loose) { - return compare(a, b, loose) !== 0 -} +/* MIT license */ +var cssKeywords = __webpack_require__(367); -exports.gte = gte -function gte (a, b, loose) { - return compare(a, b, loose) >= 0 -} +// NOTE: conversions should only return primitive values (i.e. arrays, or +// values that give correct `typeof` results). +// do not use box values types (i.e. Number(), String(), etc.) -exports.lte = lte -function lte (a, b, loose) { - return compare(a, b, loose) <= 0 +var reverseKeywords = {}; +for (var key in cssKeywords) { + if (cssKeywords.hasOwnProperty(key)) { + reverseKeywords[cssKeywords[key]] = key; + } } -exports.cmp = cmp -function cmp (a, op, b, loose) { - switch (op) { - case '===': - if (typeof a === 'object') - a = a.version - if (typeof b === 'object') - b = b.version - return a === b +var convert = module.exports = { + rgb: {channels: 3, labels: 'rgb'}, + hsl: {channels: 3, labels: 'hsl'}, + hsv: {channels: 3, labels: 'hsv'}, + hwb: {channels: 3, labels: 'hwb'}, + cmyk: {channels: 4, labels: 'cmyk'}, + xyz: {channels: 3, labels: 'xyz'}, + lab: {channels: 3, labels: 'lab'}, + lch: {channels: 3, labels: 'lch'}, + hex: {channels: 1, labels: ['hex']}, + keyword: {channels: 1, labels: ['keyword']}, + ansi16: {channels: 1, labels: ['ansi16']}, + ansi256: {channels: 1, labels: ['ansi256']}, + hcg: {channels: 3, labels: ['h', 'c', 'g']}, + apple: {channels: 3, labels: ['r16', 'g16', 'b16']}, + gray: {channels: 1, labels: ['gray']} +}; - case '!==': - if (typeof a === 'object') - a = a.version - if (typeof b === 'object') - b = b.version - return a !== b +// hide .channels and .labels properties +for (var model in convert) { + if (convert.hasOwnProperty(model)) { + if (!('channels' in convert[model])) { + throw new Error('missing channels property: ' + model); + } - case '': - case '=': - case '==': - return eq(a, b, loose) + if (!('labels' in convert[model])) { + throw new Error('missing channel labels property: ' + model); + } - case '!=': - return neq(a, b, loose) + if (convert[model].labels.length !== convert[model].channels) { + throw new Error('channel and label counts mismatch: ' + model); + } - case '>': - return gt(a, b, loose) + var channels = convert[model].channels; + var labels = convert[model].labels; + delete convert[model].channels; + delete convert[model].labels; + Object.defineProperty(convert[model], 'channels', {value: channels}); + Object.defineProperty(convert[model], 'labels', {value: labels}); + } +} - case '>=': - return gte(a, b, loose) +convert.rgb.hsl = function (rgb) { + var r = rgb[0] / 255; + var g = rgb[1] / 255; + var b = rgb[2] / 255; + var min = Math.min(r, g, b); + var max = Math.max(r, g, b); + var delta = max - min; + var h; + var s; + var l; - case '<': - return lt(a, b, loose) + if (max === min) { + h = 0; + } else if (r === max) { + h = (g - b) / delta; + } else if (g === max) { + h = 2 + (b - r) / delta; + } else if (b === max) { + h = 4 + (r - g) / delta; + } - case '<=': - return lte(a, b, loose) + h = Math.min(h * 60, 360); - default: - throw new TypeError('Invalid operator: ' + op) - } -} + if (h < 0) { + h += 360; + } -exports.Comparator = Comparator -function Comparator (comp, options) { - if (!options || typeof options !== 'object') { - options = { - loose: !!options, - includePrerelease: false - } - } + l = (min + max) / 2; - if (comp instanceof Comparator) { - if (comp.loose === !!options.loose) { - return comp - } else { - comp = comp.value - } - } + if (max === min) { + s = 0; + } else if (l <= 0.5) { + s = delta / (max + min); + } else { + s = delta / (2 - max - min); + } - if (!(this instanceof Comparator)) { - return new Comparator(comp, options) - } + return [h, s * 100, l * 100]; +}; - debug('comparator', comp, options) - this.options = options - this.loose = !!options.loose - this.parse(comp) +convert.rgb.hsv = function (rgb) { + var rdif; + var gdif; + var bdif; + var h; + var s; - if (this.semver === ANY) { - this.value = '' - } else { - this.value = this.operator + this.semver.version - } + var r = rgb[0] / 255; + var g = rgb[1] / 255; + var b = rgb[2] / 255; + var v = Math.max(r, g, b); + var diff = v - Math.min(r, g, b); + var diffc = function (c) { + return (v - c) / 6 / diff + 1 / 2; + }; - debug('comp', this) -} + if (diff === 0) { + h = s = 0; + } else { + s = diff / v; + rdif = diffc(r); + gdif = diffc(g); + bdif = diffc(b); -var ANY = {} -Comparator.prototype.parse = function (comp) { - var r = this.options.loose ? re[COMPARATORLOOSE] : re[COMPARATOR] - var m = comp.match(r) + if (r === v) { + h = bdif - gdif; + } else if (g === v) { + h = (1 / 3) + rdif - bdif; + } else if (b === v) { + h = (2 / 3) + gdif - rdif; + } + if (h < 0) { + h += 1; + } else if (h > 1) { + h -= 1; + } + } - if (!m) { - throw new TypeError('Invalid comparator: ' + comp) - } + return [ + h * 360, + s * 100, + v * 100 + ]; +}; - this.operator = m[1] - if (this.operator === '=') { - this.operator = '' - } +convert.rgb.hwb = function (rgb) { + var r = rgb[0]; + var g = rgb[1]; + var b = rgb[2]; + var h = convert.rgb.hsl(rgb)[0]; + var w = 1 / 255 * Math.min(r, Math.min(g, b)); - // if it literally is just '>' or '' then allow anything. - if (!m[2]) { - this.semver = ANY - } else { - this.semver = new SemVer(m[2], this.options.loose) - } -} + b = 1 - 1 / 255 * Math.max(r, Math.max(g, b)); -Comparator.prototype.toString = function () { - return this.value -} + return [h, w * 100, b * 100]; +}; -Comparator.prototype.test = function (version) { - debug('Comparator.test', version, this.options.loose) +convert.rgb.cmyk = function (rgb) { + var r = rgb[0] / 255; + var g = rgb[1] / 255; + var b = rgb[2] / 255; + var c; + var m; + var y; + var k; - if (this.semver === ANY) { - return true - } + k = Math.min(1 - r, 1 - g, 1 - b); + c = (1 - r - k) / (1 - k) || 0; + m = (1 - g - k) / (1 - k) || 0; + y = (1 - b - k) / (1 - k) || 0; - if (typeof version === 'string') { - version = new SemVer(version, this.options) - } + return [c * 100, m * 100, y * 100, k * 100]; +}; - return cmp(version, this.operator, this.semver, this.options) +/** + * See https://en.m.wikipedia.org/wiki/Euclidean_distance#Squared_Euclidean_distance + * */ +function comparativeDistance(x, y) { + return ( + Math.pow(x[0] - y[0], 2) + + Math.pow(x[1] - y[1], 2) + + Math.pow(x[2] - y[2], 2) + ); } -Comparator.prototype.intersects = function (comp, options) { - if (!(comp instanceof Comparator)) { - throw new TypeError('a Comparator is required') - } +convert.rgb.keyword = function (rgb) { + var reversed = reverseKeywords[rgb]; + if (reversed) { + return reversed; + } - if (!options || typeof options !== 'object') { - options = { - loose: !!options, - includePrerelease: false - } - } + var currentClosestDistance = Infinity; + var currentClosestKeyword; - var rangeTmp + for (var keyword in cssKeywords) { + if (cssKeywords.hasOwnProperty(keyword)) { + var value = cssKeywords[keyword]; - if (this.operator === '') { - rangeTmp = new Range(comp.value, options) - return satisfies(this.value, rangeTmp, options) - } else if (comp.operator === '') { - rangeTmp = new Range(this.value, options) - return satisfies(comp.semver, rangeTmp, options) - } + // Compute comparative distance + var distance = comparativeDistance(rgb, value); - var sameDirectionIncreasing = - (this.operator === '>=' || this.operator === '>') && - (comp.operator === '>=' || comp.operator === '>') - var sameDirectionDecreasing = - (this.operator === '<=' || this.operator === '<') && - (comp.operator === '<=' || comp.operator === '<') - var sameSemVer = this.semver.version === comp.semver.version - var differentDirectionsInclusive = - (this.operator === '>=' || this.operator === '<=') && - (comp.operator === '>=' || comp.operator === '<=') - var oppositeDirectionsLessThan = - cmp(this.semver, '<', comp.semver, options) && - ((this.operator === '>=' || this.operator === '>') && - (comp.operator === '<=' || comp.operator === '<')) - var oppositeDirectionsGreaterThan = - cmp(this.semver, '>', comp.semver, options) && - ((this.operator === '<=' || this.operator === '<') && - (comp.operator === '>=' || comp.operator === '>')) + // Check if its less, if so set as closest + if (distance < currentClosestDistance) { + currentClosestDistance = distance; + currentClosestKeyword = keyword; + } + } + } - return sameDirectionIncreasing || sameDirectionDecreasing || - (sameSemVer && differentDirectionsInclusive) || - oppositeDirectionsLessThan || oppositeDirectionsGreaterThan -} + return currentClosestKeyword; +}; -exports.Range = Range -function Range (range, options) { - if (!options || typeof options !== 'object') { - options = { - loose: !!options, - includePrerelease: false - } - } +convert.keyword.rgb = function (keyword) { + return cssKeywords[keyword]; +}; - if (range instanceof Range) { - if (range.loose === !!options.loose && - range.includePrerelease === !!options.includePrerelease) { - return range - } else { - return new Range(range.raw, options) - } - } +convert.rgb.xyz = function (rgb) { + var r = rgb[0] / 255; + var g = rgb[1] / 255; + var b = rgb[2] / 255; - if (range instanceof Comparator) { - return new Range(range.value, options) - } + // assume sRGB + r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92); + g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92); + b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92); - if (!(this instanceof Range)) { - return new Range(range, options) - } + var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805); + var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722); + var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505); - this.options = options - this.loose = !!options.loose - this.includePrerelease = !!options.includePrerelease + return [x * 100, y * 100, z * 100]; +}; - // First, split based on boolean or || - this.raw = range - this.set = range.split(/\s*\|\|\s*/).map(function (range) { - return this.parseRange(range.trim()) - }, this).filter(function (c) { - // throw out any that are not relevant for whatever reason - return c.length - }) +convert.rgb.lab = function (rgb) { + var xyz = convert.rgb.xyz(rgb); + var x = xyz[0]; + var y = xyz[1]; + var z = xyz[2]; + var l; + var a; + var b; - if (!this.set.length) { - throw new TypeError('Invalid SemVer Range: ' + range) - } + x /= 95.047; + y /= 100; + z /= 108.883; - this.format() -} + x = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116); + y = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116); + z = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116); -Range.prototype.format = function () { - this.range = this.set.map(function (comps) { - return comps.join(' ').trim() - }).join('||').trim() - return this.range -} + l = (116 * y) - 16; + a = 500 * (x - y); + b = 200 * (y - z); -Range.prototype.toString = function () { - return this.range -} + return [l, a, b]; +}; -Range.prototype.parseRange = function (range) { - var loose = this.options.loose - range = range.trim() - // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4` - var hr = loose ? re[HYPHENRANGELOOSE] : re[HYPHENRANGE] - range = range.replace(hr, hyphenReplace) - debug('hyphen replace', range) - // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5` - range = range.replace(re[COMPARATORTRIM], comparatorTrimReplace) - debug('comparator trim', range, re[COMPARATORTRIM]) +convert.hsl.rgb = function (hsl) { + var h = hsl[0] / 360; + var s = hsl[1] / 100; + var l = hsl[2] / 100; + var t1; + var t2; + var t3; + var rgb; + var val; - // `~ 1.2.3` => `~1.2.3` - range = range.replace(re[TILDETRIM], tildeTrimReplace) + if (s === 0) { + val = l * 255; + return [val, val, val]; + } - // `^ 1.2.3` => `^1.2.3` - range = range.replace(re[CARETTRIM], caretTrimReplace) + if (l < 0.5) { + t2 = l * (1 + s); + } else { + t2 = l + s - l * s; + } - // normalize spaces - range = range.split(/\s+/).join(' ') + t1 = 2 * l - t2; - // At this point, the range is completely trimmed and - // ready to be split into comparators. + rgb = [0, 0, 0]; + for (var i = 0; i < 3; i++) { + t3 = h + 1 / 3 * -(i - 1); + if (t3 < 0) { + t3++; + } + if (t3 > 1) { + t3--; + } - var compRe = loose ? re[COMPARATORLOOSE] : re[COMPARATOR] - var set = range.split(' ').map(function (comp) { - return parseComparator(comp, this.options) - }, this).join(' ').split(/\s+/) - if (this.options.loose) { - // in loose mode, throw out any that are not valid comparators - set = set.filter(function (comp) { - return !!comp.match(compRe) - }) - } - set = set.map(function (comp) { - return new Comparator(comp, this.options) - }, this) + if (6 * t3 < 1) { + val = t1 + (t2 - t1) * 6 * t3; + } else if (2 * t3 < 1) { + val = t2; + } else if (3 * t3 < 2) { + val = t1 + (t2 - t1) * (2 / 3 - t3) * 6; + } else { + val = t1; + } - return set -} + rgb[i] = val * 255; + } -Range.prototype.intersects = function (range, options) { - if (!(range instanceof Range)) { - throw new TypeError('a Range is required') - } + return rgb; +}; - return this.set.some(function (thisComparators) { - return thisComparators.every(function (thisComparator) { - return range.set.some(function (rangeComparators) { - return rangeComparators.every(function (rangeComparator) { - return thisComparator.intersects(rangeComparator, options) - }) - }) - }) - }) -} +convert.hsl.hsv = function (hsl) { + var h = hsl[0]; + var s = hsl[1] / 100; + var l = hsl[2] / 100; + var smin = s; + var lmin = Math.max(l, 0.01); + var sv; + var v; -// Mostly just for testing and legacy API reasons -exports.toComparators = toComparators -function toComparators (range, options) { - return new Range(range, options).set.map(function (comp) { - return comp.map(function (c) { - return c.value - }).join(' ').trim().split(' ') - }) -} + l *= 2; + s *= (l <= 1) ? l : 2 - l; + smin *= lmin <= 1 ? lmin : 2 - lmin; + v = (l + s) / 2; + sv = l === 0 ? (2 * smin) / (lmin + smin) : (2 * s) / (l + s); -// comprised of xranges, tildes, stars, and gtlt's at this point. -// already replaced the hyphen ranges -// turn into a set of JUST comparators. -function parseComparator (comp, options) { - debug('comp', comp, options) - comp = replaceCarets(comp, options) - debug('caret', comp) - comp = replaceTildes(comp, options) - debug('tildes', comp) - comp = replaceXRanges(comp, options) - debug('xrange', comp) - comp = replaceStars(comp, options) - debug('stars', comp) - return comp -} + return [h, sv * 100, v * 100]; +}; -function isX (id) { - return !id || id.toLowerCase() === 'x' || id === '*' -} +convert.hsv.rgb = function (hsv) { + var h = hsv[0] / 60; + var s = hsv[1] / 100; + var v = hsv[2] / 100; + var hi = Math.floor(h) % 6; -// ~, ~> --> * (any, kinda silly) -// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0 -// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0 -// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0 -// ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0 -// ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0 -function replaceTildes (comp, options) { - return comp.trim().split(/\s+/).map(function (comp) { - return replaceTilde(comp, options) - }).join(' ') -} + var f = h - Math.floor(h); + var p = 255 * v * (1 - s); + var q = 255 * v * (1 - (s * f)); + var t = 255 * v * (1 - (s * (1 - f))); + v *= 255; -function replaceTilde (comp, options) { - var r = options.loose ? re[TILDELOOSE] : re[TILDE] - return comp.replace(r, function (_, M, m, p, pr) { - debug('tilde', comp, _, M, m, p, pr) - var ret + switch (hi) { + case 0: + return [v, t, p]; + case 1: + return [q, v, p]; + case 2: + return [p, v, t]; + case 3: + return [p, q, v]; + case 4: + return [t, p, v]; + case 5: + return [v, p, q]; + } +}; - if (isX(M)) { - ret = '' - } else if (isX(m)) { - ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' - } else if (isX(p)) { - // ~1.2 == >=1.2.0 <1.3.0 - ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' - } else if (pr) { - debug('replaceTilde pr', pr) - ret = '>=' + M + '.' + m + '.' + p + '-' + pr + - ' <' + M + '.' + (+m + 1) + '.0' - } else { - // ~1.2.3 == >=1.2.3 <1.3.0 - ret = '>=' + M + '.' + m + '.' + p + - ' <' + M + '.' + (+m + 1) + '.0' - } +convert.hsv.hsl = function (hsv) { + var h = hsv[0]; + var s = hsv[1] / 100; + var v = hsv[2] / 100; + var vmin = Math.max(v, 0.01); + var lmin; + var sl; + var l; - debug('tilde return', ret) - return ret - }) -} + l = (2 - s) * v; + lmin = (2 - s) * vmin; + sl = s * vmin; + sl /= (lmin <= 1) ? lmin : 2 - lmin; + sl = sl || 0; + l /= 2; -// ^ --> * (any, kinda silly) -// ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0 -// ^2.0, ^2.0.x --> >=2.0.0 <3.0.0 -// ^1.2, ^1.2.x --> >=1.2.0 <2.0.0 -// ^1.2.3 --> >=1.2.3 <2.0.0 -// ^1.2.0 --> >=1.2.0 <2.0.0 -function replaceCarets (comp, options) { - return comp.trim().split(/\s+/).map(function (comp) { - return replaceCaret(comp, options) - }).join(' ') -} + return [h, sl * 100, l * 100]; +}; -function replaceCaret (comp, options) { - debug('caret', comp, options) - var r = options.loose ? re[CARETLOOSE] : re[CARET] - return comp.replace(r, function (_, M, m, p, pr) { - debug('caret', comp, _, M, m, p, pr) - var ret +// http://dev.w3.org/csswg/css-color/#hwb-to-rgb +convert.hwb.rgb = function (hwb) { + var h = hwb[0] / 360; + var wh = hwb[1] / 100; + var bl = hwb[2] / 100; + var ratio = wh + bl; + var i; + var v; + var f; + var n; - if (isX(M)) { - ret = '' - } else if (isX(m)) { - ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' - } else if (isX(p)) { - if (M === '0') { - ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' - } else { - ret = '>=' + M + '.' + m + '.0 <' + (+M + 1) + '.0.0' - } - } else if (pr) { - debug('replaceCaret pr', pr) - if (M === '0') { - if (m === '0') { - ret = '>=' + M + '.' + m + '.' + p + '-' + pr + - ' <' + M + '.' + m + '.' + (+p + 1) - } else { - ret = '>=' + M + '.' + m + '.' + p + '-' + pr + - ' <' + M + '.' + (+m + 1) + '.0' - } - } else { - ret = '>=' + M + '.' + m + '.' + p + '-' + pr + - ' <' + (+M + 1) + '.0.0' - } - } else { - debug('no pr') - if (M === '0') { - if (m === '0') { - ret = '>=' + M + '.' + m + '.' + p + - ' <' + M + '.' + m + '.' + (+p + 1) - } else { - ret = '>=' + M + '.' + m + '.' + p + - ' <' + M + '.' + (+m + 1) + '.0' - } - } else { - ret = '>=' + M + '.' + m + '.' + p + - ' <' + (+M + 1) + '.0.0' - } - } + // wh + bl cant be > 1 + if (ratio > 1) { + wh /= ratio; + bl /= ratio; + } - debug('caret return', ret) - return ret - }) -} + i = Math.floor(6 * h); + v = 1 - bl; + f = 6 * h - i; -function replaceXRanges (comp, options) { - debug('replaceXRanges', comp, options) - return comp.split(/\s+/).map(function (comp) { - return replaceXRange(comp, options) - }).join(' ') -} + if ((i & 0x01) !== 0) { + f = 1 - f; + } -function replaceXRange (comp, options) { - comp = comp.trim() - var r = options.loose ? re[XRANGELOOSE] : re[XRANGE] - return comp.replace(r, function (ret, gtlt, M, m, p, pr) { - debug('xRange', comp, ret, gtlt, M, m, p, pr) - var xM = isX(M) - var xm = xM || isX(m) - var xp = xm || isX(p) - var anyX = xp + n = wh + f * (v - wh); // linear interpolation - if (gtlt === '=' && anyX) { - gtlt = '' - } + var r; + var g; + var b; + switch (i) { + default: + case 6: + case 0: r = v; g = n; b = wh; break; + case 1: r = n; g = v; b = wh; break; + case 2: r = wh; g = v; b = n; break; + case 3: r = wh; g = n; b = v; break; + case 4: r = n; g = wh; b = v; break; + case 5: r = v; g = wh; b = n; break; + } - if (xM) { - if (gtlt === '>' || gtlt === '<') { - // nothing is allowed - ret = '<0.0.0' - } else { - // nothing is forbidden - ret = '*' - } - } else if (gtlt && anyX) { - // we know patch is an x, because we have any x at all. - // replace X with 0 - if (xm) { - m = 0 - } - p = 0 + return [r * 255, g * 255, b * 255]; +}; - if (gtlt === '>') { - // >1 => >=2.0.0 - // >1.2 => >=1.3.0 - // >1.2.3 => >= 1.2.4 - gtlt = '>=' - if (xm) { - M = +M + 1 - m = 0 - p = 0 - } else { - m = +m + 1 - p = 0 - } - } else if (gtlt === '<=') { - // <=0.7.x is actually <0.8.0, since any 0.7.x should - // pass. Similarly, <=7.x is actually <8.0.0, etc. - gtlt = '<' - if (xm) { - M = +M + 1 - } else { - m = +m + 1 - } - } +convert.cmyk.rgb = function (cmyk) { + var c = cmyk[0] / 100; + var m = cmyk[1] / 100; + var y = cmyk[2] / 100; + var k = cmyk[3] / 100; + var r; + var g; + var b; - ret = gtlt + M + '.' + m + '.' + p - } else if (xm) { - ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' - } else if (xp) { - ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' - } + r = 1 - Math.min(1, c * (1 - k) + k); + g = 1 - Math.min(1, m * (1 - k) + k); + b = 1 - Math.min(1, y * (1 - k) + k); - debug('xRange return', ret) + return [r * 255, g * 255, b * 255]; +}; - return ret - }) -} +convert.xyz.rgb = function (xyz) { + var x = xyz[0] / 100; + var y = xyz[1] / 100; + var z = xyz[2] / 100; + var r; + var g; + var b; -// Because * is AND-ed with everything else in the comparator, -// and '' means "any version", just remove the *s entirely. -function replaceStars (comp, options) { - debug('replaceStars', comp, options) - // Looseness is ignored here. star is always as loose as it gets! - return comp.trim().replace(re[STAR], '') -} + r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986); + g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415); + b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570); -// This function is passed to string.replace(re[HYPHENRANGE]) -// M, m, patch, prerelease, build -// 1.2 - 3.4.5 => >=1.2.0 <=3.4.5 -// 1.2.3 - 3.4 => >=1.2.0 <3.5.0 Any 3.4.x will do -// 1.2 - 3.4 => >=1.2.0 <3.5.0 -function hyphenReplace ($0, - from, fM, fm, fp, fpr, fb, - to, tM, tm, tp, tpr, tb) { - if (isX(fM)) { - from = '' - } else if (isX(fm)) { - from = '>=' + fM + '.0.0' - } else if (isX(fp)) { - from = '>=' + fM + '.' + fm + '.0' - } else { - from = '>=' + from - } + // assume sRGB + r = r > 0.0031308 + ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055) + : r * 12.92; - if (isX(tM)) { - to = '' - } else if (isX(tm)) { - to = '<' + (+tM + 1) + '.0.0' - } else if (isX(tp)) { - to = '<' + tM + '.' + (+tm + 1) + '.0' - } else if (tpr) { - to = '<=' + tM + '.' + tm + '.' + tp + '-' + tpr - } else { - to = '<=' + to - } + g = g > 0.0031308 + ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055) + : g * 12.92; - return (from + ' ' + to).trim() -} + b = b > 0.0031308 + ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055) + : b * 12.92; -// if ANY of the sets match ALL of its comparators, then pass -Range.prototype.test = function (version) { - if (!version) { - return false - } + r = Math.min(Math.max(0, r), 1); + g = Math.min(Math.max(0, g), 1); + b = Math.min(Math.max(0, b), 1); - if (typeof version === 'string') { - version = new SemVer(version, this.options) - } + return [r * 255, g * 255, b * 255]; +}; - for (var i = 0; i < this.set.length; i++) { - if (testSet(this.set[i], version, this.options)) { - return true - } - } - return false -} +convert.xyz.lab = function (xyz) { + var x = xyz[0]; + var y = xyz[1]; + var z = xyz[2]; + var l; + var a; + var b; -function testSet (set, version, options) { - for (var i = 0; i < set.length; i++) { - if (!set[i].test(version)) { - return false - } - } + x /= 95.047; + y /= 100; + z /= 108.883; - if (version.prerelease.length && !options.includePrerelease) { - // Find the set of versions that are allowed to have prereleases - // For example, ^1.2.3-pr.1 desugars to >=1.2.3-pr.1 <2.0.0 - // That should allow `1.2.3-pr.2` to pass. - // However, `1.2.4-alpha.notready` should NOT be allowed, - // even though it's within the range set by the comparators. - for (i = 0; i < set.length; i++) { - debug(set[i].semver) - if (set[i].semver === ANY) { - continue - } + x = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116); + y = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116); + z = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116); - if (set[i].semver.prerelease.length > 0) { - var allowed = set[i].semver - if (allowed.major === version.major && - allowed.minor === version.minor && - allowed.patch === version.patch) { - return true - } - } - } + l = (116 * y) - 16; + a = 500 * (x - y); + b = 200 * (y - z); - // Version has a -pre, but it's not one of the ones we like. - return false - } + return [l, a, b]; +}; - return true -} +convert.lab.xyz = function (lab) { + var l = lab[0]; + var a = lab[1]; + var b = lab[2]; + var x; + var y; + var z; -exports.satisfies = satisfies -function satisfies (version, range, options) { - try { - range = new Range(range, options) - } catch (er) { - return false - } - return range.test(version) -} + y = (l + 16) / 116; + x = a / 500 + y; + z = y - b / 200; -exports.maxSatisfying = maxSatisfying -function maxSatisfying (versions, range, options) { - var max = null - var maxSV = null - try { - var rangeObj = new Range(range, options) - } catch (er) { - return null - } - versions.forEach(function (v) { - if (rangeObj.test(v)) { - // satisfies(v, range, options) - if (!max || maxSV.compare(v) === -1) { - // compare(max, v, true) - max = v - maxSV = new SemVer(max, options) - } - } - }) - return max -} - -exports.minSatisfying = minSatisfying -function minSatisfying (versions, range, options) { - var min = null - var minSV = null - try { - var rangeObj = new Range(range, options) - } catch (er) { - return null - } - versions.forEach(function (v) { - if (rangeObj.test(v)) { - // satisfies(v, range, options) - if (!min || minSV.compare(v) === 1) { - // compare(min, v, true) - min = v - minSV = new SemVer(min, options) - } - } - }) - return min -} - -exports.minVersion = minVersion -function minVersion (range, loose) { - range = new Range(range, loose) + var y2 = Math.pow(y, 3); + var x2 = Math.pow(x, 3); + var z2 = Math.pow(z, 3); + y = y2 > 0.008856 ? y2 : (y - 16 / 116) / 7.787; + x = x2 > 0.008856 ? x2 : (x - 16 / 116) / 7.787; + z = z2 > 0.008856 ? z2 : (z - 16 / 116) / 7.787; - var minver = new SemVer('0.0.0') - if (range.test(minver)) { - return minver - } + x *= 95.047; + y *= 100; + z *= 108.883; - minver = new SemVer('0.0.0-0') - if (range.test(minver)) { - return minver - } + return [x, y, z]; +}; - minver = null - for (var i = 0; i < range.set.length; ++i) { - var comparators = range.set[i] +convert.lab.lch = function (lab) { + var l = lab[0]; + var a = lab[1]; + var b = lab[2]; + var hr; + var h; + var c; - comparators.forEach(function (comparator) { - // Clone to avoid manipulating the comparator's semver object. - var compver = new SemVer(comparator.semver.version) - switch (comparator.operator) { - case '>': - if (compver.prerelease.length === 0) { - compver.patch++ - } else { - compver.prerelease.push(0) - } - compver.raw = compver.format() - /* fallthrough */ - case '': - case '>=': - if (!minver || gt(minver, compver)) { - minver = compver - } - break - case '<': - case '<=': - /* Ignore maximum versions */ - break - /* istanbul ignore next */ - default: - throw new Error('Unexpected operation: ' + comparator.operator) - } - }) - } + hr = Math.atan2(b, a); + h = hr * 360 / 2 / Math.PI; - if (minver && range.test(minver)) { - return minver - } + if (h < 0) { + h += 360; + } - return null -} + c = Math.sqrt(a * a + b * b); -exports.validRange = validRange -function validRange (range, options) { - try { - // Return '*' instead of '' so that truthiness works. - // This will throw if it's invalid anyway - return new Range(range, options).range || '*' - } catch (er) { - return null - } -} + return [l, c, h]; +}; -// Determine if version is less than all the versions possible in the range -exports.ltr = ltr -function ltr (version, range, options) { - return outside(version, range, '<', options) -} +convert.lch.lab = function (lch) { + var l = lch[0]; + var c = lch[1]; + var h = lch[2]; + var a; + var b; + var hr; -// Determine if version is greater than all the versions possible in the range. -exports.gtr = gtr -function gtr (version, range, options) { - return outside(version, range, '>', options) -} + hr = h / 360 * 2 * Math.PI; + a = c * Math.cos(hr); + b = c * Math.sin(hr); -exports.outside = outside -function outside (version, range, hilo, options) { - version = new SemVer(version, options) - range = new Range(range, options) + return [l, a, b]; +}; - var gtfn, ltefn, ltfn, comp, ecomp - switch (hilo) { - case '>': - gtfn = gt - ltefn = lte - ltfn = lt - comp = '>' - ecomp = '>=' - break - case '<': - gtfn = lt - ltefn = gte - ltfn = gt - comp = '<' - ecomp = '<=' - break - default: - throw new TypeError('Must provide a hilo val of "<" or ">"') - } +convert.rgb.ansi16 = function (args) { + var r = args[0]; + var g = args[1]; + var b = args[2]; + var value = 1 in arguments ? arguments[1] : convert.rgb.hsv(args)[2]; // hsv -> ansi16 optimization - // If it satisifes the range it is not outside - if (satisfies(version, range, options)) { - return false - } + value = Math.round(value / 50); - // From now on, variable terms are as if we're in "gtr" mode. - // but note that everything is flipped for the "ltr" function. + if (value === 0) { + return 30; + } - for (var i = 0; i < range.set.length; ++i) { - var comparators = range.set[i] + var ansi = 30 + + ((Math.round(b / 255) << 2) + | (Math.round(g / 255) << 1) + | Math.round(r / 255)); - var high = null - var low = null + if (value === 2) { + ansi += 60; + } - comparators.forEach(function (comparator) { - if (comparator.semver === ANY) { - comparator = new Comparator('>=0.0.0') - } - high = high || comparator - low = low || comparator - if (gtfn(comparator.semver, high.semver, options)) { - high = comparator - } else if (ltfn(comparator.semver, low.semver, options)) { - low = comparator - } - }) + return ansi; +}; - // If the edge version comparator has a operator then our version - // isn't outside it - if (high.operator === comp || high.operator === ecomp) { - return false - } +convert.hsv.ansi16 = function (args) { + // optimization here; we already know the value and don't need to get + // it converted for us. + return convert.rgb.ansi16(convert.hsv.rgb(args), args[2]); +}; - // If the lowest version comparator has an operator and our version - // is less than it then it isn't higher than the range - if ((!low.operator || low.operator === comp) && - ltefn(version, low.semver)) { - return false - } else if (low.operator === ecomp && ltfn(version, low.semver)) { - return false - } - } - return true -} +convert.rgb.ansi256 = function (args) { + var r = args[0]; + var g = args[1]; + var b = args[2]; -exports.prerelease = prerelease -function prerelease (version, options) { - var parsed = parse(version, options) - return (parsed && parsed.prerelease.length) ? parsed.prerelease : null -} + // we use the extended greyscale palette here, with the exception of + // black and white. normal palette only has 4 greyscale shades. + if (r === g && g === b) { + if (r < 8) { + return 16; + } -exports.intersects = intersects -function intersects (r1, r2, options) { - r1 = new Range(r1, options) - r2 = new Range(r2, options) - return r1.intersects(r2) -} + if (r > 248) { + return 231; + } -exports.coerce = coerce -function coerce (version) { - if (version instanceof SemVer) { - return version - } + return Math.round(((r - 8) / 247) * 24) + 232; + } - if (typeof version !== 'string') { - return null - } + var ansi = 16 + + (36 * Math.round(r / 255 * 5)) + + (6 * Math.round(g / 255 * 5)) + + Math.round(b / 255 * 5); - var match = version.match(re[COERCE]) + return ansi; +}; - if (match == null) { - return null - } +convert.ansi16.rgb = function (args) { + var color = args % 10; - return parse(match[1] + - '.' + (match[2] || '0') + - '.' + (match[3] || '0')) -} + // handle greyscale + if (color === 0 || color === 7) { + if (args > 50) { + color += 3.5; + } + color = color / 10.5 * 255; -/***/ }), -/* 369 */ -/***/ (function(module, exports, __webpack_require__) { + return [color, color, color]; + } -var parse = __webpack_require__(370); -var correct = __webpack_require__(372); + var mult = (~~(args > 50) + 1) * 0.5; + var r = ((color & 1) * mult) * 255; + var g = (((color >> 1) & 1) * mult) * 255; + var b = (((color >> 2) & 1) * mult) * 255; -var genericWarning = ( - 'license should be ' + - 'a valid SPDX license expression (without "LicenseRef"), ' + - '"UNLICENSED", or ' + - '"SEE LICENSE IN "' -); + return [r, g, b]; +}; -var fileReferenceRE = /^SEE LICEN[CS]E IN (.+)$/; +convert.ansi256.rgb = function (args) { + // handle greyscale + if (args >= 232) { + var c = (args - 232) * 10 + 8; + return [c, c, c]; + } -function startsWith(prefix, string) { - return string.slice(0, prefix.length) === prefix; -} + args -= 16; -function usesLicenseRef(ast) { - if (ast.hasOwnProperty('license')) { - var license = ast.license; - return ( - startsWith('LicenseRef', license) || - startsWith('DocumentRef', license) - ); - } else { - return ( - usesLicenseRef(ast.left) || - usesLicenseRef(ast.right) - ); - } -} + var rem; + var r = Math.floor(args / 36) / 5 * 255; + var g = Math.floor((rem = args % 36) / 6) / 5 * 255; + var b = (rem % 6) / 5 * 255; -module.exports = function(argument) { - var ast; + return [r, g, b]; +}; - try { - ast = parse(argument); - } catch (e) { - var match - if ( - argument === 'UNLICENSED' || - argument === 'UNLICENCED' - ) { - return { - validForOldPackages: true, - validForNewPackages: true, - unlicensed: true - }; - } else if (match = fileReferenceRE.exec(argument)) { - return { - validForOldPackages: true, - validForNewPackages: true, - inFile: match[1] - }; - } else { - var result = { - validForOldPackages: false, - validForNewPackages: false, - warnings: [genericWarning] - }; - var corrected = correct(argument); - if (corrected) { - result.warnings.push( - 'license is similar to the valid expression "' + corrected + '"' - ); - } - return result; - } - } +convert.rgb.hex = function (args) { + var integer = ((Math.round(args[0]) & 0xFF) << 16) + + ((Math.round(args[1]) & 0xFF) << 8) + + (Math.round(args[2]) & 0xFF); - if (usesLicenseRef(ast)) { - return { - validForNewPackages: false, - validForOldPackages: false, - spdx: true, - warnings: [genericWarning] - }; - } else { - return { - validForNewPackages: true, - validForOldPackages: true, - spdx: true - }; - } + var string = integer.toString(16).toUpperCase(); + return '000000'.substring(string.length) + string; }; +convert.hex.rgb = function (args) { + var match = args.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i); + if (!match) { + return [0, 0, 0]; + } -/***/ }), -/* 370 */ -/***/ (function(module, exports, __webpack_require__) { + var colorString = match[0]; -var parser = __webpack_require__(371).parser + if (match[0].length === 3) { + colorString = colorString.split('').map(function (char) { + return char + char; + }).join(''); + } -module.exports = function (argument) { - return parser.parse(argument) -} + var integer = parseInt(colorString, 16); + var r = (integer >> 16) & 0xFF; + var g = (integer >> 8) & 0xFF; + var b = integer & 0xFF; + return [r, g, b]; +}; -/***/ }), -/* 371 */ -/***/ (function(module, exports, __webpack_require__) { +convert.rgb.hcg = function (rgb) { + var r = rgb[0] / 255; + var g = rgb[1] / 255; + var b = rgb[2] / 255; + var max = Math.max(Math.max(r, g), b); + var min = Math.min(Math.min(r, g), b); + var chroma = (max - min); + var grayscale; + var hue; -/* WEBPACK VAR INJECTION */(function(module) {/* parser generated by jison 0.4.17 */ -/* - Returns a Parser object of the following structure: + if (chroma < 1) { + grayscale = min / (1 - chroma); + } else { + grayscale = 0; + } - Parser: { - yy: {} - } + if (chroma <= 0) { + hue = 0; + } else + if (max === r) { + hue = ((g - b) / chroma) % 6; + } else + if (max === g) { + hue = 2 + (b - r) / chroma; + } else { + hue = 4 + (r - g) / chroma + 4; + } - Parser.prototype: { - yy: {}, - trace: function(), - symbols_: {associative list: name ==> number}, - terminals_: {associative list: number ==> name}, - productions_: [...], - performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate, $$, _$), - table: [...], - defaultActions: {...}, - parseError: function(str, hash), - parse: function(input), + hue /= 6; + hue %= 1; - lexer: { - EOF: 1, - parseError: function(str, hash), - setInput: function(input), - input: function(), - unput: function(str), - more: function(), - less: function(n), - pastInput: function(), - upcomingInput: function(), - showPosition: function(), - test_match: function(regex_match_array, rule_index), - next: function(), - lex: function(), - begin: function(condition), - popState: function(), - _currentRules: function(), - topState: function(), - pushState: function(condition), + return [hue * 360, chroma * 100, grayscale * 100]; +}; - options: { - ranges: boolean (optional: true ==> token location info will include a .range[] member) - flex: boolean (optional: true ==> flex-like lexing behaviour where the rules are tested exhaustively to find the longest match) - backtrack_lexer: boolean (optional: true ==> lexer regexes are tested in order and for each matching regex the action code is invoked; the lexer terminates the scan when a token is returned by the action code) - }, +convert.hsl.hcg = function (hsl) { + var s = hsl[1] / 100; + var l = hsl[2] / 100; + var c = 1; + var f = 0; - performAction: function(yy, yy_, $avoiding_name_collisions, YY_START), - rules: [...], - conditions: {associative list: name ==> set}, - } - } + if (l < 0.5) { + c = 2.0 * s * l; + } else { + c = 2.0 * s * (1.0 - l); + } + if (c < 1.0) { + f = (l - 0.5 * c) / (1.0 - c); + } - token location info (@$, _$, etc.): { - first_line: n, - last_line: n, - first_column: n, - last_column: n, - range: [start_number, end_number] (where the numbers are indexes into the input string, regular zero-based) - } + return [hsl[0], c * 100, f * 100]; +}; +convert.hsv.hcg = function (hsv) { + var s = hsv[1] / 100; + var v = hsv[2] / 100; - the parseError function receives a 'hash' object with these members for lexer and parser errors: { - text: (matched text) - token: (the produced terminal token, if any) - line: (yylineno) - } - while parser (grammar) errors will also provide these members, i.e. parser errors deliver a superset of attributes: { - loc: (yylloc) - expected: (string describing the set of expected tokens) - recoverable: (boolean: TRUE when the parser has a error recovery rule available for this particular error) - } -*/ -var spdxparse = (function(){ -var o=function(k,v,o,l){for(o=o||{},l=k.length;l--;o[k[l]]=v);return o},$V0=[1,5],$V1=[1,6],$V2=[1,7],$V3=[1,4],$V4=[1,9],$V5=[1,10],$V6=[5,14,15,17],$V7=[5,12,14,15,17]; -var parser = {trace: function trace() { }, -yy: {}, -symbols_: {"error":2,"start":3,"expression":4,"EOS":5,"simpleExpression":6,"LICENSE":7,"PLUS":8,"LICENSEREF":9,"DOCUMENTREF":10,"COLON":11,"WITH":12,"EXCEPTION":13,"AND":14,"OR":15,"OPEN":16,"CLOSE":17,"$accept":0,"$end":1}, -terminals_: {2:"error",5:"EOS",7:"LICENSE",8:"PLUS",9:"LICENSEREF",10:"DOCUMENTREF",11:"COLON",12:"WITH",13:"EXCEPTION",14:"AND",15:"OR",16:"OPEN",17:"CLOSE"}, -productions_: [0,[3,2],[6,1],[6,2],[6,1],[6,3],[4,1],[4,3],[4,3],[4,3],[4,3]], -performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate /* action[1] */, $$ /* vstack */, _$ /* lstack */) { -/* this == yyval */ + var c = s * v; + var f = 0; -var $0 = $$.length - 1; -switch (yystate) { -case 1: -return this.$ = $$[$0-1] -break; -case 2: case 4: case 5: -this.$ = {license: yytext} -break; -case 3: -this.$ = {license: $$[$0-1], plus: true} -break; -case 6: -this.$ = $$[$0] -break; -case 7: -this.$ = {exception: $$[$0]} -this.$.license = $$[$0-2].license -if ($$[$0-2].hasOwnProperty('plus')) { - this.$.plus = $$[$0-2].plus -} -break; -case 8: -this.$ = {conjunction: 'and', left: $$[$0-2], right: $$[$0]} -break; -case 9: -this.$ = {conjunction: 'or', left: $$[$0-2], right: $$[$0]} -break; -case 10: -this.$ = $$[$0-1] -break; -} -}, -table: [{3:1,4:2,6:3,7:$V0,9:$V1,10:$V2,16:$V3},{1:[3]},{5:[1,8],14:$V4,15:$V5},o($V6,[2,6],{12:[1,11]}),{4:12,6:3,7:$V0,9:$V1,10:$V2,16:$V3},o($V7,[2,2],{8:[1,13]}),o($V7,[2,4]),{11:[1,14]},{1:[2,1]},{4:15,6:3,7:$V0,9:$V1,10:$V2,16:$V3},{4:16,6:3,7:$V0,9:$V1,10:$V2,16:$V3},{13:[1,17]},{14:$V4,15:$V5,17:[1,18]},o($V7,[2,3]),{9:[1,19]},o($V6,[2,8]),o([5,15,17],[2,9],{14:$V4}),o($V6,[2,7]),o($V6,[2,10]),o($V7,[2,5])], -defaultActions: {8:[2,1]}, -parseError: function parseError(str, hash) { - if (hash.recoverable) { - this.trace(str); - } else { - function _parseError (msg, hash) { - this.message = msg; - this.hash = hash; - } - _parseError.prototype = Error; + if (c < 1.0) { + f = (v - c) / (1 - c); + } - throw new _parseError(str, hash); - } -}, -parse: function parse(input) { - var self = this, stack = [0], tstack = [], vstack = [null], lstack = [], table = this.table, yytext = '', yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1; - var args = lstack.slice.call(arguments, 1); - var lexer = Object.create(this.lexer); - var sharedState = { yy: {} }; - for (var k in this.yy) { - if (Object.prototype.hasOwnProperty.call(this.yy, k)) { - sharedState.yy[k] = this.yy[k]; - } - } - lexer.setInput(input, sharedState.yy); - sharedState.yy.lexer = lexer; - sharedState.yy.parser = this; - if (typeof lexer.yylloc == 'undefined') { - lexer.yylloc = {}; - } - var yyloc = lexer.yylloc; - lstack.push(yyloc); - var ranges = lexer.options && lexer.options.ranges; - if (typeof sharedState.yy.parseError === 'function') { - this.parseError = sharedState.yy.parseError; - } else { - this.parseError = Object.getPrototypeOf(this).parseError; - } - function popStack(n) { - stack.length = stack.length - 2 * n; - vstack.length = vstack.length - n; - lstack.length = lstack.length - n; - } - _token_stack: - var lex = function () { - var token; - token = lexer.lex() || EOF; - if (typeof token !== 'number') { - token = self.symbols_[token] || token; - } - return token; - }; - var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected; - while (true) { - state = stack[stack.length - 1]; - if (this.defaultActions[state]) { - action = this.defaultActions[state]; - } else { - if (symbol === null || typeof symbol == 'undefined') { - symbol = lex(); - } - action = table[state] && table[state][symbol]; - } - if (typeof action === 'undefined' || !action.length || !action[0]) { - var errStr = ''; - expected = []; - for (p in table[state]) { - if (this.terminals_[p] && p > TERROR) { - expected.push('\'' + this.terminals_[p] + '\''); - } - } - if (lexer.showPosition) { - errStr = 'Parse error on line ' + (yylineno + 1) + ':\n' + lexer.showPosition() + '\nExpecting ' + expected.join(', ') + ', got \'' + (this.terminals_[symbol] || symbol) + '\''; - } else { - errStr = 'Parse error on line ' + (yylineno + 1) + ': Unexpected ' + (symbol == EOF ? 'end of input' : '\'' + (this.terminals_[symbol] || symbol) + '\''); - } - this.parseError(errStr, { - text: lexer.match, - token: this.terminals_[symbol] || symbol, - line: lexer.yylineno, - loc: yyloc, - expected: expected - }); - } - if (action[0] instanceof Array && action.length > 1) { - throw new Error('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol); - } - switch (action[0]) { - case 1: - stack.push(symbol); - vstack.push(lexer.yytext); - lstack.push(lexer.yylloc); - stack.push(action[1]); - symbol = null; - if (!preErrorSymbol) { - yyleng = lexer.yyleng; - yytext = lexer.yytext; - yylineno = lexer.yylineno; - yyloc = lexer.yylloc; - if (recovering > 0) { - recovering--; - } - } else { - symbol = preErrorSymbol; - preErrorSymbol = null; - } - break; - case 2: - len = this.productions_[action[1]][1]; - yyval.$ = vstack[vstack.length - len]; - yyval._$ = { - first_line: lstack[lstack.length - (len || 1)].first_line, - last_line: lstack[lstack.length - 1].last_line, - first_column: lstack[lstack.length - (len || 1)].first_column, - last_column: lstack[lstack.length - 1].last_column - }; - if (ranges) { - yyval._$.range = [ - lstack[lstack.length - (len || 1)].range[0], - lstack[lstack.length - 1].range[1] - ]; - } - r = this.performAction.apply(yyval, [ - yytext, - yyleng, - yylineno, - sharedState.yy, - action[1], - vstack, - lstack - ].concat(args)); - if (typeof r !== 'undefined') { - return r; - } - if (len) { - stack = stack.slice(0, -1 * len * 2); - vstack = vstack.slice(0, -1 * len); - lstack = lstack.slice(0, -1 * len); - } - stack.push(this.productions_[action[1]][0]); - vstack.push(yyval.$); - lstack.push(yyval._$); - newState = table[stack[stack.length - 2]][stack[stack.length - 1]]; - stack.push(newState); - break; - case 3: - return true; - } - } - return true; -}}; -/* generated by jison-lex 0.3.4 */ -var lexer = (function(){ -var lexer = ({ + return [hsv[0], c * 100, f * 100]; +}; -EOF:1, +convert.hcg.rgb = function (hcg) { + var h = hcg[0] / 360; + var c = hcg[1] / 100; + var g = hcg[2] / 100; -parseError:function parseError(str, hash) { - if (this.yy.parser) { - this.yy.parser.parseError(str, hash); - } else { - throw new Error(str); - } - }, + if (c === 0.0) { + return [g * 255, g * 255, g * 255]; + } -// resets the lexer, sets new input -setInput:function (input, yy) { - this.yy = yy || this.yy || {}; - this._input = input; - this._more = this._backtrack = this.done = false; - this.yylineno = this.yyleng = 0; - this.yytext = this.matched = this.match = ''; - this.conditionStack = ['INITIAL']; - this.yylloc = { - first_line: 1, - first_column: 0, - last_line: 1, - last_column: 0 - }; - if (this.options.ranges) { - this.yylloc.range = [0,0]; - } - this.offset = 0; - return this; - }, + var pure = [0, 0, 0]; + var hi = (h % 1) * 6; + var v = hi % 1; + var w = 1 - v; + var mg = 0; -// consumes and returns one char from the input -input:function () { - var ch = this._input[0]; - this.yytext += ch; - this.yyleng++; - this.offset++; - this.match += ch; - this.matched += ch; - var lines = ch.match(/(?:\r\n?|\n).*/g); - if (lines) { - this.yylineno++; - this.yylloc.last_line++; - } else { - this.yylloc.last_column++; - } - if (this.options.ranges) { - this.yylloc.range[1]++; - } + switch (Math.floor(hi)) { + case 0: + pure[0] = 1; pure[1] = v; pure[2] = 0; break; + case 1: + pure[0] = w; pure[1] = 1; pure[2] = 0; break; + case 2: + pure[0] = 0; pure[1] = 1; pure[2] = v; break; + case 3: + pure[0] = 0; pure[1] = w; pure[2] = 1; break; + case 4: + pure[0] = v; pure[1] = 0; pure[2] = 1; break; + default: + pure[0] = 1; pure[1] = 0; pure[2] = w; + } - this._input = this._input.slice(1); - return ch; - }, + mg = (1.0 - c) * g; -// unshifts one char (or a string) into the input -unput:function (ch) { - var len = ch.length; - var lines = ch.split(/(?:\r\n?|\n)/g); + return [ + (c * pure[0] + mg) * 255, + (c * pure[1] + mg) * 255, + (c * pure[2] + mg) * 255 + ]; +}; - this._input = ch + this._input; - this.yytext = this.yytext.substr(0, this.yytext.length - len); - //this.yyleng -= len; - this.offset -= len; - var oldLines = this.match.split(/(?:\r\n?|\n)/g); - this.match = this.match.substr(0, this.match.length - 1); - this.matched = this.matched.substr(0, this.matched.length - 1); +convert.hcg.hsv = function (hcg) { + var c = hcg[1] / 100; + var g = hcg[2] / 100; - if (lines.length - 1) { - this.yylineno -= lines.length - 1; - } - var r = this.yylloc.range; + var v = c + g * (1.0 - c); + var f = 0; - this.yylloc = { - first_line: this.yylloc.first_line, - last_line: this.yylineno + 1, - first_column: this.yylloc.first_column, - last_column: lines ? - (lines.length === oldLines.length ? this.yylloc.first_column : 0) - + oldLines[oldLines.length - lines.length].length - lines[0].length : - this.yylloc.first_column - len - }; + if (v > 0.0) { + f = c / v; + } - if (this.options.ranges) { - this.yylloc.range = [r[0], r[0] + this.yyleng - len]; - } - this.yyleng = this.yytext.length; - return this; - }, + return [hcg[0], f * 100, v * 100]; +}; -// When called from action, caches matched text and appends it on next action -more:function () { - this._more = true; - return this; - }, +convert.hcg.hsl = function (hcg) { + var c = hcg[1] / 100; + var g = hcg[2] / 100; -// When called from action, signals the lexer that this rule fails to match the input, so the next matching rule (regex) should be tested instead. -reject:function () { - if (this.options.backtrack_lexer) { - this._backtrack = true; - } else { - return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n' + this.showPosition(), { - text: "", - token: null, - line: this.yylineno - }); + var l = g * (1.0 - c) + 0.5 * c; + var s = 0; - } - return this; - }, + if (l > 0.0 && l < 0.5) { + s = c / (2 * l); + } else + if (l >= 0.5 && l < 1.0) { + s = c / (2 * (1 - l)); + } -// retain first n characters of the match -less:function (n) { - this.unput(this.match.slice(n)); - }, + return [hcg[0], s * 100, l * 100]; +}; -// displays already matched input, i.e. for error messages -pastInput:function () { - var past = this.matched.substr(0, this.matched.length - this.match.length); - return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, ""); - }, +convert.hcg.hwb = function (hcg) { + var c = hcg[1] / 100; + var g = hcg[2] / 100; + var v = c + g * (1.0 - c); + return [hcg[0], (v - c) * 100, (1 - v) * 100]; +}; -// displays upcoming input, i.e. for error messages -upcomingInput:function () { - var next = this.match; - if (next.length < 20) { - next += this._input.substr(0, 20-next.length); - } - return (next.substr(0,20) + (next.length > 20 ? '...' : '')).replace(/\n/g, ""); - }, +convert.hwb.hcg = function (hwb) { + var w = hwb[1] / 100; + var b = hwb[2] / 100; + var v = 1 - b; + var c = v - w; + var g = 0; -// displays the character position where the lexing error occurred, i.e. for error messages -showPosition:function () { - var pre = this.pastInput(); - var c = new Array(pre.length + 1).join("-"); - return pre + this.upcomingInput() + "\n" + c + "^"; - }, + if (c < 1) { + g = (v - c) / (1 - c); + } -// test the lexed token: return FALSE when not a match, otherwise return token -test_match:function (match, indexed_rule) { - var token, - lines, - backup; + return [hwb[0], c * 100, g * 100]; +}; - if (this.options.backtrack_lexer) { - // save context - backup = { - yylineno: this.yylineno, - yylloc: { - first_line: this.yylloc.first_line, - last_line: this.last_line, - first_column: this.yylloc.first_column, - last_column: this.yylloc.last_column - }, - yytext: this.yytext, - match: this.match, - matches: this.matches, - matched: this.matched, - yyleng: this.yyleng, - offset: this.offset, - _more: this._more, - _input: this._input, - yy: this.yy, - conditionStack: this.conditionStack.slice(0), - done: this.done - }; - if (this.options.ranges) { - backup.yylloc.range = this.yylloc.range.slice(0); - } - } +convert.apple.rgb = function (apple) { + return [(apple[0] / 65535) * 255, (apple[1] / 65535) * 255, (apple[2] / 65535) * 255]; +}; - lines = match[0].match(/(?:\r\n?|\n).*/g); - if (lines) { - this.yylineno += lines.length; - } - this.yylloc = { - first_line: this.yylloc.last_line, - last_line: this.yylineno + 1, - first_column: this.yylloc.last_column, - last_column: lines ? - lines[lines.length - 1].length - lines[lines.length - 1].match(/\r?\n?/)[0].length : - this.yylloc.last_column + match[0].length - }; - this.yytext += match[0]; - this.match += match[0]; - this.matches = match; - this.yyleng = this.yytext.length; - if (this.options.ranges) { - this.yylloc.range = [this.offset, this.offset += this.yyleng]; - } - this._more = false; - this._backtrack = false; - this._input = this._input.slice(match[0].length); - this.matched += match[0]; - token = this.performAction.call(this, this.yy, this, indexed_rule, this.conditionStack[this.conditionStack.length - 1]); - if (this.done && this._input) { - this.done = false; - } - if (token) { - return token; - } else if (this._backtrack) { - // recover context - for (var k in backup) { - this[k] = backup[k]; - } - return false; // rule action called reject() implying the next rule should be tested instead. - } - return false; - }, +convert.rgb.apple = function (rgb) { + return [(rgb[0] / 255) * 65535, (rgb[1] / 255) * 65535, (rgb[2] / 255) * 65535]; +}; -// return next match in input -next:function () { - if (this.done) { - return this.EOF; - } - if (!this._input) { - this.done = true; - } +convert.gray.rgb = function (args) { + return [args[0] / 100 * 255, args[0] / 100 * 255, args[0] / 100 * 255]; +}; - var token, - match, - tempMatch, - index; - if (!this._more) { - this.yytext = ''; - this.match = ''; - } - var rules = this._currentRules(); - for (var i = 0; i < rules.length; i++) { - tempMatch = this._input.match(this.rules[rules[i]]); - if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { - match = tempMatch; - index = i; - if (this.options.backtrack_lexer) { - token = this.test_match(tempMatch, rules[i]); - if (token !== false) { - return token; - } else if (this._backtrack) { - match = false; - continue; // rule action called reject() implying a rule MISmatch. - } else { - // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace) - return false; - } - } else if (!this.options.flex) { - break; - } - } - } - if (match) { - token = this.test_match(match, rules[index]); - if (token !== false) { - return token; - } - // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace) - return false; - } - if (this._input === "") { - return this.EOF; - } else { - return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. Unrecognized text.\n' + this.showPosition(), { - text: "", - token: null, - line: this.yylineno - }); - } - }, +convert.gray.hsl = convert.gray.hsv = function (args) { + return [0, 0, args[0]]; +}; -// return next match that has a token -lex:function lex() { - var r = this.next(); - if (r) { - return r; - } else { - return this.lex(); - } - }, +convert.gray.hwb = function (gray) { + return [0, 100, gray[0]]; +}; -// activates a new lexer condition state (pushes the new lexer condition state onto the condition stack) -begin:function begin(condition) { - this.conditionStack.push(condition); - }, +convert.gray.cmyk = function (gray) { + return [0, 0, 0, gray[0]]; +}; -// pop the previously active lexer condition state off the condition stack -popState:function popState() { - var n = this.conditionStack.length - 1; - if (n > 0) { - return this.conditionStack.pop(); - } else { - return this.conditionStack[0]; - } - }, +convert.gray.lab = function (gray) { + return [gray[0], 0, 0]; +}; -// produce the lexer rule set which is active for the currently active lexer condition state -_currentRules:function _currentRules() { - if (this.conditionStack.length && this.conditionStack[this.conditionStack.length - 1]) { - return this.conditions[this.conditionStack[this.conditionStack.length - 1]].rules; - } else { - return this.conditions["INITIAL"].rules; - } - }, +convert.gray.hex = function (gray) { + var val = Math.round(gray[0] / 100 * 255) & 0xFF; + var integer = (val << 16) + (val << 8) + val; -// return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available -topState:function topState(n) { - n = this.conditionStack.length - 1 - Math.abs(n || 0); - if (n >= 0) { - return this.conditionStack[n]; - } else { - return "INITIAL"; - } - }, + var string = integer.toString(16).toUpperCase(); + return '000000'.substring(string.length) + string; +}; -// alias for begin(condition) -pushState:function pushState(condition) { - this.begin(condition); - }, +convert.rgb.gray = function (rgb) { + var val = (rgb[0] + rgb[1] + rgb[2]) / 3; + return [val / 255 * 100]; +}; -// return the number of states currently on the stack -stateStackSize:function stateStackSize() { - return this.conditionStack.length; - }, -options: {}, -performAction: function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) { -var YYSTATE=YY_START; -switch($avoiding_name_collisions) { -case 0:return 5 -break; -case 1:/* skip whitespace */ -break; -case 2:return 8 -break; -case 3:return 16 -break; -case 4:return 17 -break; -case 5:return 11 -break; -case 6:return 10 -break; -case 7:return 9 -break; -case 8:return 14 -break; -case 9:return 15 -break; -case 10:return 12 -break; -case 11:return 7 -break; -case 12:return 7 -break; -case 13:return 7 -break; -case 14:return 7 -break; -case 15:return 7 -break; -case 16:return 7 -break; -case 17:return 7 -break; -case 18:return 7 -break; -case 19:return 7 -break; -case 20:return 7 -break; -case 21:return 7 -break; -case 22:return 7 -break; -case 23:return 7 -break; -case 24:return 13 -break; -case 25:return 13 -break; -case 26:return 13 -break; -case 27:return 13 -break; -case 28:return 13 -break; -case 29:return 13 -break; -case 30:return 13 -break; -case 31:return 13 -break; -case 32:return 7 -break; -case 33:return 13 -break; -case 34:return 7 -break; -case 35:return 13 -break; -case 36:return 7 -break; -case 37:return 13 -break; -case 38:return 13 -break; -case 39:return 7 -break; -case 40:return 13 -break; -case 41:return 13 -break; -case 42:return 13 -break; -case 43:return 13 -break; -case 44:return 13 -break; -case 45:return 7 -break; -case 46:return 13 -break; -case 47:return 7 -break; -case 48:return 7 -break; -case 49:return 7 -break; -case 50:return 7 -break; -case 51:return 7 -break; -case 52:return 7 -break; -case 53:return 7 -break; -case 54:return 7 -break; -case 55:return 7 -break; -case 56:return 7 -break; -case 57:return 7 -break; -case 58:return 7 -break; -case 59:return 7 -break; -case 60:return 7 -break; -case 61:return 7 -break; -case 62:return 7 -break; -case 63:return 13 -break; -case 64:return 7 -break; -case 65:return 7 -break; -case 66:return 13 -break; -case 67:return 7 -break; -case 68:return 7 -break; -case 69:return 7 -break; -case 70:return 7 -break; -case 71:return 7 -break; -case 72:return 7 -break; -case 73:return 13 -break; -case 74:return 7 -break; -case 75:return 13 -break; -case 76:return 7 -break; -case 77:return 7 -break; -case 78:return 7 -break; -case 79:return 7 -break; -case 80:return 7 -break; -case 81:return 7 -break; -case 82:return 7 -break; -case 83:return 7 -break; -case 84:return 7 -break; -case 85:return 7 -break; -case 86:return 7 -break; -case 87:return 7 -break; -case 88:return 7 -break; -case 89:return 7 -break; -case 90:return 7 -break; -case 91:return 7 -break; -case 92:return 7 -break; -case 93:return 7 -break; -case 94:return 7 -break; -case 95:return 7 -break; -case 96:return 7 -break; -case 97:return 7 -break; -case 98:return 7 -break; -case 99:return 7 -break; -case 100:return 7 -break; -case 101:return 7 -break; -case 102:return 7 -break; -case 103:return 7 -break; -case 104:return 7 -break; -case 105:return 7 -break; -case 106:return 7 -break; -case 107:return 7 -break; -case 108:return 7 -break; -case 109:return 7 -break; -case 110:return 7 -break; -case 111:return 7 -break; -case 112:return 7 -break; -case 113:return 7 -break; -case 114:return 7 -break; -case 115:return 7 -break; -case 116:return 7 -break; -case 117:return 7 -break; -case 118:return 7 -break; -case 119:return 7 -break; -case 120:return 7 -break; -case 121:return 7 -break; -case 122:return 7 -break; -case 123:return 7 -break; -case 124:return 7 -break; -case 125:return 7 -break; -case 126:return 7 -break; -case 127:return 7 -break; -case 128:return 7 -break; -case 129:return 7 -break; -case 130:return 7 -break; -case 131:return 7 -break; -case 132:return 7 -break; -case 133:return 7 -break; -case 134:return 7 -break; -case 135:return 7 -break; -case 136:return 7 -break; -case 137:return 7 -break; -case 138:return 7 -break; -case 139:return 7 -break; -case 140:return 7 -break; -case 141:return 7 -break; -case 142:return 7 -break; -case 143:return 7 -break; -case 144:return 7 -break; -case 145:return 7 -break; -case 146:return 7 -break; -case 147:return 7 -break; -case 148:return 7 -break; -case 149:return 7 -break; -case 150:return 7 -break; -case 151:return 7 -break; -case 152:return 7 -break; -case 153:return 7 -break; -case 154:return 7 -break; -case 155:return 7 -break; -case 156:return 7 -break; -case 157:return 7 -break; -case 158:return 7 -break; -case 159:return 7 -break; -case 160:return 7 -break; -case 161:return 7 -break; -case 162:return 7 -break; -case 163:return 7 -break; -case 164:return 7 -break; -case 165:return 7 -break; -case 166:return 7 -break; -case 167:return 7 -break; -case 168:return 7 -break; -case 169:return 7 -break; -case 170:return 7 -break; -case 171:return 7 -break; -case 172:return 7 -break; -case 173:return 7 -break; -case 174:return 7 -break; -case 175:return 7 -break; -case 176:return 7 -break; -case 177:return 7 -break; -case 178:return 7 -break; -case 179:return 7 -break; -case 180:return 7 -break; -case 181:return 7 -break; -case 182:return 7 -break; -case 183:return 7 -break; -case 184:return 7 -break; -case 185:return 7 -break; -case 186:return 7 -break; -case 187:return 7 -break; -case 188:return 7 -break; -case 189:return 7 -break; -case 190:return 7 -break; -case 191:return 7 -break; -case 192:return 7 -break; -case 193:return 7 -break; -case 194:return 7 -break; -case 195:return 7 -break; -case 196:return 7 -break; -case 197:return 7 -break; -case 198:return 7 -break; -case 199:return 7 -break; -case 200:return 7 -break; -case 201:return 7 -break; -case 202:return 7 -break; -case 203:return 7 -break; -case 204:return 7 -break; -case 205:return 7 -break; -case 206:return 7 -break; -case 207:return 7 + +/***/ }), +/* 367 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +module.exports = { + "aliceblue": [240, 248, 255], + "antiquewhite": [250, 235, 215], + "aqua": [0, 255, 255], + "aquamarine": [127, 255, 212], + "azure": [240, 255, 255], + "beige": [245, 245, 220], + "bisque": [255, 228, 196], + "black": [0, 0, 0], + "blanchedalmond": [255, 235, 205], + "blue": [0, 0, 255], + "blueviolet": [138, 43, 226], + "brown": [165, 42, 42], + "burlywood": [222, 184, 135], + "cadetblue": [95, 158, 160], + "chartreuse": [127, 255, 0], + "chocolate": [210, 105, 30], + "coral": [255, 127, 80], + "cornflowerblue": [100, 149, 237], + "cornsilk": [255, 248, 220], + "crimson": [220, 20, 60], + "cyan": [0, 255, 255], + "darkblue": [0, 0, 139], + "darkcyan": [0, 139, 139], + "darkgoldenrod": [184, 134, 11], + "darkgray": [169, 169, 169], + "darkgreen": [0, 100, 0], + "darkgrey": [169, 169, 169], + "darkkhaki": [189, 183, 107], + "darkmagenta": [139, 0, 139], + "darkolivegreen": [85, 107, 47], + "darkorange": [255, 140, 0], + "darkorchid": [153, 50, 204], + "darkred": [139, 0, 0], + "darksalmon": [233, 150, 122], + "darkseagreen": [143, 188, 143], + "darkslateblue": [72, 61, 139], + "darkslategray": [47, 79, 79], + "darkslategrey": [47, 79, 79], + "darkturquoise": [0, 206, 209], + "darkviolet": [148, 0, 211], + "deeppink": [255, 20, 147], + "deepskyblue": [0, 191, 255], + "dimgray": [105, 105, 105], + "dimgrey": [105, 105, 105], + "dodgerblue": [30, 144, 255], + "firebrick": [178, 34, 34], + "floralwhite": [255, 250, 240], + "forestgreen": [34, 139, 34], + "fuchsia": [255, 0, 255], + "gainsboro": [220, 220, 220], + "ghostwhite": [248, 248, 255], + "gold": [255, 215, 0], + "goldenrod": [218, 165, 32], + "gray": [128, 128, 128], + "green": [0, 128, 0], + "greenyellow": [173, 255, 47], + "grey": [128, 128, 128], + "honeydew": [240, 255, 240], + "hotpink": [255, 105, 180], + "indianred": [205, 92, 92], + "indigo": [75, 0, 130], + "ivory": [255, 255, 240], + "khaki": [240, 230, 140], + "lavender": [230, 230, 250], + "lavenderblush": [255, 240, 245], + "lawngreen": [124, 252, 0], + "lemonchiffon": [255, 250, 205], + "lightblue": [173, 216, 230], + "lightcoral": [240, 128, 128], + "lightcyan": [224, 255, 255], + "lightgoldenrodyellow": [250, 250, 210], + "lightgray": [211, 211, 211], + "lightgreen": [144, 238, 144], + "lightgrey": [211, 211, 211], + "lightpink": [255, 182, 193], + "lightsalmon": [255, 160, 122], + "lightseagreen": [32, 178, 170], + "lightskyblue": [135, 206, 250], + "lightslategray": [119, 136, 153], + "lightslategrey": [119, 136, 153], + "lightsteelblue": [176, 196, 222], + "lightyellow": [255, 255, 224], + "lime": [0, 255, 0], + "limegreen": [50, 205, 50], + "linen": [250, 240, 230], + "magenta": [255, 0, 255], + "maroon": [128, 0, 0], + "mediumaquamarine": [102, 205, 170], + "mediumblue": [0, 0, 205], + "mediumorchid": [186, 85, 211], + "mediumpurple": [147, 112, 219], + "mediumseagreen": [60, 179, 113], + "mediumslateblue": [123, 104, 238], + "mediumspringgreen": [0, 250, 154], + "mediumturquoise": [72, 209, 204], + "mediumvioletred": [199, 21, 133], + "midnightblue": [25, 25, 112], + "mintcream": [245, 255, 250], + "mistyrose": [255, 228, 225], + "moccasin": [255, 228, 181], + "navajowhite": [255, 222, 173], + "navy": [0, 0, 128], + "oldlace": [253, 245, 230], + "olive": [128, 128, 0], + "olivedrab": [107, 142, 35], + "orange": [255, 165, 0], + "orangered": [255, 69, 0], + "orchid": [218, 112, 214], + "palegoldenrod": [238, 232, 170], + "palegreen": [152, 251, 152], + "paleturquoise": [175, 238, 238], + "palevioletred": [219, 112, 147], + "papayawhip": [255, 239, 213], + "peachpuff": [255, 218, 185], + "peru": [205, 133, 63], + "pink": [255, 192, 203], + "plum": [221, 160, 221], + "powderblue": [176, 224, 230], + "purple": [128, 0, 128], + "rebeccapurple": [102, 51, 153], + "red": [255, 0, 0], + "rosybrown": [188, 143, 143], + "royalblue": [65, 105, 225], + "saddlebrown": [139, 69, 19], + "salmon": [250, 128, 114], + "sandybrown": [244, 164, 96], + "seagreen": [46, 139, 87], + "seashell": [255, 245, 238], + "sienna": [160, 82, 45], + "silver": [192, 192, 192], + "skyblue": [135, 206, 235], + "slateblue": [106, 90, 205], + "slategray": [112, 128, 144], + "slategrey": [112, 128, 144], + "snow": [255, 250, 250], + "springgreen": [0, 255, 127], + "steelblue": [70, 130, 180], + "tan": [210, 180, 140], + "teal": [0, 128, 128], + "thistle": [216, 191, 216], + "tomato": [255, 99, 71], + "turquoise": [64, 224, 208], + "violet": [238, 130, 238], + "wheat": [245, 222, 179], + "white": [255, 255, 255], + "whitesmoke": [245, 245, 245], + "yellow": [255, 255, 0], + "yellowgreen": [154, 205, 50] +}; + + +/***/ }), +/* 368 */ +/***/ (function(module, exports, __webpack_require__) { + +var conversions = __webpack_require__(366); + +/* + this function routes a model to all other models. + + all functions that are routed have a property `.conversion` attached + to the returned synthetic function. This property is an array + of strings, each with the steps in between the 'from' and 'to' + color models (inclusive). + + conversions that are not possible simply are not included. +*/ + +function buildGraph() { + var graph = {}; + // https://jsperf.com/object-keys-vs-for-in-with-closure/3 + var models = Object.keys(conversions); + + for (var len = models.length, i = 0; i < len; i++) { + graph[models[i]] = { + // http://jsperf.com/1-vs-infinity + // micro-opt, but this is simple. + distance: -1, + parent: null + }; + } + + return graph; +} + +// https://en.wikipedia.org/wiki/Breadth-first_search +function deriveBFS(fromModel) { + var graph = buildGraph(); + var queue = [fromModel]; // unshift -> queue -> pop + + graph[fromModel].distance = 0; + + while (queue.length) { + var current = queue.pop(); + var adjacents = Object.keys(conversions[current]); + + for (var len = adjacents.length, i = 0; i < len; i++) { + var adjacent = adjacents[i]; + var node = graph[adjacent]; + + if (node.distance === -1) { + node.distance = graph[current].distance + 1; + node.parent = current; + queue.unshift(adjacent); + } + } + } + + return graph; +} + +function link(from, to) { + return function (args) { + return to(from(args)); + }; +} + +function wrapConversion(toModel, graph) { + var path = [graph[toModel].parent, toModel]; + var fn = conversions[graph[toModel].parent][toModel]; + + var cur = graph[toModel].parent; + while (graph[cur].parent) { + path.unshift(graph[cur].parent); + fn = link(conversions[graph[cur].parent][cur], fn); + cur = graph[cur].parent; + } + + fn.conversion = path; + return fn; +} + +module.exports = function (fromModel) { + var graph = deriveBFS(fromModel); + var conversion = {}; + + var models = Object.keys(graph); + for (var len = models.length, i = 0; i < len; i++) { + var toModel = models[i]; + var node = graph[toModel]; + + if (node.parent === null) { + // no possible conversion, or this node is the source model. + continue; + } + + conversion[toModel] = wrapConversion(toModel, graph); + } + + return conversion; +}; + + + +/***/ }), +/* 369 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +const os = __webpack_require__(122); +const hasFlag = __webpack_require__(370); + +const env = process.env; + +let forceColor; +if (hasFlag('no-color') || + hasFlag('no-colors') || + hasFlag('color=false')) { + forceColor = false; +} else if (hasFlag('color') || + hasFlag('colors') || + hasFlag('color=true') || + hasFlag('color=always')) { + forceColor = true; +} +if ('FORCE_COLOR' in env) { + forceColor = env.FORCE_COLOR.length === 0 || parseInt(env.FORCE_COLOR, 10) !== 0; +} + +function translateLevel(level) { + if (level === 0) { + return false; + } + + return { + level, + hasBasic: true, + has256: level >= 2, + has16m: level >= 3 + }; +} + +function supportsColor(stream) { + if (forceColor === false) { + return 0; + } + + if (hasFlag('color=16m') || + hasFlag('color=full') || + hasFlag('color=truecolor')) { + return 3; + } + + if (hasFlag('color=256')) { + return 2; + } + + if (stream && !stream.isTTY && forceColor !== true) { + return 0; + } + + const min = forceColor ? 1 : 0; + + if (process.platform === 'win32') { + // Node.js 7.5.0 is the first version of Node.js to include a patch to + // libuv that enables 256 color output on Windows. Anything earlier and it + // won't work. However, here we target Node.js 8 at minimum as it is an LTS + // release, and Node.js 7 is not. Windows 10 build 10586 is the first Windows + // release that supports 256 colors. Windows 10 build 14931 is the first release + // that supports 16m/TrueColor. + const osRelease = os.release().split('.'); + if ( + Number(process.versions.node.split('.')[0]) >= 8 && + Number(osRelease[0]) >= 10 && + Number(osRelease[2]) >= 10586 + ) { + return Number(osRelease[2]) >= 14931 ? 3 : 2; + } + + return 1; + } + + if ('CI' in env) { + if (['TRAVIS', 'CIRCLECI', 'APPVEYOR', 'GITLAB_CI'].some(sign => sign in env) || env.CI_NAME === 'codeship') { + return 1; + } + + return min; + } + + if ('TEAMCITY_VERSION' in env) { + return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0; + } + + if (env.COLORTERM === 'truecolor') { + return 3; + } + + if ('TERM_PROGRAM' in env) { + const version = parseInt((env.TERM_PROGRAM_VERSION || '').split('.')[0], 10); + + switch (env.TERM_PROGRAM) { + case 'iTerm.app': + return version >= 3 ? 3 : 2; + case 'Apple_Terminal': + return 2; + // No default + } + } + + if (/-256(color)?$/i.test(env.TERM)) { + return 2; + } + + if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) { + return 1; + } + + if ('COLORTERM' in env) { + return 1; + } + + if (env.TERM === 'dumb') { + return min; + } + + return min; +} + +function getSupportLevel(stream) { + const level = supportsColor(stream); + return translateLevel(level); +} + +module.exports = { + supportsColor: getSupportLevel, + stdout: getSupportLevel(process.stdout), + stderr: getSupportLevel(process.stderr) +}; + + +/***/ }), +/* 370 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +module.exports = (flag, argv) => { + argv = argv || process.argv; + const prefix = flag.startsWith('-') ? '' : (flag.length === 1 ? '-' : '--'); + const pos = argv.indexOf(prefix + flag); + const terminatorPos = argv.indexOf('--'); + return pos !== -1 && (terminatorPos === -1 ? true : pos < terminatorPos); +}; + + +/***/ }), +/* 371 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +const TEMPLATE_REGEX = /(?:\\(u[a-f\d]{4}|x[a-f\d]{2}|.))|(?:\{(~)?(\w+(?:\([^)]*\))?(?:\.\w+(?:\([^)]*\))?)*)(?:[ \t]|(?=\r?\n)))|(\})|((?:.|[\r\n\f])+?)/gi; +const STYLE_REGEX = /(?:^|\.)(\w+)(?:\(([^)]*)\))?/g; +const STRING_REGEX = /^(['"])((?:\\.|(?!\1)[^\\])*)\1$/; +const ESCAPE_REGEX = /\\(u[a-f\d]{4}|x[a-f\d]{2}|.)|([^\\])/gi; + +const ESCAPES = new Map([ + ['n', '\n'], + ['r', '\r'], + ['t', '\t'], + ['b', '\b'], + ['f', '\f'], + ['v', '\v'], + ['0', '\0'], + ['\\', '\\'], + ['e', '\u001B'], + ['a', '\u0007'] +]); + +function unescape(c) { + if ((c[0] === 'u' && c.length === 5) || (c[0] === 'x' && c.length === 3)) { + return String.fromCharCode(parseInt(c.slice(1), 16)); + } + + return ESCAPES.get(c) || c; +} + +function parseArguments(name, args) { + const results = []; + const chunks = args.trim().split(/\s*,\s*/g); + let matches; + + for (const chunk of chunks) { + if (!isNaN(chunk)) { + results.push(Number(chunk)); + } else if ((matches = chunk.match(STRING_REGEX))) { + results.push(matches[2].replace(ESCAPE_REGEX, (m, escape, chr) => escape ? unescape(escape) : chr)); + } else { + throw new Error(`Invalid Chalk template style argument: ${chunk} (in style '${name}')`); + } + } + + return results; +} + +function parseStyle(style) { + STYLE_REGEX.lastIndex = 0; + + const results = []; + let matches; + + while ((matches = STYLE_REGEX.exec(style)) !== null) { + const name = matches[1]; + + if (matches[2]) { + const args = parseArguments(name, matches[2]); + results.push([name].concat(args)); + } else { + results.push([name]); + } + } + + return results; +} + +function buildStyle(chalk, styles) { + const enabled = {}; + + for (const layer of styles) { + for (const style of layer.styles) { + enabled[style[0]] = layer.inverse ? null : style.slice(1); + } + } + + let current = chalk; + for (const styleName of Object.keys(enabled)) { + if (Array.isArray(enabled[styleName])) { + if (!(styleName in current)) { + throw new Error(`Unknown Chalk style: ${styleName}`); + } + + if (enabled[styleName].length > 0) { + current = current[styleName].apply(current, enabled[styleName]); + } else { + current = current[styleName]; + } + } + } + + return current; +} + +module.exports = (chalk, tmp) => { + const styles = []; + const chunks = []; + let chunk = []; + + // eslint-disable-next-line max-params + tmp.replace(TEMPLATE_REGEX, (m, escapeChar, inverse, style, close, chr) => { + if (escapeChar) { + chunk.push(unescape(escapeChar)); + } else if (style) { + const str = chunk.join(''); + chunk = []; + chunks.push(styles.length === 0 ? str : buildStyle(chalk, styles)(str)); + styles.push({inverse, styles: parseStyle(style)}); + } else if (close) { + if (styles.length === 0) { + throw new Error('Found extraneous } in Chalk template literal'); + } + + chunks.push(buildStyle(chalk, styles)(chunk.join(''))); + chunk = []; + styles.pop(); + } else { + chunk.push(chr); + } + }); + + chunks.push(chunk.join('')); + + if (styles.length > 0) { + const errMsg = `Chalk template literal is missing ${styles.length} closing bracket${styles.length === 1 ? '' : 's'} (\`}\`)`; + throw new Error(errMsg); + } + + return chunks.join(''); +}; + + +/***/ }), +/* 372 */ +/***/ (function(module, exports, __webpack_require__) { + +module.exports = normalize + +var fixer = __webpack_require__(373) +normalize.fixer = fixer + +var makeWarning = __webpack_require__(400) + +var fieldsToFix = ['name','version','description','repository','modules','scripts' + ,'files','bin','man','bugs','keywords','readme','homepage','license'] +var otherThingsToFix = ['dependencies','people', 'typos'] + +var thingsToFix = fieldsToFix.map(function(fieldName) { + return ucFirst(fieldName) + "Field" +}) +// two ways to do this in CoffeeScript on only one line, sub-70 chars: +// thingsToFix = fieldsToFix.map (name) -> ucFirst(name) + "Field" +// thingsToFix = (ucFirst(name) + "Field" for name in fieldsToFix) +thingsToFix = thingsToFix.concat(otherThingsToFix) + +function normalize (data, warn, strict) { + if(warn === true) warn = null, strict = true + if(!strict) strict = false + if(!warn || data.private) warn = function(msg) { /* noop */ } + + if (data.scripts && + data.scripts.install === "node-gyp rebuild" && + !data.scripts.preinstall) { + data.gypfile = true + } + fixer.warn = function() { warn(makeWarning.apply(null, arguments)) } + thingsToFix.forEach(function(thingName) { + fixer["fix" + ucFirst(thingName)](data, strict) + }) + data._id = data.name + "@" + data.version +} + +function ucFirst (string) { + return string.charAt(0).toUpperCase() + string.slice(1); +} + + +/***/ }), +/* 373 */ +/***/ (function(module, exports, __webpack_require__) { + +var semver = __webpack_require__(374) +var validateLicense = __webpack_require__(375); +var hostedGitInfo = __webpack_require__(380) +var isBuiltinModule = __webpack_require__(383).isCore +var depTypes = ["dependencies","devDependencies","optionalDependencies"] +var extractDescription = __webpack_require__(398) +var url = __webpack_require__(203) +var typos = __webpack_require__(399) + +var fixer = module.exports = { + // default warning function + warn: function() {}, + + fixRepositoryField: function(data) { + if (data.repositories) { + this.warn("repositories"); + data.repository = data.repositories[0] + } + if (!data.repository) return this.warn("missingRepository") + if (typeof data.repository === "string") { + data.repository = { + type: "git", + url: data.repository + } + } + var r = data.repository.url || "" + if (r) { + var hosted = hostedGitInfo.fromUrl(r) + if (hosted) { + r = data.repository.url + = hosted.getDefaultRepresentation() == "shortcut" ? hosted.https() : hosted.toString() + } + } + + if (r.match(/github.com\/[^\/]+\/[^\/]+\.git\.git$/)) { + this.warn("brokenGitUrl", r) + } + } + +, fixTypos: function(data) { + Object.keys(typos.topLevel).forEach(function (d) { + if (data.hasOwnProperty(d)) { + this.warn("typo", d, typos.topLevel[d]) + } + }, this) + } + +, fixScriptsField: function(data) { + if (!data.scripts) return + if (typeof data.scripts !== "object") { + this.warn("nonObjectScripts") + delete data.scripts + return + } + Object.keys(data.scripts).forEach(function (k) { + if (typeof data.scripts[k] !== "string") { + this.warn("nonStringScript") + delete data.scripts[k] + } else if (typos.script[k] && !data.scripts[typos.script[k]]) { + this.warn("typo", k, typos.script[k], "scripts") + } + }, this) + } + +, fixFilesField: function(data) { + var files = data.files + if (files && !Array.isArray(files)) { + this.warn("nonArrayFiles") + delete data.files + } else if (data.files) { + data.files = data.files.filter(function(file) { + if (!file || typeof file !== "string") { + this.warn("invalidFilename", file) + return false + } else { + return true + } + }, this) + } + } + +, fixBinField: function(data) { + if (!data.bin) return; + if (typeof data.bin === "string") { + var b = {} + var match + if (match = data.name.match(/^@[^/]+[/](.*)$/)) { + b[match[1]] = data.bin + } else { + b[data.name] = data.bin + } + data.bin = b + } + } + +, fixManField: function(data) { + if (!data.man) return; + if (typeof data.man === "string") { + data.man = [ data.man ] + } + } +, fixBundleDependenciesField: function(data) { + var bdd = "bundledDependencies" + var bd = "bundleDependencies" + if (data[bdd] && !data[bd]) { + data[bd] = data[bdd] + delete data[bdd] + } + if (data[bd] && !Array.isArray(data[bd])) { + this.warn("nonArrayBundleDependencies") + delete data[bd] + } else if (data[bd]) { + data[bd] = data[bd].filter(function(bd) { + if (!bd || typeof bd !== 'string') { + this.warn("nonStringBundleDependency", bd) + return false + } else { + if (!data.dependencies) { + data.dependencies = {} + } + if (!data.dependencies.hasOwnProperty(bd)) { + this.warn("nonDependencyBundleDependency", bd) + data.dependencies[bd] = "*" + } + return true + } + }, this) + } + } + +, fixDependencies: function(data, strict) { + var loose = !strict + objectifyDeps(data, this.warn) + addOptionalDepsToDeps(data, this.warn) + this.fixBundleDependenciesField(data) + + ;['dependencies','devDependencies'].forEach(function(deps) { + if (!(deps in data)) return + if (!data[deps] || typeof data[deps] !== "object") { + this.warn("nonObjectDependencies", deps) + delete data[deps] + return + } + Object.keys(data[deps]).forEach(function (d) { + var r = data[deps][d] + if (typeof r !== 'string') { + this.warn("nonStringDependency", d, JSON.stringify(r)) + delete data[deps][d] + } + var hosted = hostedGitInfo.fromUrl(data[deps][d]) + if (hosted) data[deps][d] = hosted.toString() + }, this) + }, this) + } + +, fixModulesField: function (data) { + if (data.modules) { + this.warn("deprecatedModules") + delete data.modules + } + } + +, fixKeywordsField: function (data) { + if (typeof data.keywords === "string") { + data.keywords = data.keywords.split(/,\s+/) + } + if (data.keywords && !Array.isArray(data.keywords)) { + delete data.keywords + this.warn("nonArrayKeywords") + } else if (data.keywords) { + data.keywords = data.keywords.filter(function(kw) { + if (typeof kw !== "string" || !kw) { + this.warn("nonStringKeyword"); + return false + } else { + return true + } + }, this) + } + } + +, fixVersionField: function(data, strict) { + // allow "loose" semver 1.0 versions in non-strict mode + // enforce strict semver 2.0 compliance in strict mode + var loose = !strict + if (!data.version) { + data.version = "" + return true + } + if (!semver.valid(data.version, loose)) { + throw new Error('Invalid version: "'+ data.version + '"') + } + data.version = semver.clean(data.version, loose) + return true + } + +, fixPeople: function(data) { + modifyPeople(data, unParsePerson) + modifyPeople(data, parsePerson) + } + +, fixNameField: function(data, options) { + if (typeof options === "boolean") options = {strict: options} + else if (typeof options === "undefined") options = {} + var strict = options.strict + if (!data.name && !strict) { + data.name = "" + return + } + if (typeof data.name !== "string") { + throw new Error("name field must be a string.") + } + if (!strict) + data.name = data.name.trim() + ensureValidName(data.name, strict, options.allowLegacyCase) + if (isBuiltinModule(data.name)) + this.warn("conflictingName", data.name) + } + + +, fixDescriptionField: function (data) { + if (data.description && typeof data.description !== 'string') { + this.warn("nonStringDescription") + delete data.description + } + if (data.readme && !data.description) + data.description = extractDescription(data.readme) + if(data.description === undefined) delete data.description; + if (!data.description) this.warn("missingDescription") + } + +, fixReadmeField: function (data) { + if (!data.readme) { + this.warn("missingReadme") + data.readme = "ERROR: No README data found!" + } + } + +, fixBugsField: function(data) { + if (!data.bugs && data.repository && data.repository.url) { + var hosted = hostedGitInfo.fromUrl(data.repository.url) + if(hosted && hosted.bugs()) { + data.bugs = {url: hosted.bugs()} + } + } + else if(data.bugs) { + var emailRe = /^.+@.*\..+$/ + if(typeof data.bugs == "string") { + if(emailRe.test(data.bugs)) + data.bugs = {email:data.bugs} + else if(url.parse(data.bugs).protocol) + data.bugs = {url: data.bugs} + else + this.warn("nonEmailUrlBugsString") + } + else { + bugsTypos(data.bugs, this.warn) + var oldBugs = data.bugs + data.bugs = {} + if(oldBugs.url) { + if(typeof(oldBugs.url) == "string" && url.parse(oldBugs.url).protocol) + data.bugs.url = oldBugs.url + else + this.warn("nonUrlBugsUrlField") + } + if(oldBugs.email) { + if(typeof(oldBugs.email) == "string" && emailRe.test(oldBugs.email)) + data.bugs.email = oldBugs.email + else + this.warn("nonEmailBugsEmailField") + } + } + if(!data.bugs.email && !data.bugs.url) { + delete data.bugs + this.warn("emptyNormalizedBugs") + } + } + } + +, fixHomepageField: function(data) { + if (!data.homepage && data.repository && data.repository.url) { + var hosted = hostedGitInfo.fromUrl(data.repository.url) + if (hosted && hosted.docs()) data.homepage = hosted.docs() + } + if (!data.homepage) return + + if(typeof data.homepage !== "string") { + this.warn("nonUrlHomepage") + return delete data.homepage + } + if(!url.parse(data.homepage).protocol) { + data.homepage = "http://" + data.homepage + } + } + +, fixLicenseField: function(data) { + if (!data.license) { + return this.warn("missingLicense") + } else{ + if ( + typeof(data.license) !== 'string' || + data.license.length < 1 || + data.license.trim() === '' + ) { + this.warn("invalidLicense") + } else { + if (!validateLicense(data.license).validForNewPackages) + this.warn("invalidLicense") + } + } + } +} + +function isValidScopedPackageName(spec) { + if (spec.charAt(0) !== '@') return false + + var rest = spec.slice(1).split('/') + if (rest.length !== 2) return false + + return rest[0] && rest[1] && + rest[0] === encodeURIComponent(rest[0]) && + rest[1] === encodeURIComponent(rest[1]) +} + +function isCorrectlyEncodedName(spec) { + return !spec.match(/[\/@\s\+%:]/) && + spec === encodeURIComponent(spec) +} + +function ensureValidName (name, strict, allowLegacyCase) { + if (name.charAt(0) === "." || + !(isValidScopedPackageName(name) || isCorrectlyEncodedName(name)) || + (strict && (!allowLegacyCase) && name !== name.toLowerCase()) || + name.toLowerCase() === "node_modules" || + name.toLowerCase() === "favicon.ico") { + throw new Error("Invalid name: " + JSON.stringify(name)) + } +} + +function modifyPeople (data, fn) { + if (data.author) data.author = fn(data.author) + ;["maintainers", "contributors"].forEach(function (set) { + if (!Array.isArray(data[set])) return; + data[set] = data[set].map(fn) + }) + return data +} + +function unParsePerson (person) { + if (typeof person === "string") return person + var name = person.name || "" + var u = person.url || person.web + var url = u ? (" ("+u+")") : "" + var e = person.email || person.mail + var email = e ? (" <"+e+">") : "" + return name+email+url +} + +function parsePerson (person) { + if (typeof person !== "string") return person + var name = person.match(/^([^\(<]+)/) + var url = person.match(/\(([^\)]+)\)/) + var email = person.match(/<([^>]+)>/) + var obj = {} + if (name && name[0].trim()) obj.name = name[0].trim() + if (email) obj.email = email[1]; + if (url) obj.url = url[1]; + return obj +} + +function addOptionalDepsToDeps (data, warn) { + var o = data.optionalDependencies + if (!o) return; + var d = data.dependencies || {} + Object.keys(o).forEach(function (k) { + d[k] = o[k] + }) + data.dependencies = d +} + +function depObjectify (deps, type, warn) { + if (!deps) return {} + if (typeof deps === "string") { + deps = deps.trim().split(/[\n\r\s\t ,]+/) + } + if (!Array.isArray(deps)) return deps + warn("deprecatedArrayDependencies", type) + var o = {} + deps.filter(function (d) { + return typeof d === "string" + }).forEach(function(d) { + d = d.trim().split(/(:?[@\s><=])/) + var dn = d.shift() + var dv = d.join("") + dv = dv.trim() + dv = dv.replace(/^@/, "") + o[dn] = dv + }) + return o +} + +function objectifyDeps (data, warn) { + depTypes.forEach(function (type) { + if (!data[type]) return; + data[type] = depObjectify(data[type], type, warn) + }) +} + +function bugsTypos(bugs, warn) { + if (!bugs) return + Object.keys(bugs).forEach(function (k) { + if (typos.bugs[k]) { + warn("typo", k, typos.bugs[k], "bugs") + bugs[typos.bugs[k]] = bugs[k] + delete bugs[k] + } + }) +} + + +/***/ }), +/* 374 */ +/***/ (function(module, exports) { + +exports = module.exports = SemVer + +var debug +/* istanbul ignore next */ +if (typeof process === 'object' && + process.env && + process.env.NODE_DEBUG && + /\bsemver\b/i.test(process.env.NODE_DEBUG)) { + debug = function () { + var args = Array.prototype.slice.call(arguments, 0) + args.unshift('SEMVER') + console.log.apply(console, args) + } +} else { + debug = function () {} +} + +// Note: this is the semver.org version of the spec that it implements +// Not necessarily the package version of this code. +exports.SEMVER_SPEC_VERSION = '2.0.0' + +var MAX_LENGTH = 256 +var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || + /* istanbul ignore next */ 9007199254740991 + +// Max safe segment length for coercion. +var MAX_SAFE_COMPONENT_LENGTH = 16 + +// The actual regexps go on exports.re +var re = exports.re = [] +var src = exports.src = [] +var R = 0 + +// The following Regular Expressions can be used for tokenizing, +// validating, and parsing SemVer version strings. + +// ## Numeric Identifier +// A single `0`, or a non-zero digit followed by zero or more digits. + +var NUMERICIDENTIFIER = R++ +src[NUMERICIDENTIFIER] = '0|[1-9]\\d*' +var NUMERICIDENTIFIERLOOSE = R++ +src[NUMERICIDENTIFIERLOOSE] = '[0-9]+' + +// ## Non-numeric Identifier +// Zero or more digits, followed by a letter or hyphen, and then zero or +// more letters, digits, or hyphens. + +var NONNUMERICIDENTIFIER = R++ +src[NONNUMERICIDENTIFIER] = '\\d*[a-zA-Z-][a-zA-Z0-9-]*' + +// ## Main Version +// Three dot-separated numeric identifiers. + +var MAINVERSION = R++ +src[MAINVERSION] = '(' + src[NUMERICIDENTIFIER] + ')\\.' + + '(' + src[NUMERICIDENTIFIER] + ')\\.' + + '(' + src[NUMERICIDENTIFIER] + ')' + +var MAINVERSIONLOOSE = R++ +src[MAINVERSIONLOOSE] = '(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' + + '(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' + + '(' + src[NUMERICIDENTIFIERLOOSE] + ')' + +// ## Pre-release Version Identifier +// A numeric identifier, or a non-numeric identifier. + +var PRERELEASEIDENTIFIER = R++ +src[PRERELEASEIDENTIFIER] = '(?:' + src[NUMERICIDENTIFIER] + + '|' + src[NONNUMERICIDENTIFIER] + ')' + +var PRERELEASEIDENTIFIERLOOSE = R++ +src[PRERELEASEIDENTIFIERLOOSE] = '(?:' + src[NUMERICIDENTIFIERLOOSE] + + '|' + src[NONNUMERICIDENTIFIER] + ')' + +// ## Pre-release Version +// Hyphen, followed by one or more dot-separated pre-release version +// identifiers. + +var PRERELEASE = R++ +src[PRERELEASE] = '(?:-(' + src[PRERELEASEIDENTIFIER] + + '(?:\\.' + src[PRERELEASEIDENTIFIER] + ')*))' + +var PRERELEASELOOSE = R++ +src[PRERELEASELOOSE] = '(?:-?(' + src[PRERELEASEIDENTIFIERLOOSE] + + '(?:\\.' + src[PRERELEASEIDENTIFIERLOOSE] + ')*))' + +// ## Build Metadata Identifier +// Any combination of digits, letters, or hyphens. + +var BUILDIDENTIFIER = R++ +src[BUILDIDENTIFIER] = '[0-9A-Za-z-]+' + +// ## Build Metadata +// Plus sign, followed by one or more period-separated build metadata +// identifiers. + +var BUILD = R++ +src[BUILD] = '(?:\\+(' + src[BUILDIDENTIFIER] + + '(?:\\.' + src[BUILDIDENTIFIER] + ')*))' + +// ## Full Version String +// A main version, followed optionally by a pre-release version and +// build metadata. + +// Note that the only major, minor, patch, and pre-release sections of +// the version string are capturing groups. The build metadata is not a +// capturing group, because it should not ever be used in version +// comparison. + +var FULL = R++ +var FULLPLAIN = 'v?' + src[MAINVERSION] + + src[PRERELEASE] + '?' + + src[BUILD] + '?' + +src[FULL] = '^' + FULLPLAIN + '$' + +// like full, but allows v1.2.3 and =1.2.3, which people do sometimes. +// also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty +// common in the npm registry. +var LOOSEPLAIN = '[v=\\s]*' + src[MAINVERSIONLOOSE] + + src[PRERELEASELOOSE] + '?' + + src[BUILD] + '?' + +var LOOSE = R++ +src[LOOSE] = '^' + LOOSEPLAIN + '$' + +var GTLT = R++ +src[GTLT] = '((?:<|>)?=?)' + +// Something like "2.*" or "1.2.x". +// Note that "x.x" is a valid xRange identifer, meaning "any version" +// Only the first item is strictly required. +var XRANGEIDENTIFIERLOOSE = R++ +src[XRANGEIDENTIFIERLOOSE] = src[NUMERICIDENTIFIERLOOSE] + '|x|X|\\*' +var XRANGEIDENTIFIER = R++ +src[XRANGEIDENTIFIER] = src[NUMERICIDENTIFIER] + '|x|X|\\*' + +var XRANGEPLAIN = R++ +src[XRANGEPLAIN] = '[v=\\s]*(' + src[XRANGEIDENTIFIER] + ')' + + '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' + + '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' + + '(?:' + src[PRERELEASE] + ')?' + + src[BUILD] + '?' + + ')?)?' + +var XRANGEPLAINLOOSE = R++ +src[XRANGEPLAINLOOSE] = '[v=\\s]*(' + src[XRANGEIDENTIFIERLOOSE] + ')' + + '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' + + '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' + + '(?:' + src[PRERELEASELOOSE] + ')?' + + src[BUILD] + '?' + + ')?)?' + +var XRANGE = R++ +src[XRANGE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAIN] + '$' +var XRANGELOOSE = R++ +src[XRANGELOOSE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAINLOOSE] + '$' + +// Coercion. +// Extract anything that could conceivably be a part of a valid semver +var COERCE = R++ +src[COERCE] = '(?:^|[^\\d])' + + '(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '})' + + '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + + '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + + '(?:$|[^\\d])' + +// Tilde ranges. +// Meaning is "reasonably at or greater than" +var LONETILDE = R++ +src[LONETILDE] = '(?:~>?)' + +var TILDETRIM = R++ +src[TILDETRIM] = '(\\s*)' + src[LONETILDE] + '\\s+' +re[TILDETRIM] = new RegExp(src[TILDETRIM], 'g') +var tildeTrimReplace = '$1~' + +var TILDE = R++ +src[TILDE] = '^' + src[LONETILDE] + src[XRANGEPLAIN] + '$' +var TILDELOOSE = R++ +src[TILDELOOSE] = '^' + src[LONETILDE] + src[XRANGEPLAINLOOSE] + '$' + +// Caret ranges. +// Meaning is "at least and backwards compatible with" +var LONECARET = R++ +src[LONECARET] = '(?:\\^)' + +var CARETTRIM = R++ +src[CARETTRIM] = '(\\s*)' + src[LONECARET] + '\\s+' +re[CARETTRIM] = new RegExp(src[CARETTRIM], 'g') +var caretTrimReplace = '$1^' + +var CARET = R++ +src[CARET] = '^' + src[LONECARET] + src[XRANGEPLAIN] + '$' +var CARETLOOSE = R++ +src[CARETLOOSE] = '^' + src[LONECARET] + src[XRANGEPLAINLOOSE] + '$' + +// A simple gt/lt/eq thing, or just "" to indicate "any version" +var COMPARATORLOOSE = R++ +src[COMPARATORLOOSE] = '^' + src[GTLT] + '\\s*(' + LOOSEPLAIN + ')$|^$' +var COMPARATOR = R++ +src[COMPARATOR] = '^' + src[GTLT] + '\\s*(' + FULLPLAIN + ')$|^$' + +// An expression to strip any whitespace between the gtlt and the thing +// it modifies, so that `> 1.2.3` ==> `>1.2.3` +var COMPARATORTRIM = R++ +src[COMPARATORTRIM] = '(\\s*)' + src[GTLT] + + '\\s*(' + LOOSEPLAIN + '|' + src[XRANGEPLAIN] + ')' + +// this one has to use the /g flag +re[COMPARATORTRIM] = new RegExp(src[COMPARATORTRIM], 'g') +var comparatorTrimReplace = '$1$2$3' + +// Something like `1.2.3 - 1.2.4` +// Note that these all use the loose form, because they'll be +// checked against either the strict or loose comparator form +// later. +var HYPHENRANGE = R++ +src[HYPHENRANGE] = '^\\s*(' + src[XRANGEPLAIN] + ')' + + '\\s+-\\s+' + + '(' + src[XRANGEPLAIN] + ')' + + '\\s*$' + +var HYPHENRANGELOOSE = R++ +src[HYPHENRANGELOOSE] = '^\\s*(' + src[XRANGEPLAINLOOSE] + ')' + + '\\s+-\\s+' + + '(' + src[XRANGEPLAINLOOSE] + ')' + + '\\s*$' + +// Star ranges basically just allow anything at all. +var STAR = R++ +src[STAR] = '(<|>)?=?\\s*\\*' + +// Compile to actual regexp objects. +// All are flag-free, unless they were created above with a flag. +for (var i = 0; i < R; i++) { + debug(i, src[i]) + if (!re[i]) { + re[i] = new RegExp(src[i]) + } +} + +exports.parse = parse +function parse (version, options) { + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false + } + } + + if (version instanceof SemVer) { + return version + } + + if (typeof version !== 'string') { + return null + } + + if (version.length > MAX_LENGTH) { + return null + } + + var r = options.loose ? re[LOOSE] : re[FULL] + if (!r.test(version)) { + return null + } + + try { + return new SemVer(version, options) + } catch (er) { + return null + } +} + +exports.valid = valid +function valid (version, options) { + var v = parse(version, options) + return v ? v.version : null +} + +exports.clean = clean +function clean (version, options) { + var s = parse(version.trim().replace(/^[=v]+/, ''), options) + return s ? s.version : null +} + +exports.SemVer = SemVer + +function SemVer (version, options) { + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false + } + } + if (version instanceof SemVer) { + if (version.loose === options.loose) { + return version + } else { + version = version.version + } + } else if (typeof version !== 'string') { + throw new TypeError('Invalid Version: ' + version) + } + + if (version.length > MAX_LENGTH) { + throw new TypeError('version is longer than ' + MAX_LENGTH + ' characters') + } + + if (!(this instanceof SemVer)) { + return new SemVer(version, options) + } + + debug('SemVer', version, options) + this.options = options + this.loose = !!options.loose + + var m = version.trim().match(options.loose ? re[LOOSE] : re[FULL]) + + if (!m) { + throw new TypeError('Invalid Version: ' + version) + } + + this.raw = version + + // these are actually numbers + this.major = +m[1] + this.minor = +m[2] + this.patch = +m[3] + + if (this.major > MAX_SAFE_INTEGER || this.major < 0) { + throw new TypeError('Invalid major version') + } + + if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) { + throw new TypeError('Invalid minor version') + } + + if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) { + throw new TypeError('Invalid patch version') + } + + // numberify any prerelease numeric ids + if (!m[4]) { + this.prerelease = [] + } else { + this.prerelease = m[4].split('.').map(function (id) { + if (/^[0-9]+$/.test(id)) { + var num = +id + if (num >= 0 && num < MAX_SAFE_INTEGER) { + return num + } + } + return id + }) + } + + this.build = m[5] ? m[5].split('.') : [] + this.format() +} + +SemVer.prototype.format = function () { + this.version = this.major + '.' + this.minor + '.' + this.patch + if (this.prerelease.length) { + this.version += '-' + this.prerelease.join('.') + } + return this.version +} + +SemVer.prototype.toString = function () { + return this.version +} + +SemVer.prototype.compare = function (other) { + debug('SemVer.compare', this.version, this.options, other) + if (!(other instanceof SemVer)) { + other = new SemVer(other, this.options) + } + + return this.compareMain(other) || this.comparePre(other) +} + +SemVer.prototype.compareMain = function (other) { + if (!(other instanceof SemVer)) { + other = new SemVer(other, this.options) + } + + return compareIdentifiers(this.major, other.major) || + compareIdentifiers(this.minor, other.minor) || + compareIdentifiers(this.patch, other.patch) +} + +SemVer.prototype.comparePre = function (other) { + if (!(other instanceof SemVer)) { + other = new SemVer(other, this.options) + } + + // NOT having a prerelease is > having one + if (this.prerelease.length && !other.prerelease.length) { + return -1 + } else if (!this.prerelease.length && other.prerelease.length) { + return 1 + } else if (!this.prerelease.length && !other.prerelease.length) { + return 0 + } + + var i = 0 + do { + var a = this.prerelease[i] + var b = other.prerelease[i] + debug('prerelease compare', i, a, b) + if (a === undefined && b === undefined) { + return 0 + } else if (b === undefined) { + return 1 + } else if (a === undefined) { + return -1 + } else if (a === b) { + continue + } else { + return compareIdentifiers(a, b) + } + } while (++i) +} + +// preminor will bump the version up to the next minor release, and immediately +// down to pre-release. premajor and prepatch work the same way. +SemVer.prototype.inc = function (release, identifier) { + switch (release) { + case 'premajor': + this.prerelease.length = 0 + this.patch = 0 + this.minor = 0 + this.major++ + this.inc('pre', identifier) + break + case 'preminor': + this.prerelease.length = 0 + this.patch = 0 + this.minor++ + this.inc('pre', identifier) + break + case 'prepatch': + // If this is already a prerelease, it will bump to the next version + // drop any prereleases that might already exist, since they are not + // relevant at this point. + this.prerelease.length = 0 + this.inc('patch', identifier) + this.inc('pre', identifier) + break + // If the input is a non-prerelease version, this acts the same as + // prepatch. + case 'prerelease': + if (this.prerelease.length === 0) { + this.inc('patch', identifier) + } + this.inc('pre', identifier) + break + + case 'major': + // If this is a pre-major version, bump up to the same major version. + // Otherwise increment major. + // 1.0.0-5 bumps to 1.0.0 + // 1.1.0 bumps to 2.0.0 + if (this.minor !== 0 || + this.patch !== 0 || + this.prerelease.length === 0) { + this.major++ + } + this.minor = 0 + this.patch = 0 + this.prerelease = [] + break + case 'minor': + // If this is a pre-minor version, bump up to the same minor version. + // Otherwise increment minor. + // 1.2.0-5 bumps to 1.2.0 + // 1.2.1 bumps to 1.3.0 + if (this.patch !== 0 || this.prerelease.length === 0) { + this.minor++ + } + this.patch = 0 + this.prerelease = [] + break + case 'patch': + // If this is not a pre-release version, it will increment the patch. + // If it is a pre-release it will bump up to the same patch version. + // 1.2.0-5 patches to 1.2.0 + // 1.2.0 patches to 1.2.1 + if (this.prerelease.length === 0) { + this.patch++ + } + this.prerelease = [] + break + // This probably shouldn't be used publicly. + // 1.0.0 "pre" would become 1.0.0-0 which is the wrong direction. + case 'pre': + if (this.prerelease.length === 0) { + this.prerelease = [0] + } else { + var i = this.prerelease.length + while (--i >= 0) { + if (typeof this.prerelease[i] === 'number') { + this.prerelease[i]++ + i = -2 + } + } + if (i === -1) { + // didn't increment anything + this.prerelease.push(0) + } + } + if (identifier) { + // 1.2.0-beta.1 bumps to 1.2.0-beta.2, + // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0 + if (this.prerelease[0] === identifier) { + if (isNaN(this.prerelease[1])) { + this.prerelease = [identifier, 0] + } + } else { + this.prerelease = [identifier, 0] + } + } + break + + default: + throw new Error('invalid increment argument: ' + release) + } + this.format() + this.raw = this.version + return this +} + +exports.inc = inc +function inc (version, release, loose, identifier) { + if (typeof (loose) === 'string') { + identifier = loose + loose = undefined + } + + try { + return new SemVer(version, loose).inc(release, identifier).version + } catch (er) { + return null + } +} + +exports.diff = diff +function diff (version1, version2) { + if (eq(version1, version2)) { + return null + } else { + var v1 = parse(version1) + var v2 = parse(version2) + var prefix = '' + if (v1.prerelease.length || v2.prerelease.length) { + prefix = 'pre' + var defaultResult = 'prerelease' + } + for (var key in v1) { + if (key === 'major' || key === 'minor' || key === 'patch') { + if (v1[key] !== v2[key]) { + return prefix + key + } + } + } + return defaultResult // may be undefined + } +} + +exports.compareIdentifiers = compareIdentifiers + +var numeric = /^[0-9]+$/ +function compareIdentifiers (a, b) { + var anum = numeric.test(a) + var bnum = numeric.test(b) + + if (anum && bnum) { + a = +a + b = +b + } + + return a === b ? 0 + : (anum && !bnum) ? -1 + : (bnum && !anum) ? 1 + : a < b ? -1 + : 1 +} + +exports.rcompareIdentifiers = rcompareIdentifiers +function rcompareIdentifiers (a, b) { + return compareIdentifiers(b, a) +} + +exports.major = major +function major (a, loose) { + return new SemVer(a, loose).major +} + +exports.minor = minor +function minor (a, loose) { + return new SemVer(a, loose).minor +} + +exports.patch = patch +function patch (a, loose) { + return new SemVer(a, loose).patch +} + +exports.compare = compare +function compare (a, b, loose) { + return new SemVer(a, loose).compare(new SemVer(b, loose)) +} + +exports.compareLoose = compareLoose +function compareLoose (a, b) { + return compare(a, b, true) +} + +exports.rcompare = rcompare +function rcompare (a, b, loose) { + return compare(b, a, loose) +} + +exports.sort = sort +function sort (list, loose) { + return list.sort(function (a, b) { + return exports.compare(a, b, loose) + }) +} + +exports.rsort = rsort +function rsort (list, loose) { + return list.sort(function (a, b) { + return exports.rcompare(a, b, loose) + }) +} + +exports.gt = gt +function gt (a, b, loose) { + return compare(a, b, loose) > 0 +} + +exports.lt = lt +function lt (a, b, loose) { + return compare(a, b, loose) < 0 +} + +exports.eq = eq +function eq (a, b, loose) { + return compare(a, b, loose) === 0 +} + +exports.neq = neq +function neq (a, b, loose) { + return compare(a, b, loose) !== 0 +} + +exports.gte = gte +function gte (a, b, loose) { + return compare(a, b, loose) >= 0 +} + +exports.lte = lte +function lte (a, b, loose) { + return compare(a, b, loose) <= 0 +} + +exports.cmp = cmp +function cmp (a, op, b, loose) { + switch (op) { + case '===': + if (typeof a === 'object') + a = a.version + if (typeof b === 'object') + b = b.version + return a === b + + case '!==': + if (typeof a === 'object') + a = a.version + if (typeof b === 'object') + b = b.version + return a !== b + + case '': + case '=': + case '==': + return eq(a, b, loose) + + case '!=': + return neq(a, b, loose) + + case '>': + return gt(a, b, loose) + + case '>=': + return gte(a, b, loose) + + case '<': + return lt(a, b, loose) + + case '<=': + return lte(a, b, loose) + + default: + throw new TypeError('Invalid operator: ' + op) + } +} + +exports.Comparator = Comparator +function Comparator (comp, options) { + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false + } + } + + if (comp instanceof Comparator) { + if (comp.loose === !!options.loose) { + return comp + } else { + comp = comp.value + } + } + + if (!(this instanceof Comparator)) { + return new Comparator(comp, options) + } + + debug('comparator', comp, options) + this.options = options + this.loose = !!options.loose + this.parse(comp) + + if (this.semver === ANY) { + this.value = '' + } else { + this.value = this.operator + this.semver.version + } + + debug('comp', this) +} + +var ANY = {} +Comparator.prototype.parse = function (comp) { + var r = this.options.loose ? re[COMPARATORLOOSE] : re[COMPARATOR] + var m = comp.match(r) + + if (!m) { + throw new TypeError('Invalid comparator: ' + comp) + } + + this.operator = m[1] + if (this.operator === '=') { + this.operator = '' + } + + // if it literally is just '>' or '' then allow anything. + if (!m[2]) { + this.semver = ANY + } else { + this.semver = new SemVer(m[2], this.options.loose) + } +} + +Comparator.prototype.toString = function () { + return this.value +} + +Comparator.prototype.test = function (version) { + debug('Comparator.test', version, this.options.loose) + + if (this.semver === ANY) { + return true + } + + if (typeof version === 'string') { + version = new SemVer(version, this.options) + } + + return cmp(version, this.operator, this.semver, this.options) +} + +Comparator.prototype.intersects = function (comp, options) { + if (!(comp instanceof Comparator)) { + throw new TypeError('a Comparator is required') + } + + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false + } + } + + var rangeTmp + + if (this.operator === '') { + rangeTmp = new Range(comp.value, options) + return satisfies(this.value, rangeTmp, options) + } else if (comp.operator === '') { + rangeTmp = new Range(this.value, options) + return satisfies(comp.semver, rangeTmp, options) + } + + var sameDirectionIncreasing = + (this.operator === '>=' || this.operator === '>') && + (comp.operator === '>=' || comp.operator === '>') + var sameDirectionDecreasing = + (this.operator === '<=' || this.operator === '<') && + (comp.operator === '<=' || comp.operator === '<') + var sameSemVer = this.semver.version === comp.semver.version + var differentDirectionsInclusive = + (this.operator === '>=' || this.operator === '<=') && + (comp.operator === '>=' || comp.operator === '<=') + var oppositeDirectionsLessThan = + cmp(this.semver, '<', comp.semver, options) && + ((this.operator === '>=' || this.operator === '>') && + (comp.operator === '<=' || comp.operator === '<')) + var oppositeDirectionsGreaterThan = + cmp(this.semver, '>', comp.semver, options) && + ((this.operator === '<=' || this.operator === '<') && + (comp.operator === '>=' || comp.operator === '>')) + + return sameDirectionIncreasing || sameDirectionDecreasing || + (sameSemVer && differentDirectionsInclusive) || + oppositeDirectionsLessThan || oppositeDirectionsGreaterThan +} + +exports.Range = Range +function Range (range, options) { + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false + } + } + + if (range instanceof Range) { + if (range.loose === !!options.loose && + range.includePrerelease === !!options.includePrerelease) { + return range + } else { + return new Range(range.raw, options) + } + } + + if (range instanceof Comparator) { + return new Range(range.value, options) + } + + if (!(this instanceof Range)) { + return new Range(range, options) + } + + this.options = options + this.loose = !!options.loose + this.includePrerelease = !!options.includePrerelease + + // First, split based on boolean or || + this.raw = range + this.set = range.split(/\s*\|\|\s*/).map(function (range) { + return this.parseRange(range.trim()) + }, this).filter(function (c) { + // throw out any that are not relevant for whatever reason + return c.length + }) + + if (!this.set.length) { + throw new TypeError('Invalid SemVer Range: ' + range) + } + + this.format() +} + +Range.prototype.format = function () { + this.range = this.set.map(function (comps) { + return comps.join(' ').trim() + }).join('||').trim() + return this.range +} + +Range.prototype.toString = function () { + return this.range +} + +Range.prototype.parseRange = function (range) { + var loose = this.options.loose + range = range.trim() + // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4` + var hr = loose ? re[HYPHENRANGELOOSE] : re[HYPHENRANGE] + range = range.replace(hr, hyphenReplace) + debug('hyphen replace', range) + // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5` + range = range.replace(re[COMPARATORTRIM], comparatorTrimReplace) + debug('comparator trim', range, re[COMPARATORTRIM]) + + // `~ 1.2.3` => `~1.2.3` + range = range.replace(re[TILDETRIM], tildeTrimReplace) + + // `^ 1.2.3` => `^1.2.3` + range = range.replace(re[CARETTRIM], caretTrimReplace) + + // normalize spaces + range = range.split(/\s+/).join(' ') + + // At this point, the range is completely trimmed and + // ready to be split into comparators. + + var compRe = loose ? re[COMPARATORLOOSE] : re[COMPARATOR] + var set = range.split(' ').map(function (comp) { + return parseComparator(comp, this.options) + }, this).join(' ').split(/\s+/) + if (this.options.loose) { + // in loose mode, throw out any that are not valid comparators + set = set.filter(function (comp) { + return !!comp.match(compRe) + }) + } + set = set.map(function (comp) { + return new Comparator(comp, this.options) + }, this) + + return set +} + +Range.prototype.intersects = function (range, options) { + if (!(range instanceof Range)) { + throw new TypeError('a Range is required') + } + + return this.set.some(function (thisComparators) { + return thisComparators.every(function (thisComparator) { + return range.set.some(function (rangeComparators) { + return rangeComparators.every(function (rangeComparator) { + return thisComparator.intersects(rangeComparator, options) + }) + }) + }) + }) +} + +// Mostly just for testing and legacy API reasons +exports.toComparators = toComparators +function toComparators (range, options) { + return new Range(range, options).set.map(function (comp) { + return comp.map(function (c) { + return c.value + }).join(' ').trim().split(' ') + }) +} + +// comprised of xranges, tildes, stars, and gtlt's at this point. +// already replaced the hyphen ranges +// turn into a set of JUST comparators. +function parseComparator (comp, options) { + debug('comp', comp, options) + comp = replaceCarets(comp, options) + debug('caret', comp) + comp = replaceTildes(comp, options) + debug('tildes', comp) + comp = replaceXRanges(comp, options) + debug('xrange', comp) + comp = replaceStars(comp, options) + debug('stars', comp) + return comp +} + +function isX (id) { + return !id || id.toLowerCase() === 'x' || id === '*' +} + +// ~, ~> --> * (any, kinda silly) +// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0 +// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0 +// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0 +// ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0 +// ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0 +function replaceTildes (comp, options) { + return comp.trim().split(/\s+/).map(function (comp) { + return replaceTilde(comp, options) + }).join(' ') +} + +function replaceTilde (comp, options) { + var r = options.loose ? re[TILDELOOSE] : re[TILDE] + return comp.replace(r, function (_, M, m, p, pr) { + debug('tilde', comp, _, M, m, p, pr) + var ret + + if (isX(M)) { + ret = '' + } else if (isX(m)) { + ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' + } else if (isX(p)) { + // ~1.2 == >=1.2.0 <1.3.0 + ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' + } else if (pr) { + debug('replaceTilde pr', pr) + ret = '>=' + M + '.' + m + '.' + p + '-' + pr + + ' <' + M + '.' + (+m + 1) + '.0' + } else { + // ~1.2.3 == >=1.2.3 <1.3.0 + ret = '>=' + M + '.' + m + '.' + p + + ' <' + M + '.' + (+m + 1) + '.0' + } + + debug('tilde return', ret) + return ret + }) +} + +// ^ --> * (any, kinda silly) +// ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0 +// ^2.0, ^2.0.x --> >=2.0.0 <3.0.0 +// ^1.2, ^1.2.x --> >=1.2.0 <2.0.0 +// ^1.2.3 --> >=1.2.3 <2.0.0 +// ^1.2.0 --> >=1.2.0 <2.0.0 +function replaceCarets (comp, options) { + return comp.trim().split(/\s+/).map(function (comp) { + return replaceCaret(comp, options) + }).join(' ') +} + +function replaceCaret (comp, options) { + debug('caret', comp, options) + var r = options.loose ? re[CARETLOOSE] : re[CARET] + return comp.replace(r, function (_, M, m, p, pr) { + debug('caret', comp, _, M, m, p, pr) + var ret + + if (isX(M)) { + ret = '' + } else if (isX(m)) { + ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' + } else if (isX(p)) { + if (M === '0') { + ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' + } else { + ret = '>=' + M + '.' + m + '.0 <' + (+M + 1) + '.0.0' + } + } else if (pr) { + debug('replaceCaret pr', pr) + if (M === '0') { + if (m === '0') { + ret = '>=' + M + '.' + m + '.' + p + '-' + pr + + ' <' + M + '.' + m + '.' + (+p + 1) + } else { + ret = '>=' + M + '.' + m + '.' + p + '-' + pr + + ' <' + M + '.' + (+m + 1) + '.0' + } + } else { + ret = '>=' + M + '.' + m + '.' + p + '-' + pr + + ' <' + (+M + 1) + '.0.0' + } + } else { + debug('no pr') + if (M === '0') { + if (m === '0') { + ret = '>=' + M + '.' + m + '.' + p + + ' <' + M + '.' + m + '.' + (+p + 1) + } else { + ret = '>=' + M + '.' + m + '.' + p + + ' <' + M + '.' + (+m + 1) + '.0' + } + } else { + ret = '>=' + M + '.' + m + '.' + p + + ' <' + (+M + 1) + '.0.0' + } + } + + debug('caret return', ret) + return ret + }) +} + +function replaceXRanges (comp, options) { + debug('replaceXRanges', comp, options) + return comp.split(/\s+/).map(function (comp) { + return replaceXRange(comp, options) + }).join(' ') +} + +function replaceXRange (comp, options) { + comp = comp.trim() + var r = options.loose ? re[XRANGELOOSE] : re[XRANGE] + return comp.replace(r, function (ret, gtlt, M, m, p, pr) { + debug('xRange', comp, ret, gtlt, M, m, p, pr) + var xM = isX(M) + var xm = xM || isX(m) + var xp = xm || isX(p) + var anyX = xp + + if (gtlt === '=' && anyX) { + gtlt = '' + } + + if (xM) { + if (gtlt === '>' || gtlt === '<') { + // nothing is allowed + ret = '<0.0.0' + } else { + // nothing is forbidden + ret = '*' + } + } else if (gtlt && anyX) { + // we know patch is an x, because we have any x at all. + // replace X with 0 + if (xm) { + m = 0 + } + p = 0 + + if (gtlt === '>') { + // >1 => >=2.0.0 + // >1.2 => >=1.3.0 + // >1.2.3 => >= 1.2.4 + gtlt = '>=' + if (xm) { + M = +M + 1 + m = 0 + p = 0 + } else { + m = +m + 1 + p = 0 + } + } else if (gtlt === '<=') { + // <=0.7.x is actually <0.8.0, since any 0.7.x should + // pass. Similarly, <=7.x is actually <8.0.0, etc. + gtlt = '<' + if (xm) { + M = +M + 1 + } else { + m = +m + 1 + } + } + + ret = gtlt + M + '.' + m + '.' + p + } else if (xm) { + ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' + } else if (xp) { + ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' + } + + debug('xRange return', ret) + + return ret + }) +} + +// Because * is AND-ed with everything else in the comparator, +// and '' means "any version", just remove the *s entirely. +function replaceStars (comp, options) { + debug('replaceStars', comp, options) + // Looseness is ignored here. star is always as loose as it gets! + return comp.trim().replace(re[STAR], '') +} + +// This function is passed to string.replace(re[HYPHENRANGE]) +// M, m, patch, prerelease, build +// 1.2 - 3.4.5 => >=1.2.0 <=3.4.5 +// 1.2.3 - 3.4 => >=1.2.0 <3.5.0 Any 3.4.x will do +// 1.2 - 3.4 => >=1.2.0 <3.5.0 +function hyphenReplace ($0, + from, fM, fm, fp, fpr, fb, + to, tM, tm, tp, tpr, tb) { + if (isX(fM)) { + from = '' + } else if (isX(fm)) { + from = '>=' + fM + '.0.0' + } else if (isX(fp)) { + from = '>=' + fM + '.' + fm + '.0' + } else { + from = '>=' + from + } + + if (isX(tM)) { + to = '' + } else if (isX(tm)) { + to = '<' + (+tM + 1) + '.0.0' + } else if (isX(tp)) { + to = '<' + tM + '.' + (+tm + 1) + '.0' + } else if (tpr) { + to = '<=' + tM + '.' + tm + '.' + tp + '-' + tpr + } else { + to = '<=' + to + } + + return (from + ' ' + to).trim() +} + +// if ANY of the sets match ALL of its comparators, then pass +Range.prototype.test = function (version) { + if (!version) { + return false + } + + if (typeof version === 'string') { + version = new SemVer(version, this.options) + } + + for (var i = 0; i < this.set.length; i++) { + if (testSet(this.set[i], version, this.options)) { + return true + } + } + return false +} + +function testSet (set, version, options) { + for (var i = 0; i < set.length; i++) { + if (!set[i].test(version)) { + return false + } + } + + if (version.prerelease.length && !options.includePrerelease) { + // Find the set of versions that are allowed to have prereleases + // For example, ^1.2.3-pr.1 desugars to >=1.2.3-pr.1 <2.0.0 + // That should allow `1.2.3-pr.2` to pass. + // However, `1.2.4-alpha.notready` should NOT be allowed, + // even though it's within the range set by the comparators. + for (i = 0; i < set.length; i++) { + debug(set[i].semver) + if (set[i].semver === ANY) { + continue + } + + if (set[i].semver.prerelease.length > 0) { + var allowed = set[i].semver + if (allowed.major === version.major && + allowed.minor === version.minor && + allowed.patch === version.patch) { + return true + } + } + } + + // Version has a -pre, but it's not one of the ones we like. + return false + } + + return true +} + +exports.satisfies = satisfies +function satisfies (version, range, options) { + try { + range = new Range(range, options) + } catch (er) { + return false + } + return range.test(version) +} + +exports.maxSatisfying = maxSatisfying +function maxSatisfying (versions, range, options) { + var max = null + var maxSV = null + try { + var rangeObj = new Range(range, options) + } catch (er) { + return null + } + versions.forEach(function (v) { + if (rangeObj.test(v)) { + // satisfies(v, range, options) + if (!max || maxSV.compare(v) === -1) { + // compare(max, v, true) + max = v + maxSV = new SemVer(max, options) + } + } + }) + return max +} + +exports.minSatisfying = minSatisfying +function minSatisfying (versions, range, options) { + var min = null + var minSV = null + try { + var rangeObj = new Range(range, options) + } catch (er) { + return null + } + versions.forEach(function (v) { + if (rangeObj.test(v)) { + // satisfies(v, range, options) + if (!min || minSV.compare(v) === 1) { + // compare(min, v, true) + min = v + minSV = new SemVer(min, options) + } + } + }) + return min +} + +exports.minVersion = minVersion +function minVersion (range, loose) { + range = new Range(range, loose) + + var minver = new SemVer('0.0.0') + if (range.test(minver)) { + return minver + } + + minver = new SemVer('0.0.0-0') + if (range.test(minver)) { + return minver + } + + minver = null + for (var i = 0; i < range.set.length; ++i) { + var comparators = range.set[i] + + comparators.forEach(function (comparator) { + // Clone to avoid manipulating the comparator's semver object. + var compver = new SemVer(comparator.semver.version) + switch (comparator.operator) { + case '>': + if (compver.prerelease.length === 0) { + compver.patch++ + } else { + compver.prerelease.push(0) + } + compver.raw = compver.format() + /* fallthrough */ + case '': + case '>=': + if (!minver || gt(minver, compver)) { + minver = compver + } + break + case '<': + case '<=': + /* Ignore maximum versions */ + break + /* istanbul ignore next */ + default: + throw new Error('Unexpected operation: ' + comparator.operator) + } + }) + } + + if (minver && range.test(minver)) { + return minver + } + + return null +} + +exports.validRange = validRange +function validRange (range, options) { + try { + // Return '*' instead of '' so that truthiness works. + // This will throw if it's invalid anyway + return new Range(range, options).range || '*' + } catch (er) { + return null + } +} + +// Determine if version is less than all the versions possible in the range +exports.ltr = ltr +function ltr (version, range, options) { + return outside(version, range, '<', options) +} + +// Determine if version is greater than all the versions possible in the range. +exports.gtr = gtr +function gtr (version, range, options) { + return outside(version, range, '>', options) +} + +exports.outside = outside +function outside (version, range, hilo, options) { + version = new SemVer(version, options) + range = new Range(range, options) + + var gtfn, ltefn, ltfn, comp, ecomp + switch (hilo) { + case '>': + gtfn = gt + ltefn = lte + ltfn = lt + comp = '>' + ecomp = '>=' + break + case '<': + gtfn = lt + ltefn = gte + ltfn = gt + comp = '<' + ecomp = '<=' + break + default: + throw new TypeError('Must provide a hilo val of "<" or ">"') + } + + // If it satisifes the range it is not outside + if (satisfies(version, range, options)) { + return false + } + + // From now on, variable terms are as if we're in "gtr" mode. + // but note that everything is flipped for the "ltr" function. + + for (var i = 0; i < range.set.length; ++i) { + var comparators = range.set[i] + + var high = null + var low = null + + comparators.forEach(function (comparator) { + if (comparator.semver === ANY) { + comparator = new Comparator('>=0.0.0') + } + high = high || comparator + low = low || comparator + if (gtfn(comparator.semver, high.semver, options)) { + high = comparator + } else if (ltfn(comparator.semver, low.semver, options)) { + low = comparator + } + }) + + // If the edge version comparator has a operator then our version + // isn't outside it + if (high.operator === comp || high.operator === ecomp) { + return false + } + + // If the lowest version comparator has an operator and our version + // is less than it then it isn't higher than the range + if ((!low.operator || low.operator === comp) && + ltefn(version, low.semver)) { + return false + } else if (low.operator === ecomp && ltfn(version, low.semver)) { + return false + } + } + return true +} + +exports.prerelease = prerelease +function prerelease (version, options) { + var parsed = parse(version, options) + return (parsed && parsed.prerelease.length) ? parsed.prerelease : null +} + +exports.intersects = intersects +function intersects (r1, r2, options) { + r1 = new Range(r1, options) + r2 = new Range(r2, options) + return r1.intersects(r2) +} + +exports.coerce = coerce +function coerce (version) { + if (version instanceof SemVer) { + return version + } + + if (typeof version !== 'string') { + return null + } + + var match = version.match(re[COERCE]) + + if (match == null) { + return null + } + + return parse(match[1] + + '.' + (match[2] || '0') + + '.' + (match[3] || '0')) +} + + +/***/ }), +/* 375 */ +/***/ (function(module, exports, __webpack_require__) { + +var parse = __webpack_require__(376); +var correct = __webpack_require__(378); + +var genericWarning = ( + 'license should be ' + + 'a valid SPDX license expression (without "LicenseRef"), ' + + '"UNLICENSED", or ' + + '"SEE LICENSE IN "' +); + +var fileReferenceRE = /^SEE LICEN[CS]E IN (.+)$/; + +function startsWith(prefix, string) { + return string.slice(0, prefix.length) === prefix; +} + +function usesLicenseRef(ast) { + if (ast.hasOwnProperty('license')) { + var license = ast.license; + return ( + startsWith('LicenseRef', license) || + startsWith('DocumentRef', license) + ); + } else { + return ( + usesLicenseRef(ast.left) || + usesLicenseRef(ast.right) + ); + } +} + +module.exports = function(argument) { + var ast; + + try { + ast = parse(argument); + } catch (e) { + var match + if ( + argument === 'UNLICENSED' || + argument === 'UNLICENCED' + ) { + return { + validForOldPackages: true, + validForNewPackages: true, + unlicensed: true + }; + } else if (match = fileReferenceRE.exec(argument)) { + return { + validForOldPackages: true, + validForNewPackages: true, + inFile: match[1] + }; + } else { + var result = { + validForOldPackages: false, + validForNewPackages: false, + warnings: [genericWarning] + }; + var corrected = correct(argument); + if (corrected) { + result.warnings.push( + 'license is similar to the valid expression "' + corrected + '"' + ); + } + return result; + } + } + + if (usesLicenseRef(ast)) { + return { + validForNewPackages: false, + validForOldPackages: false, + spdx: true, + warnings: [genericWarning] + }; + } else { + return { + validForNewPackages: true, + validForOldPackages: true, + spdx: true + }; + } +}; + + +/***/ }), +/* 376 */ +/***/ (function(module, exports, __webpack_require__) { + +var parser = __webpack_require__(377).parser + +module.exports = function (argument) { + return parser.parse(argument) +} + + +/***/ }), +/* 377 */ +/***/ (function(module, exports, __webpack_require__) { + +/* WEBPACK VAR INJECTION */(function(module) {/* parser generated by jison 0.4.17 */ +/* + Returns a Parser object of the following structure: + + Parser: { + yy: {} + } + + Parser.prototype: { + yy: {}, + trace: function(), + symbols_: {associative list: name ==> number}, + terminals_: {associative list: number ==> name}, + productions_: [...], + performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate, $$, _$), + table: [...], + defaultActions: {...}, + parseError: function(str, hash), + parse: function(input), + + lexer: { + EOF: 1, + parseError: function(str, hash), + setInput: function(input), + input: function(), + unput: function(str), + more: function(), + less: function(n), + pastInput: function(), + upcomingInput: function(), + showPosition: function(), + test_match: function(regex_match_array, rule_index), + next: function(), + lex: function(), + begin: function(condition), + popState: function(), + _currentRules: function(), + topState: function(), + pushState: function(condition), + + options: { + ranges: boolean (optional: true ==> token location info will include a .range[] member) + flex: boolean (optional: true ==> flex-like lexing behaviour where the rules are tested exhaustively to find the longest match) + backtrack_lexer: boolean (optional: true ==> lexer regexes are tested in order and for each matching regex the action code is invoked; the lexer terminates the scan when a token is returned by the action code) + }, + + performAction: function(yy, yy_, $avoiding_name_collisions, YY_START), + rules: [...], + conditions: {associative list: name ==> set}, + } + } + + + token location info (@$, _$, etc.): { + first_line: n, + last_line: n, + first_column: n, + last_column: n, + range: [start_number, end_number] (where the numbers are indexes into the input string, regular zero-based) + } + + + the parseError function receives a 'hash' object with these members for lexer and parser errors: { + text: (matched text) + token: (the produced terminal token, if any) + line: (yylineno) + } + while parser (grammar) errors will also provide these members, i.e. parser errors deliver a superset of attributes: { + loc: (yylloc) + expected: (string describing the set of expected tokens) + recoverable: (boolean: TRUE when the parser has a error recovery rule available for this particular error) + } +*/ +var spdxparse = (function(){ +var o=function(k,v,o,l){for(o=o||{},l=k.length;l--;o[k[l]]=v);return o},$V0=[1,5],$V1=[1,6],$V2=[1,7],$V3=[1,4],$V4=[1,9],$V5=[1,10],$V6=[5,14,15,17],$V7=[5,12,14,15,17]; +var parser = {trace: function trace() { }, +yy: {}, +symbols_: {"error":2,"start":3,"expression":4,"EOS":5,"simpleExpression":6,"LICENSE":7,"PLUS":8,"LICENSEREF":9,"DOCUMENTREF":10,"COLON":11,"WITH":12,"EXCEPTION":13,"AND":14,"OR":15,"OPEN":16,"CLOSE":17,"$accept":0,"$end":1}, +terminals_: {2:"error",5:"EOS",7:"LICENSE",8:"PLUS",9:"LICENSEREF",10:"DOCUMENTREF",11:"COLON",12:"WITH",13:"EXCEPTION",14:"AND",15:"OR",16:"OPEN",17:"CLOSE"}, +productions_: [0,[3,2],[6,1],[6,2],[6,1],[6,3],[4,1],[4,3],[4,3],[4,3],[4,3]], +performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate /* action[1] */, $$ /* vstack */, _$ /* lstack */) { +/* this == yyval */ + +var $0 = $$.length - 1; +switch (yystate) { +case 1: +return this.$ = $$[$0-1] +break; +case 2: case 4: case 5: +this.$ = {license: yytext} +break; +case 3: +this.$ = {license: $$[$0-1], plus: true} +break; +case 6: +this.$ = $$[$0] +break; +case 7: +this.$ = {exception: $$[$0]} +this.$.license = $$[$0-2].license +if ($$[$0-2].hasOwnProperty('plus')) { + this.$.plus = $$[$0-2].plus +} +break; +case 8: +this.$ = {conjunction: 'and', left: $$[$0-2], right: $$[$0]} +break; +case 9: +this.$ = {conjunction: 'or', left: $$[$0-2], right: $$[$0]} +break; +case 10: +this.$ = $$[$0-1] +break; +} +}, +table: [{3:1,4:2,6:3,7:$V0,9:$V1,10:$V2,16:$V3},{1:[3]},{5:[1,8],14:$V4,15:$V5},o($V6,[2,6],{12:[1,11]}),{4:12,6:3,7:$V0,9:$V1,10:$V2,16:$V3},o($V7,[2,2],{8:[1,13]}),o($V7,[2,4]),{11:[1,14]},{1:[2,1]},{4:15,6:3,7:$V0,9:$V1,10:$V2,16:$V3},{4:16,6:3,7:$V0,9:$V1,10:$V2,16:$V3},{13:[1,17]},{14:$V4,15:$V5,17:[1,18]},o($V7,[2,3]),{9:[1,19]},o($V6,[2,8]),o([5,15,17],[2,9],{14:$V4}),o($V6,[2,7]),o($V6,[2,10]),o($V7,[2,5])], +defaultActions: {8:[2,1]}, +parseError: function parseError(str, hash) { + if (hash.recoverable) { + this.trace(str); + } else { + function _parseError (msg, hash) { + this.message = msg; + this.hash = hash; + } + _parseError.prototype = Error; + + throw new _parseError(str, hash); + } +}, +parse: function parse(input) { + var self = this, stack = [0], tstack = [], vstack = [null], lstack = [], table = this.table, yytext = '', yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1; + var args = lstack.slice.call(arguments, 1); + var lexer = Object.create(this.lexer); + var sharedState = { yy: {} }; + for (var k in this.yy) { + if (Object.prototype.hasOwnProperty.call(this.yy, k)) { + sharedState.yy[k] = this.yy[k]; + } + } + lexer.setInput(input, sharedState.yy); + sharedState.yy.lexer = lexer; + sharedState.yy.parser = this; + if (typeof lexer.yylloc == 'undefined') { + lexer.yylloc = {}; + } + var yyloc = lexer.yylloc; + lstack.push(yyloc); + var ranges = lexer.options && lexer.options.ranges; + if (typeof sharedState.yy.parseError === 'function') { + this.parseError = sharedState.yy.parseError; + } else { + this.parseError = Object.getPrototypeOf(this).parseError; + } + function popStack(n) { + stack.length = stack.length - 2 * n; + vstack.length = vstack.length - n; + lstack.length = lstack.length - n; + } + _token_stack: + var lex = function () { + var token; + token = lexer.lex() || EOF; + if (typeof token !== 'number') { + token = self.symbols_[token] || token; + } + return token; + }; + var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected; + while (true) { + state = stack[stack.length - 1]; + if (this.defaultActions[state]) { + action = this.defaultActions[state]; + } else { + if (symbol === null || typeof symbol == 'undefined') { + symbol = lex(); + } + action = table[state] && table[state][symbol]; + } + if (typeof action === 'undefined' || !action.length || !action[0]) { + var errStr = ''; + expected = []; + for (p in table[state]) { + if (this.terminals_[p] && p > TERROR) { + expected.push('\'' + this.terminals_[p] + '\''); + } + } + if (lexer.showPosition) { + errStr = 'Parse error on line ' + (yylineno + 1) + ':\n' + lexer.showPosition() + '\nExpecting ' + expected.join(', ') + ', got \'' + (this.terminals_[symbol] || symbol) + '\''; + } else { + errStr = 'Parse error on line ' + (yylineno + 1) + ': Unexpected ' + (symbol == EOF ? 'end of input' : '\'' + (this.terminals_[symbol] || symbol) + '\''); + } + this.parseError(errStr, { + text: lexer.match, + token: this.terminals_[symbol] || symbol, + line: lexer.yylineno, + loc: yyloc, + expected: expected + }); + } + if (action[0] instanceof Array && action.length > 1) { + throw new Error('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol); + } + switch (action[0]) { + case 1: + stack.push(symbol); + vstack.push(lexer.yytext); + lstack.push(lexer.yylloc); + stack.push(action[1]); + symbol = null; + if (!preErrorSymbol) { + yyleng = lexer.yyleng; + yytext = lexer.yytext; + yylineno = lexer.yylineno; + yyloc = lexer.yylloc; + if (recovering > 0) { + recovering--; + } + } else { + symbol = preErrorSymbol; + preErrorSymbol = null; + } + break; + case 2: + len = this.productions_[action[1]][1]; + yyval.$ = vstack[vstack.length - len]; + yyval._$ = { + first_line: lstack[lstack.length - (len || 1)].first_line, + last_line: lstack[lstack.length - 1].last_line, + first_column: lstack[lstack.length - (len || 1)].first_column, + last_column: lstack[lstack.length - 1].last_column + }; + if (ranges) { + yyval._$.range = [ + lstack[lstack.length - (len || 1)].range[0], + lstack[lstack.length - 1].range[1] + ]; + } + r = this.performAction.apply(yyval, [ + yytext, + yyleng, + yylineno, + sharedState.yy, + action[1], + vstack, + lstack + ].concat(args)); + if (typeof r !== 'undefined') { + return r; + } + if (len) { + stack = stack.slice(0, -1 * len * 2); + vstack = vstack.slice(0, -1 * len); + lstack = lstack.slice(0, -1 * len); + } + stack.push(this.productions_[action[1]][0]); + vstack.push(yyval.$); + lstack.push(yyval._$); + newState = table[stack[stack.length - 2]][stack[stack.length - 1]]; + stack.push(newState); + break; + case 3: + return true; + } + } + return true; +}}; +/* generated by jison-lex 0.3.4 */ +var lexer = (function(){ +var lexer = ({ + +EOF:1, + +parseError:function parseError(str, hash) { + if (this.yy.parser) { + this.yy.parser.parseError(str, hash); + } else { + throw new Error(str); + } + }, + +// resets the lexer, sets new input +setInput:function (input, yy) { + this.yy = yy || this.yy || {}; + this._input = input; + this._more = this._backtrack = this.done = false; + this.yylineno = this.yyleng = 0; + this.yytext = this.matched = this.match = ''; + this.conditionStack = ['INITIAL']; + this.yylloc = { + first_line: 1, + first_column: 0, + last_line: 1, + last_column: 0 + }; + if (this.options.ranges) { + this.yylloc.range = [0,0]; + } + this.offset = 0; + return this; + }, + +// consumes and returns one char from the input +input:function () { + var ch = this._input[0]; + this.yytext += ch; + this.yyleng++; + this.offset++; + this.match += ch; + this.matched += ch; + var lines = ch.match(/(?:\r\n?|\n).*/g); + if (lines) { + this.yylineno++; + this.yylloc.last_line++; + } else { + this.yylloc.last_column++; + } + if (this.options.ranges) { + this.yylloc.range[1]++; + } + + this._input = this._input.slice(1); + return ch; + }, + +// unshifts one char (or a string) into the input +unput:function (ch) { + var len = ch.length; + var lines = ch.split(/(?:\r\n?|\n)/g); + + this._input = ch + this._input; + this.yytext = this.yytext.substr(0, this.yytext.length - len); + //this.yyleng -= len; + this.offset -= len; + var oldLines = this.match.split(/(?:\r\n?|\n)/g); + this.match = this.match.substr(0, this.match.length - 1); + this.matched = this.matched.substr(0, this.matched.length - 1); + + if (lines.length - 1) { + this.yylineno -= lines.length - 1; + } + var r = this.yylloc.range; + + this.yylloc = { + first_line: this.yylloc.first_line, + last_line: this.yylineno + 1, + first_column: this.yylloc.first_column, + last_column: lines ? + (lines.length === oldLines.length ? this.yylloc.first_column : 0) + + oldLines[oldLines.length - lines.length].length - lines[0].length : + this.yylloc.first_column - len + }; + + if (this.options.ranges) { + this.yylloc.range = [r[0], r[0] + this.yyleng - len]; + } + this.yyleng = this.yytext.length; + return this; + }, + +// When called from action, caches matched text and appends it on next action +more:function () { + this._more = true; + return this; + }, + +// When called from action, signals the lexer that this rule fails to match the input, so the next matching rule (regex) should be tested instead. +reject:function () { + if (this.options.backtrack_lexer) { + this._backtrack = true; + } else { + return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n' + this.showPosition(), { + text: "", + token: null, + line: this.yylineno + }); + + } + return this; + }, + +// retain first n characters of the match +less:function (n) { + this.unput(this.match.slice(n)); + }, + +// displays already matched input, i.e. for error messages +pastInput:function () { + var past = this.matched.substr(0, this.matched.length - this.match.length); + return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, ""); + }, + +// displays upcoming input, i.e. for error messages +upcomingInput:function () { + var next = this.match; + if (next.length < 20) { + next += this._input.substr(0, 20-next.length); + } + return (next.substr(0,20) + (next.length > 20 ? '...' : '')).replace(/\n/g, ""); + }, + +// displays the character position where the lexing error occurred, i.e. for error messages +showPosition:function () { + var pre = this.pastInput(); + var c = new Array(pre.length + 1).join("-"); + return pre + this.upcomingInput() + "\n" + c + "^"; + }, + +// test the lexed token: return FALSE when not a match, otherwise return token +test_match:function (match, indexed_rule) { + var token, + lines, + backup; + + if (this.options.backtrack_lexer) { + // save context + backup = { + yylineno: this.yylineno, + yylloc: { + first_line: this.yylloc.first_line, + last_line: this.last_line, + first_column: this.yylloc.first_column, + last_column: this.yylloc.last_column + }, + yytext: this.yytext, + match: this.match, + matches: this.matches, + matched: this.matched, + yyleng: this.yyleng, + offset: this.offset, + _more: this._more, + _input: this._input, + yy: this.yy, + conditionStack: this.conditionStack.slice(0), + done: this.done + }; + if (this.options.ranges) { + backup.yylloc.range = this.yylloc.range.slice(0); + } + } + + lines = match[0].match(/(?:\r\n?|\n).*/g); + if (lines) { + this.yylineno += lines.length; + } + this.yylloc = { + first_line: this.yylloc.last_line, + last_line: this.yylineno + 1, + first_column: this.yylloc.last_column, + last_column: lines ? + lines[lines.length - 1].length - lines[lines.length - 1].match(/\r?\n?/)[0].length : + this.yylloc.last_column + match[0].length + }; + this.yytext += match[0]; + this.match += match[0]; + this.matches = match; + this.yyleng = this.yytext.length; + if (this.options.ranges) { + this.yylloc.range = [this.offset, this.offset += this.yyleng]; + } + this._more = false; + this._backtrack = false; + this._input = this._input.slice(match[0].length); + this.matched += match[0]; + token = this.performAction.call(this, this.yy, this, indexed_rule, this.conditionStack[this.conditionStack.length - 1]); + if (this.done && this._input) { + this.done = false; + } + if (token) { + return token; + } else if (this._backtrack) { + // recover context + for (var k in backup) { + this[k] = backup[k]; + } + return false; // rule action called reject() implying the next rule should be tested instead. + } + return false; + }, + +// return next match in input +next:function () { + if (this.done) { + return this.EOF; + } + if (!this._input) { + this.done = true; + } + + var token, + match, + tempMatch, + index; + if (!this._more) { + this.yytext = ''; + this.match = ''; + } + var rules = this._currentRules(); + for (var i = 0; i < rules.length; i++) { + tempMatch = this._input.match(this.rules[rules[i]]); + if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { + match = tempMatch; + index = i; + if (this.options.backtrack_lexer) { + token = this.test_match(tempMatch, rules[i]); + if (token !== false) { + return token; + } else if (this._backtrack) { + match = false; + continue; // rule action called reject() implying a rule MISmatch. + } else { + // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace) + return false; + } + } else if (!this.options.flex) { + break; + } + } + } + if (match) { + token = this.test_match(match, rules[index]); + if (token !== false) { + return token; + } + // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace) + return false; + } + if (this._input === "") { + return this.EOF; + } else { + return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. Unrecognized text.\n' + this.showPosition(), { + text: "", + token: null, + line: this.yylineno + }); + } + }, + +// return next match that has a token +lex:function lex() { + var r = this.next(); + if (r) { + return r; + } else { + return this.lex(); + } + }, + +// activates a new lexer condition state (pushes the new lexer condition state onto the condition stack) +begin:function begin(condition) { + this.conditionStack.push(condition); + }, + +// pop the previously active lexer condition state off the condition stack +popState:function popState() { + var n = this.conditionStack.length - 1; + if (n > 0) { + return this.conditionStack.pop(); + } else { + return this.conditionStack[0]; + } + }, + +// produce the lexer rule set which is active for the currently active lexer condition state +_currentRules:function _currentRules() { + if (this.conditionStack.length && this.conditionStack[this.conditionStack.length - 1]) { + return this.conditions[this.conditionStack[this.conditionStack.length - 1]].rules; + } else { + return this.conditions["INITIAL"].rules; + } + }, + +// return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available +topState:function topState(n) { + n = this.conditionStack.length - 1 - Math.abs(n || 0); + if (n >= 0) { + return this.conditionStack[n]; + } else { + return "INITIAL"; + } + }, + +// alias for begin(condition) +pushState:function pushState(condition) { + this.begin(condition); + }, + +// return the number of states currently on the stack +stateStackSize:function stateStackSize() { + return this.conditionStack.length; + }, +options: {}, +performAction: function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) { +var YYSTATE=YY_START; +switch($avoiding_name_collisions) { +case 0:return 5 +break; +case 1:/* skip whitespace */ +break; +case 2:return 8 +break; +case 3:return 16 +break; +case 4:return 17 +break; +case 5:return 11 +break; +case 6:return 10 +break; +case 7:return 9 +break; +case 8:return 14 +break; +case 9:return 15 +break; +case 10:return 12 +break; +case 11:return 7 +break; +case 12:return 7 +break; +case 13:return 7 +break; +case 14:return 7 +break; +case 15:return 7 +break; +case 16:return 7 +break; +case 17:return 7 +break; +case 18:return 7 +break; +case 19:return 7 +break; +case 20:return 7 +break; +case 21:return 7 +break; +case 22:return 7 +break; +case 23:return 7 +break; +case 24:return 13 +break; +case 25:return 13 +break; +case 26:return 13 +break; +case 27:return 13 +break; +case 28:return 13 +break; +case 29:return 13 +break; +case 30:return 13 +break; +case 31:return 13 +break; +case 32:return 7 +break; +case 33:return 13 +break; +case 34:return 7 +break; +case 35:return 13 +break; +case 36:return 7 +break; +case 37:return 13 +break; +case 38:return 13 +break; +case 39:return 7 +break; +case 40:return 13 +break; +case 41:return 13 +break; +case 42:return 13 +break; +case 43:return 13 +break; +case 44:return 13 +break; +case 45:return 7 +break; +case 46:return 13 +break; +case 47:return 7 +break; +case 48:return 7 +break; +case 49:return 7 +break; +case 50:return 7 +break; +case 51:return 7 +break; +case 52:return 7 +break; +case 53:return 7 +break; +case 54:return 7 +break; +case 55:return 7 +break; +case 56:return 7 +break; +case 57:return 7 +break; +case 58:return 7 +break; +case 59:return 7 +break; +case 60:return 7 +break; +case 61:return 7 +break; +case 62:return 7 +break; +case 63:return 13 +break; +case 64:return 7 +break; +case 65:return 7 +break; +case 66:return 13 +break; +case 67:return 7 +break; +case 68:return 7 +break; +case 69:return 7 +break; +case 70:return 7 +break; +case 71:return 7 +break; +case 72:return 7 +break; +case 73:return 13 +break; +case 74:return 7 +break; +case 75:return 13 +break; +case 76:return 7 +break; +case 77:return 7 +break; +case 78:return 7 +break; +case 79:return 7 +break; +case 80:return 7 +break; +case 81:return 7 +break; +case 82:return 7 +break; +case 83:return 7 +break; +case 84:return 7 +break; +case 85:return 7 +break; +case 86:return 7 +break; +case 87:return 7 +break; +case 88:return 7 +break; +case 89:return 7 +break; +case 90:return 7 +break; +case 91:return 7 +break; +case 92:return 7 +break; +case 93:return 7 +break; +case 94:return 7 +break; +case 95:return 7 +break; +case 96:return 7 +break; +case 97:return 7 +break; +case 98:return 7 +break; +case 99:return 7 +break; +case 100:return 7 +break; +case 101:return 7 +break; +case 102:return 7 +break; +case 103:return 7 +break; +case 104:return 7 +break; +case 105:return 7 +break; +case 106:return 7 +break; +case 107:return 7 +break; +case 108:return 7 +break; +case 109:return 7 +break; +case 110:return 7 +break; +case 111:return 7 +break; +case 112:return 7 +break; +case 113:return 7 +break; +case 114:return 7 +break; +case 115:return 7 +break; +case 116:return 7 +break; +case 117:return 7 +break; +case 118:return 7 +break; +case 119:return 7 +break; +case 120:return 7 +break; +case 121:return 7 +break; +case 122:return 7 +break; +case 123:return 7 +break; +case 124:return 7 +break; +case 125:return 7 +break; +case 126:return 7 +break; +case 127:return 7 +break; +case 128:return 7 +break; +case 129:return 7 +break; +case 130:return 7 +break; +case 131:return 7 +break; +case 132:return 7 +break; +case 133:return 7 +break; +case 134:return 7 +break; +case 135:return 7 +break; +case 136:return 7 +break; +case 137:return 7 +break; +case 138:return 7 +break; +case 139:return 7 +break; +case 140:return 7 +break; +case 141:return 7 +break; +case 142:return 7 +break; +case 143:return 7 +break; +case 144:return 7 +break; +case 145:return 7 +break; +case 146:return 7 +break; +case 147:return 7 +break; +case 148:return 7 +break; +case 149:return 7 +break; +case 150:return 7 +break; +case 151:return 7 +break; +case 152:return 7 +break; +case 153:return 7 +break; +case 154:return 7 +break; +case 155:return 7 +break; +case 156:return 7 +break; +case 157:return 7 +break; +case 158:return 7 +break; +case 159:return 7 +break; +case 160:return 7 +break; +case 161:return 7 +break; +case 162:return 7 +break; +case 163:return 7 +break; +case 164:return 7 +break; +case 165:return 7 +break; +case 166:return 7 +break; +case 167:return 7 +break; +case 168:return 7 +break; +case 169:return 7 +break; +case 170:return 7 +break; +case 171:return 7 +break; +case 172:return 7 +break; +case 173:return 7 +break; +case 174:return 7 +break; +case 175:return 7 +break; +case 176:return 7 +break; +case 177:return 7 +break; +case 178:return 7 +break; +case 179:return 7 +break; +case 180:return 7 +break; +case 181:return 7 +break; +case 182:return 7 +break; +case 183:return 7 +break; +case 184:return 7 +break; +case 185:return 7 +break; +case 186:return 7 +break; +case 187:return 7 +break; +case 188:return 7 +break; +case 189:return 7 +break; +case 190:return 7 +break; +case 191:return 7 +break; +case 192:return 7 +break; +case 193:return 7 +break; +case 194:return 7 +break; +case 195:return 7 +break; +case 196:return 7 +break; +case 197:return 7 +break; +case 198:return 7 +break; +case 199:return 7 +break; +case 200:return 7 +break; +case 201:return 7 +break; +case 202:return 7 +break; +case 203:return 7 +break; +case 204:return 7 +break; +case 205:return 7 +break; +case 206:return 7 +break; +case 207:return 7 break; case 208:return 7 break; @@ -36700,9157 +38805,10887 @@ break; case 364:return 7 break; } -}, -rules: [/^(?:$)/,/^(?:\s+)/,/^(?:\+)/,/^(?:\()/,/^(?:\))/,/^(?::)/,/^(?:DocumentRef-([0-9A-Za-z-+.]+))/,/^(?:LicenseRef-([0-9A-Za-z-+.]+))/,/^(?:AND)/,/^(?:OR)/,/^(?:WITH)/,/^(?:BSD-3-Clause-No-Nuclear-License-2014)/,/^(?:BSD-3-Clause-No-Nuclear-Warranty)/,/^(?:GPL-2\.0-with-classpath-exception)/,/^(?:GPL-3\.0-with-autoconf-exception)/,/^(?:GPL-2\.0-with-autoconf-exception)/,/^(?:BSD-3-Clause-No-Nuclear-License)/,/^(?:MPL-2\.0-no-copyleft-exception)/,/^(?:GPL-2\.0-with-bison-exception)/,/^(?:GPL-2\.0-with-font-exception)/,/^(?:GPL-2\.0-with-GCC-exception)/,/^(?:CNRI-Python-GPL-Compatible)/,/^(?:GPL-3\.0-with-GCC-exception)/,/^(?:BSD-3-Clause-Attribution)/,/^(?:Classpath-exception-2\.0)/,/^(?:WxWindows-exception-3\.1)/,/^(?:freertos-exception-2\.0)/,/^(?:Autoconf-exception-3\.0)/,/^(?:i2p-gpl-java-exception)/,/^(?:gnu-javamail-exception)/,/^(?:Nokia-Qt-exception-1\.1)/,/^(?:Autoconf-exception-2\.0)/,/^(?:BSD-2-Clause-FreeBSD)/,/^(?:u-boot-exception-2\.0)/,/^(?:zlib-acknowledgement)/,/^(?:Bison-exception-2\.2)/,/^(?:BSD-2-Clause-NetBSD)/,/^(?:CLISP-exception-2\.0)/,/^(?:eCos-exception-2\.0)/,/^(?:BSD-3-Clause-Clear)/,/^(?:Font-exception-2\.0)/,/^(?:FLTK-exception-2\.0)/,/^(?:GCC-exception-2\.0)/,/^(?:Qwt-exception-1\.0)/,/^(?:Libtool-exception)/,/^(?:BSD-3-Clause-LBNL)/,/^(?:GCC-exception-3\.1)/,/^(?:Artistic-1\.0-Perl)/,/^(?:Artistic-1\.0-cl8)/,/^(?:CC-BY-NC-SA-2\.5)/,/^(?:MIT-advertising)/,/^(?:BSD-Source-Code)/,/^(?:CC-BY-NC-SA-4\.0)/,/^(?:LiLiQ-Rplus-1\.1)/,/^(?:CC-BY-NC-SA-3\.0)/,/^(?:BSD-4-Clause-UC)/,/^(?:CC-BY-NC-SA-2\.0)/,/^(?:CC-BY-NC-SA-1\.0)/,/^(?:CC-BY-NC-ND-4\.0)/,/^(?:CC-BY-NC-ND-3\.0)/,/^(?:CC-BY-NC-ND-2\.5)/,/^(?:CC-BY-NC-ND-2\.0)/,/^(?:CC-BY-NC-ND-1\.0)/,/^(?:LZMA-exception)/,/^(?:BitTorrent-1\.1)/,/^(?:CrystalStacker)/,/^(?:FLTK-exception)/,/^(?:SugarCRM-1\.1\.3)/,/^(?:BSD-Protection)/,/^(?:BitTorrent-1\.0)/,/^(?:HaskellReport)/,/^(?:Interbase-1\.0)/,/^(?:StandardML-NJ)/,/^(?:mif-exception)/,/^(?:Frameworx-1\.0)/,/^(?:389-exception)/,/^(?:CC-BY-NC-2\.0)/,/^(?:CC-BY-NC-2\.5)/,/^(?:CC-BY-NC-3\.0)/,/^(?:CC-BY-NC-4\.0)/,/^(?:W3C-19980720)/,/^(?:CC-BY-SA-1\.0)/,/^(?:CC-BY-SA-2\.0)/,/^(?:CC-BY-SA-2\.5)/,/^(?:CC-BY-ND-2\.0)/,/^(?:CC-BY-SA-4\.0)/,/^(?:CC-BY-SA-3\.0)/,/^(?:Artistic-1\.0)/,/^(?:Artistic-2\.0)/,/^(?:CC-BY-ND-2\.5)/,/^(?:CC-BY-ND-3\.0)/,/^(?:CC-BY-ND-4\.0)/,/^(?:CC-BY-ND-1\.0)/,/^(?:BSD-4-Clause)/,/^(?:BSD-3-Clause)/,/^(?:BSD-2-Clause)/,/^(?:CC-BY-NC-1\.0)/,/^(?:bzip2-1\.0\.6)/,/^(?:Unicode-TOU)/,/^(?:CNRI-Jython)/,/^(?:ImageMagick)/,/^(?:Adobe-Glyph)/,/^(?:CUA-OPL-1\.0)/,/^(?:OLDAP-2\.2\.2)/,/^(?:LiLiQ-R-1\.1)/,/^(?:bzip2-1\.0\.5)/,/^(?:LiLiQ-P-1\.1)/,/^(?:OLDAP-2\.0\.1)/,/^(?:OLDAP-2\.2\.1)/,/^(?:CNRI-Python)/,/^(?:XFree86-1\.1)/,/^(?:OSET-PL-2\.1)/,/^(?:Apache-2\.0)/,/^(?:Watcom-1\.0)/,/^(?:PostgreSQL)/,/^(?:Python-2\.0)/,/^(?:RHeCos-1\.1)/,/^(?:EUDatagrid)/,/^(?:Spencer-99)/,/^(?:Intel-ACPI)/,/^(?:CECILL-1\.0)/,/^(?:CECILL-1\.1)/,/^(?:JasPer-2\.0)/,/^(?:CECILL-2\.0)/,/^(?:CECILL-2\.1)/,/^(?:gSOAP-1\.3b)/,/^(?:Spencer-94)/,/^(?:Apache-1\.1)/,/^(?:Spencer-86)/,/^(?:Apache-1\.0)/,/^(?:ClArtistic)/,/^(?:TORQUE-1\.1)/,/^(?:CATOSL-1\.1)/,/^(?:Adobe-2006)/,/^(?:Zimbra-1\.4)/,/^(?:Zimbra-1\.3)/,/^(?:Condor-1\.1)/,/^(?:CC-BY-3\.0)/,/^(?:CC-BY-2\.5)/,/^(?:OLDAP-2\.4)/,/^(?:SGI-B-1\.1)/,/^(?:SISSL-1\.2)/,/^(?:SGI-B-1\.0)/,/^(?:OLDAP-2\.3)/,/^(?:CC-BY-4\.0)/,/^(?:Crossword)/,/^(?:SimPL-2\.0)/,/^(?:OLDAP-2\.2)/,/^(?:OLDAP-2\.1)/,/^(?:ErlPL-1\.1)/,/^(?:LPPL-1\.3a)/,/^(?:LPPL-1\.3c)/,/^(?:OLDAP-2\.0)/,/^(?:Leptonica)/,/^(?:CPOL-1\.02)/,/^(?:OLDAP-1\.4)/,/^(?:OLDAP-1\.3)/,/^(?:CC-BY-2\.0)/,/^(?:Unlicense)/,/^(?:OLDAP-2\.8)/,/^(?:OLDAP-1\.2)/,/^(?:MakeIndex)/,/^(?:OLDAP-2\.7)/,/^(?:OLDAP-1\.1)/,/^(?:Sleepycat)/,/^(?:D-FSL-1\.0)/,/^(?:CC-BY-1\.0)/,/^(?:OLDAP-2\.6)/,/^(?:WXwindows)/,/^(?:NPOSL-3\.0)/,/^(?:FreeImage)/,/^(?:SGI-B-2\.0)/,/^(?:OLDAP-2\.5)/,/^(?:Beerware)/,/^(?:Newsletr)/,/^(?:NBPL-1\.0)/,/^(?:NASA-1\.3)/,/^(?:NLOD-1\.0)/,/^(?:AGPL-1\.0)/,/^(?:OCLC-2\.0)/,/^(?:ODbL-1\.0)/,/^(?:PDDL-1\.0)/,/^(?:Motosoto)/,/^(?:Afmparse)/,/^(?:ANTLR-PD)/,/^(?:LPL-1\.02)/,/^(?:Abstyles)/,/^(?:eCos-2\.0)/,/^(?:APSL-1\.0)/,/^(?:LPPL-1\.2)/,/^(?:LPPL-1\.1)/,/^(?:LPPL-1\.0)/,/^(?:APSL-1\.1)/,/^(?:APSL-2\.0)/,/^(?:Info-ZIP)/,/^(?:Zend-2\.0)/,/^(?:IBM-pibs)/,/^(?:LGPL-2\.0)/,/^(?:LGPL-3\.0)/,/^(?:LGPL-2\.1)/,/^(?:GFDL-1\.3)/,/^(?:PHP-3\.01)/,/^(?:GFDL-1\.2)/,/^(?:GFDL-1\.1)/,/^(?:AGPL-3\.0)/,/^(?:Giftware)/,/^(?:EUPL-1\.1)/,/^(?:RPSL-1\.0)/,/^(?:EUPL-1\.0)/,/^(?:MIT-enna)/,/^(?:CECILL-B)/,/^(?:diffmark)/,/^(?:CECILL-C)/,/^(?:CDDL-1\.0)/,/^(?:Sendmail)/,/^(?:CDDL-1\.1)/,/^(?:CPAL-1\.0)/,/^(?:APSL-1\.2)/,/^(?:NPL-1\.1)/,/^(?:AFL-1\.2)/,/^(?:Caldera)/,/^(?:AFL-2\.0)/,/^(?:FSFULLR)/,/^(?:AFL-2\.1)/,/^(?:VSL-1\.0)/,/^(?:VOSTROM)/,/^(?:UPL-1\.0)/,/^(?:Dotseqn)/,/^(?:CPL-1\.0)/,/^(?:dvipdfm)/,/^(?:EPL-1\.0)/,/^(?:OCCT-PL)/,/^(?:ECL-1\.0)/,/^(?:Latex2e)/,/^(?:ECL-2\.0)/,/^(?:GPL-1\.0)/,/^(?:GPL-2\.0)/,/^(?:GPL-3\.0)/,/^(?:AFL-3\.0)/,/^(?:LAL-1\.2)/,/^(?:LAL-1\.3)/,/^(?:EFL-1\.0)/,/^(?:EFL-2\.0)/,/^(?:gnuplot)/,/^(?:Aladdin)/,/^(?:LPL-1\.0)/,/^(?:libtiff)/,/^(?:Entessa)/,/^(?:AMDPLPA)/,/^(?:IPL-1\.0)/,/^(?:OPL-1\.0)/,/^(?:OSL-1\.0)/,/^(?:OSL-1\.1)/,/^(?:OSL-2\.0)/,/^(?:OSL-2\.1)/,/^(?:OSL-3\.0)/,/^(?:OpenSSL)/,/^(?:ZPL-2\.1)/,/^(?:PHP-3\.0)/,/^(?:ZPL-2\.0)/,/^(?:ZPL-1\.1)/,/^(?:CC0-1\.0)/,/^(?:SPL-1\.0)/,/^(?:psutils)/,/^(?:MPL-1\.0)/,/^(?:QPL-1\.0)/,/^(?:MPL-1\.1)/,/^(?:MPL-2\.0)/,/^(?:APL-1\.0)/,/^(?:RPL-1\.1)/,/^(?:RPL-1\.5)/,/^(?:MIT-CMU)/,/^(?:Multics)/,/^(?:Eurosym)/,/^(?:BSL-1\.0)/,/^(?:MIT-feh)/,/^(?:Saxpath)/,/^(?:Borceux)/,/^(?:OFL-1\.1)/,/^(?:OFL-1\.0)/,/^(?:AFL-1\.1)/,/^(?:YPL-1\.1)/,/^(?:YPL-1\.0)/,/^(?:NPL-1\.0)/,/^(?:iMatix)/,/^(?:mpich2)/,/^(?:APAFML)/,/^(?:Bahyph)/,/^(?:RSA-MD)/,/^(?:psfrag)/,/^(?:Plexus)/,/^(?:eGenix)/,/^(?:Glulxe)/,/^(?:SAX-PD)/,/^(?:Imlib2)/,/^(?:Wsuipa)/,/^(?:LGPLLR)/,/^(?:Libpng)/,/^(?:xinetd)/,/^(?:MITNFA)/,/^(?:NetCDF)/,/^(?:Naumen)/,/^(?:SMPPL)/,/^(?:Nunit)/,/^(?:FSFUL)/,/^(?:GL2PS)/,/^(?:SMLNJ)/,/^(?:Rdisc)/,/^(?:Noweb)/,/^(?:Nokia)/,/^(?:SISSL)/,/^(?:Qhull)/,/^(?:Intel)/,/^(?:Glide)/,/^(?:Xerox)/,/^(?:AMPAS)/,/^(?:WTFPL)/,/^(?:MS-PL)/,/^(?:XSkat)/,/^(?:MS-RL)/,/^(?:MirOS)/,/^(?:RSCPL)/,/^(?:TMate)/,/^(?:OGTSL)/,/^(?:FSFAP)/,/^(?:NCSA)/,/^(?:Zlib)/,/^(?:SCEA)/,/^(?:SNIA)/,/^(?:NGPL)/,/^(?:NOSL)/,/^(?:ADSL)/,/^(?:MTLL)/,/^(?:NLPL)/,/^(?:Ruby)/,/^(?:JSON)/,/^(?:Barr)/,/^(?:0BSD)/,/^(?:Xnet)/,/^(?:Cube)/,/^(?:curl)/,/^(?:DSDP)/,/^(?:Fair)/,/^(?:HPND)/,/^(?:TOSL)/,/^(?:IJG)/,/^(?:SWL)/,/^(?:Vim)/,/^(?:FTL)/,/^(?:ICU)/,/^(?:OML)/,/^(?:NRL)/,/^(?:DOC)/,/^(?:TCL)/,/^(?:W3C)/,/^(?:NTP)/,/^(?:IPA)/,/^(?:ISC)/,/^(?:X11)/,/^(?:AAL)/,/^(?:AML)/,/^(?:xpp)/,/^(?:Zed)/,/^(?:MIT)/,/^(?:Mup)/], -conditions: {"INITIAL":{"rules":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364],"inclusive":true}} -}); -return lexer; -})(); -parser.lexer = lexer; -function Parser () { - this.yy = {}; +}, +rules: [/^(?:$)/,/^(?:\s+)/,/^(?:\+)/,/^(?:\()/,/^(?:\))/,/^(?::)/,/^(?:DocumentRef-([0-9A-Za-z-+.]+))/,/^(?:LicenseRef-([0-9A-Za-z-+.]+))/,/^(?:AND)/,/^(?:OR)/,/^(?:WITH)/,/^(?:BSD-3-Clause-No-Nuclear-License-2014)/,/^(?:BSD-3-Clause-No-Nuclear-Warranty)/,/^(?:GPL-2\.0-with-classpath-exception)/,/^(?:GPL-3\.0-with-autoconf-exception)/,/^(?:GPL-2\.0-with-autoconf-exception)/,/^(?:BSD-3-Clause-No-Nuclear-License)/,/^(?:MPL-2\.0-no-copyleft-exception)/,/^(?:GPL-2\.0-with-bison-exception)/,/^(?:GPL-2\.0-with-font-exception)/,/^(?:GPL-2\.0-with-GCC-exception)/,/^(?:CNRI-Python-GPL-Compatible)/,/^(?:GPL-3\.0-with-GCC-exception)/,/^(?:BSD-3-Clause-Attribution)/,/^(?:Classpath-exception-2\.0)/,/^(?:WxWindows-exception-3\.1)/,/^(?:freertos-exception-2\.0)/,/^(?:Autoconf-exception-3\.0)/,/^(?:i2p-gpl-java-exception)/,/^(?:gnu-javamail-exception)/,/^(?:Nokia-Qt-exception-1\.1)/,/^(?:Autoconf-exception-2\.0)/,/^(?:BSD-2-Clause-FreeBSD)/,/^(?:u-boot-exception-2\.0)/,/^(?:zlib-acknowledgement)/,/^(?:Bison-exception-2\.2)/,/^(?:BSD-2-Clause-NetBSD)/,/^(?:CLISP-exception-2\.0)/,/^(?:eCos-exception-2\.0)/,/^(?:BSD-3-Clause-Clear)/,/^(?:Font-exception-2\.0)/,/^(?:FLTK-exception-2\.0)/,/^(?:GCC-exception-2\.0)/,/^(?:Qwt-exception-1\.0)/,/^(?:Libtool-exception)/,/^(?:BSD-3-Clause-LBNL)/,/^(?:GCC-exception-3\.1)/,/^(?:Artistic-1\.0-Perl)/,/^(?:Artistic-1\.0-cl8)/,/^(?:CC-BY-NC-SA-2\.5)/,/^(?:MIT-advertising)/,/^(?:BSD-Source-Code)/,/^(?:CC-BY-NC-SA-4\.0)/,/^(?:LiLiQ-Rplus-1\.1)/,/^(?:CC-BY-NC-SA-3\.0)/,/^(?:BSD-4-Clause-UC)/,/^(?:CC-BY-NC-SA-2\.0)/,/^(?:CC-BY-NC-SA-1\.0)/,/^(?:CC-BY-NC-ND-4\.0)/,/^(?:CC-BY-NC-ND-3\.0)/,/^(?:CC-BY-NC-ND-2\.5)/,/^(?:CC-BY-NC-ND-2\.0)/,/^(?:CC-BY-NC-ND-1\.0)/,/^(?:LZMA-exception)/,/^(?:BitTorrent-1\.1)/,/^(?:CrystalStacker)/,/^(?:FLTK-exception)/,/^(?:SugarCRM-1\.1\.3)/,/^(?:BSD-Protection)/,/^(?:BitTorrent-1\.0)/,/^(?:HaskellReport)/,/^(?:Interbase-1\.0)/,/^(?:StandardML-NJ)/,/^(?:mif-exception)/,/^(?:Frameworx-1\.0)/,/^(?:389-exception)/,/^(?:CC-BY-NC-2\.0)/,/^(?:CC-BY-NC-2\.5)/,/^(?:CC-BY-NC-3\.0)/,/^(?:CC-BY-NC-4\.0)/,/^(?:W3C-19980720)/,/^(?:CC-BY-SA-1\.0)/,/^(?:CC-BY-SA-2\.0)/,/^(?:CC-BY-SA-2\.5)/,/^(?:CC-BY-ND-2\.0)/,/^(?:CC-BY-SA-4\.0)/,/^(?:CC-BY-SA-3\.0)/,/^(?:Artistic-1\.0)/,/^(?:Artistic-2\.0)/,/^(?:CC-BY-ND-2\.5)/,/^(?:CC-BY-ND-3\.0)/,/^(?:CC-BY-ND-4\.0)/,/^(?:CC-BY-ND-1\.0)/,/^(?:BSD-4-Clause)/,/^(?:BSD-3-Clause)/,/^(?:BSD-2-Clause)/,/^(?:CC-BY-NC-1\.0)/,/^(?:bzip2-1\.0\.6)/,/^(?:Unicode-TOU)/,/^(?:CNRI-Jython)/,/^(?:ImageMagick)/,/^(?:Adobe-Glyph)/,/^(?:CUA-OPL-1\.0)/,/^(?:OLDAP-2\.2\.2)/,/^(?:LiLiQ-R-1\.1)/,/^(?:bzip2-1\.0\.5)/,/^(?:LiLiQ-P-1\.1)/,/^(?:OLDAP-2\.0\.1)/,/^(?:OLDAP-2\.2\.1)/,/^(?:CNRI-Python)/,/^(?:XFree86-1\.1)/,/^(?:OSET-PL-2\.1)/,/^(?:Apache-2\.0)/,/^(?:Watcom-1\.0)/,/^(?:PostgreSQL)/,/^(?:Python-2\.0)/,/^(?:RHeCos-1\.1)/,/^(?:EUDatagrid)/,/^(?:Spencer-99)/,/^(?:Intel-ACPI)/,/^(?:CECILL-1\.0)/,/^(?:CECILL-1\.1)/,/^(?:JasPer-2\.0)/,/^(?:CECILL-2\.0)/,/^(?:CECILL-2\.1)/,/^(?:gSOAP-1\.3b)/,/^(?:Spencer-94)/,/^(?:Apache-1\.1)/,/^(?:Spencer-86)/,/^(?:Apache-1\.0)/,/^(?:ClArtistic)/,/^(?:TORQUE-1\.1)/,/^(?:CATOSL-1\.1)/,/^(?:Adobe-2006)/,/^(?:Zimbra-1\.4)/,/^(?:Zimbra-1\.3)/,/^(?:Condor-1\.1)/,/^(?:CC-BY-3\.0)/,/^(?:CC-BY-2\.5)/,/^(?:OLDAP-2\.4)/,/^(?:SGI-B-1\.1)/,/^(?:SISSL-1\.2)/,/^(?:SGI-B-1\.0)/,/^(?:OLDAP-2\.3)/,/^(?:CC-BY-4\.0)/,/^(?:Crossword)/,/^(?:SimPL-2\.0)/,/^(?:OLDAP-2\.2)/,/^(?:OLDAP-2\.1)/,/^(?:ErlPL-1\.1)/,/^(?:LPPL-1\.3a)/,/^(?:LPPL-1\.3c)/,/^(?:OLDAP-2\.0)/,/^(?:Leptonica)/,/^(?:CPOL-1\.02)/,/^(?:OLDAP-1\.4)/,/^(?:OLDAP-1\.3)/,/^(?:CC-BY-2\.0)/,/^(?:Unlicense)/,/^(?:OLDAP-2\.8)/,/^(?:OLDAP-1\.2)/,/^(?:MakeIndex)/,/^(?:OLDAP-2\.7)/,/^(?:OLDAP-1\.1)/,/^(?:Sleepycat)/,/^(?:D-FSL-1\.0)/,/^(?:CC-BY-1\.0)/,/^(?:OLDAP-2\.6)/,/^(?:WXwindows)/,/^(?:NPOSL-3\.0)/,/^(?:FreeImage)/,/^(?:SGI-B-2\.0)/,/^(?:OLDAP-2\.5)/,/^(?:Beerware)/,/^(?:Newsletr)/,/^(?:NBPL-1\.0)/,/^(?:NASA-1\.3)/,/^(?:NLOD-1\.0)/,/^(?:AGPL-1\.0)/,/^(?:OCLC-2\.0)/,/^(?:ODbL-1\.0)/,/^(?:PDDL-1\.0)/,/^(?:Motosoto)/,/^(?:Afmparse)/,/^(?:ANTLR-PD)/,/^(?:LPL-1\.02)/,/^(?:Abstyles)/,/^(?:eCos-2\.0)/,/^(?:APSL-1\.0)/,/^(?:LPPL-1\.2)/,/^(?:LPPL-1\.1)/,/^(?:LPPL-1\.0)/,/^(?:APSL-1\.1)/,/^(?:APSL-2\.0)/,/^(?:Info-ZIP)/,/^(?:Zend-2\.0)/,/^(?:IBM-pibs)/,/^(?:LGPL-2\.0)/,/^(?:LGPL-3\.0)/,/^(?:LGPL-2\.1)/,/^(?:GFDL-1\.3)/,/^(?:PHP-3\.01)/,/^(?:GFDL-1\.2)/,/^(?:GFDL-1\.1)/,/^(?:AGPL-3\.0)/,/^(?:Giftware)/,/^(?:EUPL-1\.1)/,/^(?:RPSL-1\.0)/,/^(?:EUPL-1\.0)/,/^(?:MIT-enna)/,/^(?:CECILL-B)/,/^(?:diffmark)/,/^(?:CECILL-C)/,/^(?:CDDL-1\.0)/,/^(?:Sendmail)/,/^(?:CDDL-1\.1)/,/^(?:CPAL-1\.0)/,/^(?:APSL-1\.2)/,/^(?:NPL-1\.1)/,/^(?:AFL-1\.2)/,/^(?:Caldera)/,/^(?:AFL-2\.0)/,/^(?:FSFULLR)/,/^(?:AFL-2\.1)/,/^(?:VSL-1\.0)/,/^(?:VOSTROM)/,/^(?:UPL-1\.0)/,/^(?:Dotseqn)/,/^(?:CPL-1\.0)/,/^(?:dvipdfm)/,/^(?:EPL-1\.0)/,/^(?:OCCT-PL)/,/^(?:ECL-1\.0)/,/^(?:Latex2e)/,/^(?:ECL-2\.0)/,/^(?:GPL-1\.0)/,/^(?:GPL-2\.0)/,/^(?:GPL-3\.0)/,/^(?:AFL-3\.0)/,/^(?:LAL-1\.2)/,/^(?:LAL-1\.3)/,/^(?:EFL-1\.0)/,/^(?:EFL-2\.0)/,/^(?:gnuplot)/,/^(?:Aladdin)/,/^(?:LPL-1\.0)/,/^(?:libtiff)/,/^(?:Entessa)/,/^(?:AMDPLPA)/,/^(?:IPL-1\.0)/,/^(?:OPL-1\.0)/,/^(?:OSL-1\.0)/,/^(?:OSL-1\.1)/,/^(?:OSL-2\.0)/,/^(?:OSL-2\.1)/,/^(?:OSL-3\.0)/,/^(?:OpenSSL)/,/^(?:ZPL-2\.1)/,/^(?:PHP-3\.0)/,/^(?:ZPL-2\.0)/,/^(?:ZPL-1\.1)/,/^(?:CC0-1\.0)/,/^(?:SPL-1\.0)/,/^(?:psutils)/,/^(?:MPL-1\.0)/,/^(?:QPL-1\.0)/,/^(?:MPL-1\.1)/,/^(?:MPL-2\.0)/,/^(?:APL-1\.0)/,/^(?:RPL-1\.1)/,/^(?:RPL-1\.5)/,/^(?:MIT-CMU)/,/^(?:Multics)/,/^(?:Eurosym)/,/^(?:BSL-1\.0)/,/^(?:MIT-feh)/,/^(?:Saxpath)/,/^(?:Borceux)/,/^(?:OFL-1\.1)/,/^(?:OFL-1\.0)/,/^(?:AFL-1\.1)/,/^(?:YPL-1\.1)/,/^(?:YPL-1\.0)/,/^(?:NPL-1\.0)/,/^(?:iMatix)/,/^(?:mpich2)/,/^(?:APAFML)/,/^(?:Bahyph)/,/^(?:RSA-MD)/,/^(?:psfrag)/,/^(?:Plexus)/,/^(?:eGenix)/,/^(?:Glulxe)/,/^(?:SAX-PD)/,/^(?:Imlib2)/,/^(?:Wsuipa)/,/^(?:LGPLLR)/,/^(?:Libpng)/,/^(?:xinetd)/,/^(?:MITNFA)/,/^(?:NetCDF)/,/^(?:Naumen)/,/^(?:SMPPL)/,/^(?:Nunit)/,/^(?:FSFUL)/,/^(?:GL2PS)/,/^(?:SMLNJ)/,/^(?:Rdisc)/,/^(?:Noweb)/,/^(?:Nokia)/,/^(?:SISSL)/,/^(?:Qhull)/,/^(?:Intel)/,/^(?:Glide)/,/^(?:Xerox)/,/^(?:AMPAS)/,/^(?:WTFPL)/,/^(?:MS-PL)/,/^(?:XSkat)/,/^(?:MS-RL)/,/^(?:MirOS)/,/^(?:RSCPL)/,/^(?:TMate)/,/^(?:OGTSL)/,/^(?:FSFAP)/,/^(?:NCSA)/,/^(?:Zlib)/,/^(?:SCEA)/,/^(?:SNIA)/,/^(?:NGPL)/,/^(?:NOSL)/,/^(?:ADSL)/,/^(?:MTLL)/,/^(?:NLPL)/,/^(?:Ruby)/,/^(?:JSON)/,/^(?:Barr)/,/^(?:0BSD)/,/^(?:Xnet)/,/^(?:Cube)/,/^(?:curl)/,/^(?:DSDP)/,/^(?:Fair)/,/^(?:HPND)/,/^(?:TOSL)/,/^(?:IJG)/,/^(?:SWL)/,/^(?:Vim)/,/^(?:FTL)/,/^(?:ICU)/,/^(?:OML)/,/^(?:NRL)/,/^(?:DOC)/,/^(?:TCL)/,/^(?:W3C)/,/^(?:NTP)/,/^(?:IPA)/,/^(?:ISC)/,/^(?:X11)/,/^(?:AAL)/,/^(?:AML)/,/^(?:xpp)/,/^(?:Zed)/,/^(?:MIT)/,/^(?:Mup)/], +conditions: {"INITIAL":{"rules":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364],"inclusive":true}} +}); +return lexer; +})(); +parser.lexer = lexer; +function Parser () { + this.yy = {}; +} +Parser.prototype = parser;parser.Parser = Parser; +return new Parser; +})(); + + +if (true) { +exports.parser = spdxparse; +exports.Parser = spdxparse.Parser; +exports.parse = function () { return spdxparse.parse.apply(spdxparse, arguments); }; +exports.main = function commonjsMain(args) { + if (!args[1]) { + console.log('Usage: '+args[0]+' FILE'); + process.exit(1); + } + var source = __webpack_require__(132).readFileSync(__webpack_require__(4).normalize(args[1]), "utf8"); + return exports.parser.parse(source); +}; +if ( true && __webpack_require__.c[__webpack_require__.s] === module) { + exports.main(process.argv.slice(1)); +} +} + +/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(116)(module))) + +/***/ }), +/* 378 */ +/***/ (function(module, exports, __webpack_require__) { + +var licenseIDs = __webpack_require__(379); + +function valid(string) { + return licenseIDs.indexOf(string) > -1; +} + +// Common transpositions of license identifier acronyms +var transpositions = [ + ['APGL', 'AGPL'], + ['Gpl', 'GPL'], + ['GLP', 'GPL'], + ['APL', 'Apache'], + ['ISD', 'ISC'], + ['GLP', 'GPL'], + ['IST', 'ISC'], + ['Claude', 'Clause'], + [' or later', '+'], + [' International', ''], + ['GNU', 'GPL'], + ['GUN', 'GPL'], + ['+', ''], + ['GNU GPL', 'GPL'], + ['GNU/GPL', 'GPL'], + ['GNU GLP', 'GPL'], + ['GNU General Public License', 'GPL'], + ['Gnu public license', 'GPL'], + ['GNU Public License', 'GPL'], + ['GNU GENERAL PUBLIC LICENSE', 'GPL'], + ['MTI', 'MIT'], + ['Mozilla Public License', 'MPL'], + ['WTH', 'WTF'], + ['-License', ''] +]; + +var TRANSPOSED = 0; +var CORRECT = 1; + +// Simple corrections to nearly valid identifiers. +var transforms = [ + // e.g. 'mit' + function(argument) { + return argument.toUpperCase(); + }, + // e.g. 'MIT ' + function(argument) { + return argument.trim(); + }, + // e.g. 'M.I.T.' + function(argument) { + return argument.replace(/\./g, ''); + }, + // e.g. 'Apache- 2.0' + function(argument) { + return argument.replace(/\s+/g, ''); + }, + // e.g. 'CC BY 4.0'' + function(argument) { + return argument.replace(/\s+/g, '-'); + }, + // e.g. 'LGPLv2.1' + function(argument) { + return argument.replace('v', '-'); + }, + // e.g. 'Apache 2.0' + function(argument) { + return argument.replace(/,?\s*(\d)/, '-$1'); + }, + // e.g. 'GPL 2' + function(argument) { + return argument.replace(/,?\s*(\d)/, '-$1.0'); + }, + // e.g. 'Apache Version 2.0' + function(argument) { + return argument.replace(/,?\s*(V\.|v\.|V|v|Version|version)\s*(\d)/, '-$2'); + }, + // e.g. 'Apache Version 2' + function(argument) { + return argument.replace(/,?\s*(V\.|v\.|V|v|Version|version)\s*(\d)/, '-$2.0'); + }, + // e.g. 'ZLIB' + function(argument) { + return argument[0].toUpperCase() + argument.slice(1); + }, + // e.g. 'MPL/2.0' + function(argument) { + return argument.replace('/', '-'); + }, + // e.g. 'Apache 2' + function(argument) { + return argument + .replace(/\s*V\s*(\d)/, '-$1') + .replace(/(\d)$/, '$1.0'); + }, + // e.g. 'GPL-2.0-' + function(argument) { + return argument.slice(0, argument.length - 1); + }, + // e.g. 'GPL2' + function(argument) { + return argument.replace(/(\d)$/, '-$1.0'); + }, + // e.g. 'BSD 3' + function(argument) { + return argument.replace(/(-| )?(\d)$/, '-$2-Clause'); + }, + // e.g. 'BSD clause 3' + function(argument) { + return argument.replace(/(-| )clause(-| )(\d)/, '-$3-Clause'); + }, + // e.g. 'BY-NC-4.0' + function(argument) { + return 'CC-' + argument; + }, + // e.g. 'BY-NC' + function(argument) { + return 'CC-' + argument + '-4.0'; + }, + // e.g. 'Attribution-NonCommercial' + function(argument) { + return argument + .replace('Attribution', 'BY') + .replace('NonCommercial', 'NC') + .replace('NoDerivatives', 'ND') + .replace(/ (\d)/, '-$1') + .replace(/ ?International/, ''); + }, + // e.g. 'Attribution-NonCommercial' + function(argument) { + return 'CC-' + + argument + .replace('Attribution', 'BY') + .replace('NonCommercial', 'NC') + .replace('NoDerivatives', 'ND') + .replace(/ (\d)/, '-$1') + .replace(/ ?International/, '') + + '-4.0'; + } +]; + +// If all else fails, guess that strings containing certain substrings +// meant to identify certain licenses. +var lastResorts = [ + ['UNLI', 'Unlicense'], + ['WTF', 'WTFPL'], + ['2 CLAUSE', 'BSD-2-Clause'], + ['2-CLAUSE', 'BSD-2-Clause'], + ['3 CLAUSE', 'BSD-3-Clause'], + ['3-CLAUSE', 'BSD-3-Clause'], + ['AFFERO', 'AGPL-3.0'], + ['AGPL', 'AGPL-3.0'], + ['APACHE', 'Apache-2.0'], + ['ARTISTIC', 'Artistic-2.0'], + ['Affero', 'AGPL-3.0'], + ['BEER', 'Beerware'], + ['BOOST', 'BSL-1.0'], + ['BSD', 'BSD-2-Clause'], + ['ECLIPSE', 'EPL-1.0'], + ['FUCK', 'WTFPL'], + ['GNU', 'GPL-3.0'], + ['LGPL', 'LGPL-3.0'], + ['GPL', 'GPL-3.0'], + ['MIT', 'MIT'], + ['MPL', 'MPL-2.0'], + ['X11', 'X11'], + ['ZLIB', 'Zlib'] +]; + +var SUBSTRING = 0; +var IDENTIFIER = 1; + +var validTransformation = function(identifier) { + for (var i = 0; i < transforms.length; i++) { + var transformed = transforms[i](identifier); + if (transformed !== identifier && valid(transformed)) { + return transformed; + } + } + return null; +}; + +var validLastResort = function(identifier) { + var upperCased = identifier.toUpperCase(); + for (var i = 0; i < lastResorts.length; i++) { + var lastResort = lastResorts[i]; + if (upperCased.indexOf(lastResort[SUBSTRING]) > -1) { + return lastResort[IDENTIFIER]; + } + } + return null; +}; + +var anyCorrection = function(identifier, check) { + for (var i = 0; i < transpositions.length; i++) { + var transposition = transpositions[i]; + var transposed = transposition[TRANSPOSED]; + if (identifier.indexOf(transposed) > -1) { + var corrected = identifier.replace( + transposed, + transposition[CORRECT] + ); + var checked = check(corrected); + if (checked !== null) { + return checked; + } + } + } + return null; +}; + +module.exports = function(identifier) { + identifier = identifier.replace(/\+$/, ''); + if (valid(identifier)) { + return identifier; + } + var transformed = validTransformation(identifier); + if (transformed !== null) { + return transformed; + } + transformed = anyCorrection(identifier, function(argument) { + if (valid(argument)) { + return argument; + } + return validTransformation(argument); + }); + if (transformed !== null) { + return transformed; + } + transformed = validLastResort(identifier); + if (transformed !== null) { + return transformed; + } + transformed = anyCorrection(identifier, validLastResort); + if (transformed !== null) { + return transformed; + } + return null; +}; + + +/***/ }), +/* 379 */ +/***/ (function(module) { + +module.exports = JSON.parse("[\"Glide\",\"Abstyles\",\"AFL-1.1\",\"AFL-1.2\",\"AFL-2.0\",\"AFL-2.1\",\"AFL-3.0\",\"AMPAS\",\"APL-1.0\",\"Adobe-Glyph\",\"APAFML\",\"Adobe-2006\",\"AGPL-1.0\",\"Afmparse\",\"Aladdin\",\"ADSL\",\"AMDPLPA\",\"ANTLR-PD\",\"Apache-1.0\",\"Apache-1.1\",\"Apache-2.0\",\"AML\",\"APSL-1.0\",\"APSL-1.1\",\"APSL-1.2\",\"APSL-2.0\",\"Artistic-1.0\",\"Artistic-1.0-Perl\",\"Artistic-1.0-cl8\",\"Artistic-2.0\",\"AAL\",\"Bahyph\",\"Barr\",\"Beerware\",\"BitTorrent-1.0\",\"BitTorrent-1.1\",\"BSL-1.0\",\"Borceux\",\"BSD-2-Clause\",\"BSD-2-Clause-FreeBSD\",\"BSD-2-Clause-NetBSD\",\"BSD-3-Clause\",\"BSD-3-Clause-Clear\",\"BSD-4-Clause\",\"BSD-Protection\",\"BSD-Source-Code\",\"BSD-3-Clause-Attribution\",\"0BSD\",\"BSD-4-Clause-UC\",\"bzip2-1.0.5\",\"bzip2-1.0.6\",\"Caldera\",\"CECILL-1.0\",\"CECILL-1.1\",\"CECILL-2.0\",\"CECILL-2.1\",\"CECILL-B\",\"CECILL-C\",\"ClArtistic\",\"MIT-CMU\",\"CNRI-Jython\",\"CNRI-Python\",\"CNRI-Python-GPL-Compatible\",\"CPOL-1.02\",\"CDDL-1.0\",\"CDDL-1.1\",\"CPAL-1.0\",\"CPL-1.0\",\"CATOSL-1.1\",\"Condor-1.1\",\"CC-BY-1.0\",\"CC-BY-2.0\",\"CC-BY-2.5\",\"CC-BY-3.0\",\"CC-BY-4.0\",\"CC-BY-ND-1.0\",\"CC-BY-ND-2.0\",\"CC-BY-ND-2.5\",\"CC-BY-ND-3.0\",\"CC-BY-ND-4.0\",\"CC-BY-NC-1.0\",\"CC-BY-NC-2.0\",\"CC-BY-NC-2.5\",\"CC-BY-NC-3.0\",\"CC-BY-NC-4.0\",\"CC-BY-NC-ND-1.0\",\"CC-BY-NC-ND-2.0\",\"CC-BY-NC-ND-2.5\",\"CC-BY-NC-ND-3.0\",\"CC-BY-NC-ND-4.0\",\"CC-BY-NC-SA-1.0\",\"CC-BY-NC-SA-2.0\",\"CC-BY-NC-SA-2.5\",\"CC-BY-NC-SA-3.0\",\"CC-BY-NC-SA-4.0\",\"CC-BY-SA-1.0\",\"CC-BY-SA-2.0\",\"CC-BY-SA-2.5\",\"CC-BY-SA-3.0\",\"CC-BY-SA-4.0\",\"CC0-1.0\",\"Crossword\",\"CrystalStacker\",\"CUA-OPL-1.0\",\"Cube\",\"curl\",\"D-FSL-1.0\",\"diffmark\",\"WTFPL\",\"DOC\",\"Dotseqn\",\"DSDP\",\"dvipdfm\",\"EPL-1.0\",\"ECL-1.0\",\"ECL-2.0\",\"eGenix\",\"EFL-1.0\",\"EFL-2.0\",\"MIT-advertising\",\"MIT-enna\",\"Entessa\",\"ErlPL-1.1\",\"EUDatagrid\",\"EUPL-1.0\",\"EUPL-1.1\",\"Eurosym\",\"Fair\",\"MIT-feh\",\"Frameworx-1.0\",\"FreeImage\",\"FTL\",\"FSFAP\",\"FSFUL\",\"FSFULLR\",\"Giftware\",\"GL2PS\",\"Glulxe\",\"AGPL-3.0\",\"GFDL-1.1\",\"GFDL-1.2\",\"GFDL-1.3\",\"GPL-1.0\",\"GPL-2.0\",\"GPL-3.0\",\"LGPL-2.1\",\"LGPL-3.0\",\"LGPL-2.0\",\"gnuplot\",\"gSOAP-1.3b\",\"HaskellReport\",\"HPND\",\"IBM-pibs\",\"IPL-1.0\",\"ICU\",\"ImageMagick\",\"iMatix\",\"Imlib2\",\"IJG\",\"Info-ZIP\",\"Intel-ACPI\",\"Intel\",\"Interbase-1.0\",\"IPA\",\"ISC\",\"JasPer-2.0\",\"JSON\",\"LPPL-1.0\",\"LPPL-1.1\",\"LPPL-1.2\",\"LPPL-1.3a\",\"LPPL-1.3c\",\"Latex2e\",\"BSD-3-Clause-LBNL\",\"Leptonica\",\"LGPLLR\",\"Libpng\",\"libtiff\",\"LAL-1.2\",\"LAL-1.3\",\"LiLiQ-P-1.1\",\"LiLiQ-Rplus-1.1\",\"LiLiQ-R-1.1\",\"LPL-1.02\",\"LPL-1.0\",\"MakeIndex\",\"MTLL\",\"MS-PL\",\"MS-RL\",\"MirOS\",\"MITNFA\",\"MIT\",\"Motosoto\",\"MPL-1.0\",\"MPL-1.1\",\"MPL-2.0\",\"MPL-2.0-no-copyleft-exception\",\"mpich2\",\"Multics\",\"Mup\",\"NASA-1.3\",\"Naumen\",\"NBPL-1.0\",\"NetCDF\",\"NGPL\",\"NOSL\",\"NPL-1.0\",\"NPL-1.1\",\"Newsletr\",\"NLPL\",\"Nokia\",\"NPOSL-3.0\",\"NLOD-1.0\",\"Noweb\",\"NRL\",\"NTP\",\"Nunit\",\"OCLC-2.0\",\"ODbL-1.0\",\"PDDL-1.0\",\"OCCT-PL\",\"OGTSL\",\"OLDAP-2.2.2\",\"OLDAP-1.1\",\"OLDAP-1.2\",\"OLDAP-1.3\",\"OLDAP-1.4\",\"OLDAP-2.0\",\"OLDAP-2.0.1\",\"OLDAP-2.1\",\"OLDAP-2.2\",\"OLDAP-2.2.1\",\"OLDAP-2.3\",\"OLDAP-2.4\",\"OLDAP-2.5\",\"OLDAP-2.6\",\"OLDAP-2.7\",\"OLDAP-2.8\",\"OML\",\"OPL-1.0\",\"OSL-1.0\",\"OSL-1.1\",\"OSL-2.0\",\"OSL-2.1\",\"OSL-3.0\",\"OpenSSL\",\"OSET-PL-2.1\",\"PHP-3.0\",\"PHP-3.01\",\"Plexus\",\"PostgreSQL\",\"psfrag\",\"psutils\",\"Python-2.0\",\"QPL-1.0\",\"Qhull\",\"Rdisc\",\"RPSL-1.0\",\"RPL-1.1\",\"RPL-1.5\",\"RHeCos-1.1\",\"RSCPL\",\"RSA-MD\",\"Ruby\",\"SAX-PD\",\"Saxpath\",\"SCEA\",\"SWL\",\"SMPPL\",\"Sendmail\",\"SGI-B-1.0\",\"SGI-B-1.1\",\"SGI-B-2.0\",\"OFL-1.0\",\"OFL-1.1\",\"SimPL-2.0\",\"Sleepycat\",\"SNIA\",\"Spencer-86\",\"Spencer-94\",\"Spencer-99\",\"SMLNJ\",\"SugarCRM-1.1.3\",\"SISSL\",\"SISSL-1.2\",\"SPL-1.0\",\"Watcom-1.0\",\"TCL\",\"Unlicense\",\"TMate\",\"TORQUE-1.1\",\"TOSL\",\"Unicode-TOU\",\"UPL-1.0\",\"NCSA\",\"Vim\",\"VOSTROM\",\"VSL-1.0\",\"W3C-19980720\",\"W3C\",\"Wsuipa\",\"Xnet\",\"X11\",\"Xerox\",\"XFree86-1.1\",\"xinetd\",\"xpp\",\"XSkat\",\"YPL-1.0\",\"YPL-1.1\",\"Zed\",\"Zend-2.0\",\"Zimbra-1.3\",\"Zimbra-1.4\",\"Zlib\",\"zlib-acknowledgement\",\"ZPL-1.1\",\"ZPL-2.0\",\"ZPL-2.1\",\"BSD-3-Clause-No-Nuclear-License\",\"BSD-3-Clause-No-Nuclear-Warranty\",\"BSD-3-Clause-No-Nuclear-License-2014\",\"eCos-2.0\",\"GPL-2.0-with-autoconf-exception\",\"GPL-2.0-with-bison-exception\",\"GPL-2.0-with-classpath-exception\",\"GPL-2.0-with-font-exception\",\"GPL-2.0-with-GCC-exception\",\"GPL-3.0-with-autoconf-exception\",\"GPL-3.0-with-GCC-exception\",\"StandardML-NJ\",\"WXwindows\"]"); + +/***/ }), +/* 380 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var url = __webpack_require__(203) +var gitHosts = __webpack_require__(381) +var GitHost = module.exports = __webpack_require__(382) + +var protocolToRepresentationMap = { + 'git+ssh:': 'sshurl', + 'git+https:': 'https', + 'ssh:': 'sshurl', + 'git:': 'git' +} + +function protocolToRepresentation (protocol) { + return protocolToRepresentationMap[protocol] || protocol.slice(0, -1) +} + +var authProtocols = { + 'git:': true, + 'https:': true, + 'git+https:': true, + 'http:': true, + 'git+http:': true +} + +var cache = {} + +module.exports.fromUrl = function (giturl, opts) { + if (typeof giturl !== 'string') return + var key = giturl + JSON.stringify(opts || {}) + + if (!(key in cache)) { + cache[key] = fromUrl(giturl, opts) + } + + return cache[key] +} + +function fromUrl (giturl, opts) { + if (giturl == null || giturl === '') return + var url = fixupUnqualifiedGist( + isGitHubShorthand(giturl) ? 'github:' + giturl : giturl + ) + var parsed = parseGitUrl(url) + var shortcutMatch = url.match(/^([^:]+):(?:[^@]+@)?(?:([^/]*)\/)?([^#]+)/) + var matches = Object.keys(gitHosts).map(function (gitHostName) { + try { + var gitHostInfo = gitHosts[gitHostName] + var auth = null + if (parsed.auth && authProtocols[parsed.protocol]) { + auth = parsed.auth + } + var committish = parsed.hash ? decodeURIComponent(parsed.hash.substr(1)) : null + var user = null + var project = null + var defaultRepresentation = null + if (shortcutMatch && shortcutMatch[1] === gitHostName) { + user = shortcutMatch[2] && decodeURIComponent(shortcutMatch[2]) + project = decodeURIComponent(shortcutMatch[3].replace(/\.git$/, '')) + defaultRepresentation = 'shortcut' + } else { + if (parsed.host && parsed.host !== gitHostInfo.domain && parsed.host.replace(/^www[.]/, '') !== gitHostInfo.domain) return + if (!gitHostInfo.protocols_re.test(parsed.protocol)) return + if (!parsed.path) return + var pathmatch = gitHostInfo.pathmatch + var matched = parsed.path.match(pathmatch) + if (!matched) return + /* istanbul ignore else */ + if (matched[1] !== null && matched[1] !== undefined) { + user = decodeURIComponent(matched[1].replace(/^:/, '')) + } + project = decodeURIComponent(matched[2]) + defaultRepresentation = protocolToRepresentation(parsed.protocol) + } + return new GitHost(gitHostName, user, auth, project, committish, defaultRepresentation, opts) + } catch (ex) { + /* istanbul ignore else */ + if (ex instanceof URIError) { + } else throw ex + } + }).filter(function (gitHostInfo) { return gitHostInfo }) + if (matches.length !== 1) return + return matches[0] +} + +function isGitHubShorthand (arg) { + // Note: This does not fully test the git ref format. + // See https://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html + // + // The only way to do this properly would be to shell out to + // git-check-ref-format, and as this is a fast sync function, + // we don't want to do that. Just let git fail if it turns + // out that the commit-ish is invalid. + // GH usernames cannot start with . or - + return /^[^:@%/\s.-][^:@%/\s]*[/][^:@\s/%]+(?:#.*)?$/.test(arg) +} + +function fixupUnqualifiedGist (giturl) { + // necessary for round-tripping gists + var parsed = url.parse(giturl) + if (parsed.protocol === 'gist:' && parsed.host && !parsed.path) { + return parsed.protocol + '/' + parsed.host + } else { + return giturl + } +} + +function parseGitUrl (giturl) { + var matched = giturl.match(/^([^@]+)@([^:/]+):[/]?((?:[^/]+[/])?[^/]+?)(?:[.]git)?(#.*)?$/) + if (!matched) { + var legacy = url.parse(giturl) + // If we don't have url.URL, then sorry, this is just not fixable. + // This affects Node <= 6.12. + if (legacy.auth && typeof url.URL === 'function') { + // git urls can be in the form of scp-style/ssh-connect strings, like + // git+ssh://user@host.com:some/path, which the legacy url parser + // supports, but WhatWG url.URL class does not. However, the legacy + // parser de-urlencodes the username and password, so something like + // https://user%3An%40me:p%40ss%3Aword@x.com/ becomes + // https://user:n@me:p@ss:word@x.com/ which is all kinds of wrong. + // Pull off just the auth and host, so we dont' get the confusing + // scp-style URL, then pass that to the WhatWG parser to get the + // auth properly escaped. + var authmatch = giturl.match(/[^@]+@[^:/]+/) + /* istanbul ignore else - this should be impossible */ + if (authmatch) { + var whatwg = new url.URL(authmatch[0]) + legacy.auth = whatwg.username || '' + if (whatwg.password) legacy.auth += ':' + whatwg.password + } + } + return legacy + } + return { + protocol: 'git+ssh:', + slashes: true, + auth: matched[1], + host: matched[2], + port: null, + hostname: matched[2], + hash: matched[4], + search: null, + query: null, + pathname: '/' + matched[3], + path: '/' + matched[3], + href: 'git+ssh://' + matched[1] + '@' + matched[2] + + '/' + matched[3] + (matched[4] || '') + } +} + + +/***/ }), +/* 381 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var gitHosts = module.exports = { + github: { + // First two are insecure and generally shouldn't be used any more, but + // they are still supported. + 'protocols': [ 'git', 'http', 'git+ssh', 'git+https', 'ssh', 'https' ], + 'domain': 'github.com', + 'treepath': 'tree', + 'filetemplate': 'https://{auth@}raw.githubusercontent.com/{user}/{project}/{committish}/{path}', + 'bugstemplate': 'https://{domain}/{user}/{project}/issues', + 'gittemplate': 'git://{auth@}{domain}/{user}/{project}.git{#committish}', + 'tarballtemplate': 'https://codeload.{domain}/{user}/{project}/tar.gz/{committish}' + }, + bitbucket: { + 'protocols': [ 'git+ssh', 'git+https', 'ssh', 'https' ], + 'domain': 'bitbucket.org', + 'treepath': 'src', + 'tarballtemplate': 'https://{domain}/{user}/{project}/get/{committish}.tar.gz' + }, + gitlab: { + 'protocols': [ 'git+ssh', 'git+https', 'ssh', 'https' ], + 'domain': 'gitlab.com', + 'treepath': 'tree', + 'bugstemplate': 'https://{domain}/{user}/{project}/issues', + 'httpstemplate': 'git+https://{auth@}{domain}/{user}/{projectPath}.git{#committish}', + 'tarballtemplate': 'https://{domain}/{user}/{project}/repository/archive.tar.gz?ref={committish}', + 'pathmatch': /^[/]([^/]+)[/]((?!.*(\/-\/|\/repository\/archive\.tar\.gz\?=.*|\/repository\/[^/]+\/archive.tar.gz$)).*?)(?:[.]git|[/])?$/ + }, + gist: { + 'protocols': [ 'git', 'git+ssh', 'git+https', 'ssh', 'https' ], + 'domain': 'gist.github.com', + 'pathmatch': /^[/](?:([^/]+)[/])?([a-z0-9]{32,})(?:[.]git)?$/, + 'filetemplate': 'https://gist.githubusercontent.com/{user}/{project}/raw{/committish}/{path}', + 'bugstemplate': 'https://{domain}/{project}', + 'gittemplate': 'git://{domain}/{project}.git{#committish}', + 'sshtemplate': 'git@{domain}:/{project}.git{#committish}', + 'sshurltemplate': 'git+ssh://git@{domain}/{project}.git{#committish}', + 'browsetemplate': 'https://{domain}/{project}{/committish}', + 'browsefiletemplate': 'https://{domain}/{project}{/committish}{#path}', + 'docstemplate': 'https://{domain}/{project}{/committish}', + 'httpstemplate': 'git+https://{domain}/{project}.git{#committish}', + 'shortcuttemplate': '{type}:{project}{#committish}', + 'pathtemplate': '{project}{#committish}', + 'tarballtemplate': 'https://codeload.github.com/gist/{project}/tar.gz/{committish}', + 'hashformat': function (fragment) { + return 'file-' + formatHashFragment(fragment) + } + } +} + +var gitHostDefaults = { + 'sshtemplate': 'git@{domain}:{user}/{project}.git{#committish}', + 'sshurltemplate': 'git+ssh://git@{domain}/{user}/{project}.git{#committish}', + 'browsetemplate': 'https://{domain}/{user}/{project}{/tree/committish}', + 'browsefiletemplate': 'https://{domain}/{user}/{project}/{treepath}/{committish}/{path}{#fragment}', + 'docstemplate': 'https://{domain}/{user}/{project}{/tree/committish}#readme', + 'httpstemplate': 'git+https://{auth@}{domain}/{user}/{project}.git{#committish}', + 'filetemplate': 'https://{domain}/{user}/{project}/raw/{committish}/{path}', + 'shortcuttemplate': '{type}:{user}/{project}{#committish}', + 'pathtemplate': '{user}/{project}{#committish}', + 'pathmatch': /^[/]([^/]+)[/]([^/]+?)(?:[.]git|[/])?$/, + 'hashformat': formatHashFragment +} + +Object.keys(gitHosts).forEach(function (name) { + Object.keys(gitHostDefaults).forEach(function (key) { + if (gitHosts[name][key]) return + gitHosts[name][key] = gitHostDefaults[key] + }) + gitHosts[name].protocols_re = RegExp('^(' + + gitHosts[name].protocols.map(function (protocol) { + return protocol.replace(/([\\+*{}()[\]$^|])/g, '\\$1') + }).join('|') + '):$') +}) + +function formatHashFragment (fragment) { + return fragment.toLowerCase().replace(/^\W+|\/|\W+$/g, '').replace(/\W+/g, '-') +} + + +/***/ }), +/* 382 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var gitHosts = __webpack_require__(381) +/* eslint-disable node/no-deprecated-api */ + +// copy-pasta util._extend from node's source, to avoid pulling +// the whole util module into peoples' webpack bundles. +/* istanbul ignore next */ +var extend = Object.assign || function _extend (target, source) { + // Don't do anything if source isn't an object + if (source === null || typeof source !== 'object') return target + + var keys = Object.keys(source) + var i = keys.length + while (i--) { + target[keys[i]] = source[keys[i]] + } + return target +} + +module.exports = GitHost +function GitHost (type, user, auth, project, committish, defaultRepresentation, opts) { + var gitHostInfo = this + gitHostInfo.type = type + Object.keys(gitHosts[type]).forEach(function (key) { + gitHostInfo[key] = gitHosts[type][key] + }) + gitHostInfo.user = user + gitHostInfo.auth = auth + gitHostInfo.project = project + gitHostInfo.committish = committish + gitHostInfo.default = defaultRepresentation + gitHostInfo.opts = opts || {} +} + +GitHost.prototype.hash = function () { + return this.committish ? '#' + this.committish : '' +} + +GitHost.prototype._fill = function (template, opts) { + if (!template) return + var vars = extend({}, opts) + vars.path = vars.path ? vars.path.replace(/^[/]+/g, '') : '' + opts = extend(extend({}, this.opts), opts) + var self = this + Object.keys(this).forEach(function (key) { + if (self[key] != null && vars[key] == null) vars[key] = self[key] + }) + var rawAuth = vars.auth + var rawcommittish = vars.committish + var rawFragment = vars.fragment + var rawPath = vars.path + var rawProject = vars.project + Object.keys(vars).forEach(function (key) { + var value = vars[key] + if ((key === 'path' || key === 'project') && typeof value === 'string') { + vars[key] = value.split('/').map(function (pathComponent) { + return encodeURIComponent(pathComponent) + }).join('/') + } else { + vars[key] = encodeURIComponent(value) + } + }) + vars['auth@'] = rawAuth ? rawAuth + '@' : '' + vars['#fragment'] = rawFragment ? '#' + this.hashformat(rawFragment) : '' + vars.fragment = vars.fragment ? vars.fragment : '' + vars['#path'] = rawPath ? '#' + this.hashformat(rawPath) : '' + vars['/path'] = vars.path ? '/' + vars.path : '' + vars.projectPath = rawProject.split('/').map(encodeURIComponent).join('/') + if (opts.noCommittish) { + vars['#committish'] = '' + vars['/tree/committish'] = '' + vars['/committish'] = '' + vars.committish = '' + } else { + vars['#committish'] = rawcommittish ? '#' + rawcommittish : '' + vars['/tree/committish'] = vars.committish + ? '/' + vars.treepath + '/' + vars.committish + : '' + vars['/committish'] = vars.committish ? '/' + vars.committish : '' + vars.committish = vars.committish || 'master' + } + var res = template + Object.keys(vars).forEach(function (key) { + res = res.replace(new RegExp('[{]' + key + '[}]', 'g'), vars[key]) + }) + if (opts.noGitPlus) { + return res.replace(/^git[+]/, '') + } else { + return res + } +} + +GitHost.prototype.ssh = function (opts) { + return this._fill(this.sshtemplate, opts) +} + +GitHost.prototype.sshurl = function (opts) { + return this._fill(this.sshurltemplate, opts) +} + +GitHost.prototype.browse = function (P, F, opts) { + if (typeof P === 'string') { + if (typeof F !== 'string') { + opts = F + F = null + } + return this._fill(this.browsefiletemplate, extend({ + fragment: F, + path: P + }, opts)) + } else { + return this._fill(this.browsetemplate, P) + } +} + +GitHost.prototype.docs = function (opts) { + return this._fill(this.docstemplate, opts) +} + +GitHost.prototype.bugs = function (opts) { + return this._fill(this.bugstemplate, opts) +} + +GitHost.prototype.https = function (opts) { + return this._fill(this.httpstemplate, opts) +} + +GitHost.prototype.git = function (opts) { + return this._fill(this.gittemplate, opts) +} + +GitHost.prototype.shortcut = function (opts) { + return this._fill(this.shortcuttemplate, opts) +} + +GitHost.prototype.path = function (opts) { + return this._fill(this.pathtemplate, opts) +} + +GitHost.prototype.tarball = function (opts_) { + var opts = extend({}, opts_, { noCommittish: false }) + return this._fill(this.tarballtemplate, opts) +} + +GitHost.prototype.file = function (P, opts) { + return this._fill(this.filetemplate, extend({ path: P }, opts)) +} + +GitHost.prototype.getDefaultRepresentation = function () { + return this.default +} + +GitHost.prototype.toString = function (opts) { + if (this.default && typeof this[this.default] === 'function') return this[this.default](opts) + return this.sshurl(opts) +} + + +/***/ }), +/* 383 */ +/***/ (function(module, exports, __webpack_require__) { + +var async = __webpack_require__(384); +async.core = __webpack_require__(394); +async.isCore = __webpack_require__(396); +async.sync = __webpack_require__(397); + +module.exports = async; + + +/***/ }), +/* 384 */ +/***/ (function(module, exports, __webpack_require__) { + +var fs = __webpack_require__(132); +var path = __webpack_require__(4); +var caller = __webpack_require__(385); +var nodeModulesPaths = __webpack_require__(386); +var normalizeOptions = __webpack_require__(388); +var isCore = __webpack_require__(389); + +var realpathFS = fs.realpath && typeof fs.realpath.native === 'function' ? fs.realpath.native : fs.realpath; + +var defaultIsFile = function isFile(file, cb) { + fs.stat(file, function (err, stat) { + if (!err) { + return cb(null, stat.isFile() || stat.isFIFO()); + } + if (err.code === 'ENOENT' || err.code === 'ENOTDIR') return cb(null, false); + return cb(err); + }); +}; + +var defaultIsDir = function isDirectory(dir, cb) { + fs.stat(dir, function (err, stat) { + if (!err) { + return cb(null, stat.isDirectory()); + } + if (err.code === 'ENOENT' || err.code === 'ENOTDIR') return cb(null, false); + return cb(err); + }); +}; + +var defaultRealpath = function realpath(x, cb) { + realpathFS(x, function (realpathErr, realPath) { + if (realpathErr && realpathErr.code !== 'ENOENT') cb(realpathErr); + else cb(null, realpathErr ? x : realPath); + }); +}; + +var maybeRealpath = function maybeRealpath(realpath, x, opts, cb) { + if (opts && opts.preserveSymlinks === false) { + realpath(x, cb); + } else { + cb(null, x); + } +}; + +var defaultReadPackage = function defaultReadPackage(readFile, pkgfile, cb) { + readFile(pkgfile, function (readFileErr, body) { + if (readFileErr) cb(readFileErr); + else { + try { + var pkg = JSON.parse(body); + cb(null, pkg); + } catch (jsonErr) { + cb(null); + } + } + }); +}; + +var getPackageCandidates = function getPackageCandidates(x, start, opts) { + var dirs = nodeModulesPaths(start, opts, x); + for (var i = 0; i < dirs.length; i++) { + dirs[i] = path.join(dirs[i], x); + } + return dirs; +}; + +module.exports = function resolve(x, options, callback) { + var cb = callback; + var opts = options; + if (typeof options === 'function') { + cb = opts; + opts = {}; + } + if (typeof x !== 'string') { + var err = new TypeError('Path must be a string.'); + return process.nextTick(function () { + cb(err); + }); + } + + opts = normalizeOptions(x, opts); + + var isFile = opts.isFile || defaultIsFile; + var isDirectory = opts.isDirectory || defaultIsDir; + var readFile = opts.readFile || fs.readFile; + var realpath = opts.realpath || defaultRealpath; + var readPackage = opts.readPackage || defaultReadPackage; + if (opts.readFile && opts.readPackage) { + var conflictErr = new TypeError('`readFile` and `readPackage` are mutually exclusive.'); + return process.nextTick(function () { + cb(conflictErr); + }); + } + var packageIterator = opts.packageIterator; + + var extensions = opts.extensions || ['.js']; + var includeCoreModules = opts.includeCoreModules !== false; + var basedir = opts.basedir || path.dirname(caller()); + var parent = opts.filename || basedir; + + opts.paths = opts.paths || []; + + // ensure that `basedir` is an absolute path at this point, resolving against the process' current working directory + var absoluteStart = path.resolve(basedir); + + maybeRealpath( + realpath, + absoluteStart, + opts, + function (err, realStart) { + if (err) cb(err); + else init(realStart); + } + ); + + var res; + function init(basedir) { + if ((/^(?:\.\.?(?:\/|$)|\/|([A-Za-z]:)?[/\\])/).test(x)) { + res = path.resolve(basedir, x); + if (x === '.' || x === '..' || x.slice(-1) === '/') res += '/'; + if ((/\/$/).test(x) && res === basedir) { + loadAsDirectory(res, opts.package, onfile); + } else loadAsFile(res, opts.package, onfile); + } else if (includeCoreModules && isCore(x)) { + return cb(null, x); + } else loadNodeModules(x, basedir, function (err, n, pkg) { + if (err) cb(err); + else if (n) { + return maybeRealpath(realpath, n, opts, function (err, realN) { + if (err) { + cb(err); + } else { + cb(null, realN, pkg); + } + }); + } else { + var moduleError = new Error("Cannot find module '" + x + "' from '" + parent + "'"); + moduleError.code = 'MODULE_NOT_FOUND'; + cb(moduleError); + } + }); + } + + function onfile(err, m, pkg) { + if (err) cb(err); + else if (m) cb(null, m, pkg); + else loadAsDirectory(res, function (err, d, pkg) { + if (err) cb(err); + else if (d) { + maybeRealpath(realpath, d, opts, function (err, realD) { + if (err) { + cb(err); + } else { + cb(null, realD, pkg); + } + }); + } else { + var moduleError = new Error("Cannot find module '" + x + "' from '" + parent + "'"); + moduleError.code = 'MODULE_NOT_FOUND'; + cb(moduleError); + } + }); + } + + function loadAsFile(x, thePackage, callback) { + var loadAsFilePackage = thePackage; + var cb = callback; + if (typeof loadAsFilePackage === 'function') { + cb = loadAsFilePackage; + loadAsFilePackage = undefined; + } + + var exts = [''].concat(extensions); + load(exts, x, loadAsFilePackage); + + function load(exts, x, loadPackage) { + if (exts.length === 0) return cb(null, undefined, loadPackage); + var file = x + exts[0]; + + var pkg = loadPackage; + if (pkg) onpkg(null, pkg); + else loadpkg(path.dirname(file), onpkg); + + function onpkg(err, pkg_, dir) { + pkg = pkg_; + if (err) return cb(err); + if (dir && pkg && opts.pathFilter) { + var rfile = path.relative(dir, file); + var rel = rfile.slice(0, rfile.length - exts[0].length); + var r = opts.pathFilter(pkg, x, rel); + if (r) return load( + [''].concat(extensions.slice()), + path.resolve(dir, r), + pkg + ); + } + isFile(file, onex); + } + function onex(err, ex) { + if (err) return cb(err); + if (ex) return cb(null, file, pkg); + load(exts.slice(1), x, pkg); + } + } + } + + function loadpkg(dir, cb) { + if (dir === '' || dir === '/') return cb(null); + if (process.platform === 'win32' && (/^\w:[/\\]*$/).test(dir)) { + return cb(null); + } + if ((/[/\\]node_modules[/\\]*$/).test(dir)) return cb(null); + + maybeRealpath(realpath, dir, opts, function (unwrapErr, pkgdir) { + if (unwrapErr) return loadpkg(path.dirname(dir), cb); + var pkgfile = path.join(pkgdir, 'package.json'); + isFile(pkgfile, function (err, ex) { + // on err, ex is false + if (!ex) return loadpkg(path.dirname(dir), cb); + + readPackage(readFile, pkgfile, function (err, pkgParam) { + if (err) cb(err); + + var pkg = pkgParam; + + if (pkg && opts.packageFilter) { + pkg = opts.packageFilter(pkg, pkgfile); + } + cb(null, pkg, dir); + }); + }); + }); + } + + function loadAsDirectory(x, loadAsDirectoryPackage, callback) { + var cb = callback; + var fpkg = loadAsDirectoryPackage; + if (typeof fpkg === 'function') { + cb = fpkg; + fpkg = opts.package; + } + + maybeRealpath(realpath, x, opts, function (unwrapErr, pkgdir) { + if (unwrapErr) return cb(unwrapErr); + var pkgfile = path.join(pkgdir, 'package.json'); + isFile(pkgfile, function (err, ex) { + if (err) return cb(err); + if (!ex) return loadAsFile(path.join(x, 'index'), fpkg, cb); + + readPackage(readFile, pkgfile, function (err, pkgParam) { + if (err) return cb(err); + + var pkg = pkgParam; + + if (pkg && opts.packageFilter) { + pkg = opts.packageFilter(pkg, pkgfile); + } + + if (pkg && pkg.main) { + if (typeof pkg.main !== 'string') { + var mainError = new TypeError('package “' + pkg.name + '” `main` must be a string'); + mainError.code = 'INVALID_PACKAGE_MAIN'; + return cb(mainError); + } + if (pkg.main === '.' || pkg.main === './') { + pkg.main = 'index'; + } + loadAsFile(path.resolve(x, pkg.main), pkg, function (err, m, pkg) { + if (err) return cb(err); + if (m) return cb(null, m, pkg); + if (!pkg) return loadAsFile(path.join(x, 'index'), pkg, cb); + + var dir = path.resolve(x, pkg.main); + loadAsDirectory(dir, pkg, function (err, n, pkg) { + if (err) return cb(err); + if (n) return cb(null, n, pkg); + loadAsFile(path.join(x, 'index'), pkg, cb); + }); + }); + return; + } + + loadAsFile(path.join(x, '/index'), pkg, cb); + }); + }); + }); + } + + function processDirs(cb, dirs) { + if (dirs.length === 0) return cb(null, undefined); + var dir = dirs[0]; + + isDirectory(path.dirname(dir), isdir); + + function isdir(err, isdir) { + if (err) return cb(err); + if (!isdir) return processDirs(cb, dirs.slice(1)); + loadAsFile(dir, opts.package, onfile); + } + + function onfile(err, m, pkg) { + if (err) return cb(err); + if (m) return cb(null, m, pkg); + loadAsDirectory(dir, opts.package, ondir); + } + + function ondir(err, n, pkg) { + if (err) return cb(err); + if (n) return cb(null, n, pkg); + processDirs(cb, dirs.slice(1)); + } + } + function loadNodeModules(x, start, cb) { + var thunk = function () { return getPackageCandidates(x, start, opts); }; + processDirs( + cb, + packageIterator ? packageIterator(x, start, thunk, opts) : thunk() + ); + } +}; + + +/***/ }), +/* 385 */ +/***/ (function(module, exports) { + +module.exports = function () { + // see https://code.google.com/p/v8/wiki/JavaScriptStackTraceApi + var origPrepareStackTrace = Error.prepareStackTrace; + Error.prepareStackTrace = function (_, stack) { return stack; }; + var stack = (new Error()).stack; + Error.prepareStackTrace = origPrepareStackTrace; + return stack[2].getFileName(); +}; + + +/***/ }), +/* 386 */ +/***/ (function(module, exports, __webpack_require__) { + +var path = __webpack_require__(4); +var parse = path.parse || __webpack_require__(387); + +var getNodeModulesDirs = function getNodeModulesDirs(absoluteStart, modules) { + var prefix = '/'; + if ((/^([A-Za-z]:)/).test(absoluteStart)) { + prefix = ''; + } else if ((/^\\\\/).test(absoluteStart)) { + prefix = '\\\\'; + } + + var paths = [absoluteStart]; + var parsed = parse(absoluteStart); + while (parsed.dir !== paths[paths.length - 1]) { + paths.push(parsed.dir); + parsed = parse(parsed.dir); + } + + return paths.reduce(function (dirs, aPath) { + return dirs.concat(modules.map(function (moduleDir) { + return path.resolve(prefix, aPath, moduleDir); + })); + }, []); +}; + +module.exports = function nodeModulesPaths(start, opts, request) { + var modules = opts && opts.moduleDirectory + ? [].concat(opts.moduleDirectory) + : ['node_modules']; + + if (opts && typeof opts.paths === 'function') { + return opts.paths( + request, + start, + function () { return getNodeModulesDirs(start, modules); }, + opts + ); + } + + var dirs = getNodeModulesDirs(start, modules); + return opts && opts.paths ? dirs.concat(opts.paths) : dirs; +}; + + +/***/ }), +/* 387 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var isWindows = process.platform === 'win32'; + +// Regex to split a windows path into into [dir, root, basename, name, ext] +var splitWindowsRe = + /^(((?:[a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?[\\\/]?)(?:[^\\\/]*[\\\/])*)((\.{1,2}|[^\\\/]+?|)(\.[^.\/\\]*|))[\\\/]*$/; + +var win32 = {}; + +function win32SplitPath(filename) { + return splitWindowsRe.exec(filename).slice(1); +} + +win32.parse = function(pathString) { + if (typeof pathString !== 'string') { + throw new TypeError( + "Parameter 'pathString' must be a string, not " + typeof pathString + ); + } + var allParts = win32SplitPath(pathString); + if (!allParts || allParts.length !== 5) { + throw new TypeError("Invalid path '" + pathString + "'"); + } + return { + root: allParts[1], + dir: allParts[0] === allParts[1] ? allParts[0] : allParts[0].slice(0, -1), + base: allParts[2], + ext: allParts[4], + name: allParts[3] + }; +}; + + + +// Split a filename into [dir, root, basename, name, ext], unix version +// 'root' is just a slash, or nothing. +var splitPathRe = + /^((\/?)(?:[^\/]*\/)*)((\.{1,2}|[^\/]+?|)(\.[^.\/]*|))[\/]*$/; +var posix = {}; + + +function posixSplitPath(filename) { + return splitPathRe.exec(filename).slice(1); +} + + +posix.parse = function(pathString) { + if (typeof pathString !== 'string') { + throw new TypeError( + "Parameter 'pathString' must be a string, not " + typeof pathString + ); + } + var allParts = posixSplitPath(pathString); + if (!allParts || allParts.length !== 5) { + throw new TypeError("Invalid path '" + pathString + "'"); + } + + return { + root: allParts[1], + dir: allParts[0].slice(0, -1), + base: allParts[2], + ext: allParts[4], + name: allParts[3], + }; +}; + + +if (isWindows) + module.exports = win32.parse; +else /* posix */ + module.exports = posix.parse; + +module.exports.posix = posix.parse; +module.exports.win32 = win32.parse; + + +/***/ }), +/* 388 */ +/***/ (function(module, exports) { + +module.exports = function (x, opts) { + /** + * This file is purposefully a passthrough. It's expected that third-party + * environments will override it at runtime in order to inject special logic + * into `resolve` (by manipulating the options). One such example is the PnP + * code path in Yarn. + */ + + return opts || {}; +}; + + +/***/ }), +/* 389 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var has = __webpack_require__(390); + +function specifierIncluded(current, specifier) { + var nodeParts = current.split('.'); + var parts = specifier.split(' '); + var op = parts.length > 1 ? parts[0] : '='; + var versionParts = (parts.length > 1 ? parts[1] : parts[0]).split('.'); + + for (var i = 0; i < 3; ++i) { + var cur = parseInt(nodeParts[i] || 0, 10); + var ver = parseInt(versionParts[i] || 0, 10); + if (cur === ver) { + continue; // eslint-disable-line no-restricted-syntax, no-continue + } + if (op === '<') { + return cur < ver; + } + if (op === '>=') { + return cur >= ver; + } + return false; + } + return op === '>='; +} + +function matchesRange(current, range) { + var specifiers = range.split(/ ?&& ?/); + if (specifiers.length === 0) { + return false; + } + for (var i = 0; i < specifiers.length; ++i) { + if (!specifierIncluded(current, specifiers[i])) { + return false; + } + } + return true; +} + +function versionIncluded(nodeVersion, specifierValue) { + if (typeof specifierValue === 'boolean') { + return specifierValue; + } + + var current = typeof nodeVersion === 'undefined' + ? process.versions && process.versions.node + : nodeVersion; + + if (typeof current !== 'string') { + throw new TypeError(typeof nodeVersion === 'undefined' ? 'Unable to determine current node version' : 'If provided, a valid node version is required'); + } + + if (specifierValue && typeof specifierValue === 'object') { + for (var i = 0; i < specifierValue.length; ++i) { + if (matchesRange(current, specifierValue[i])) { + return true; + } + } + return false; + } + return matchesRange(current, specifierValue); +} + +var data = __webpack_require__(393); + +module.exports = function isCore(x, nodeVersion) { + return has(data, x) && versionIncluded(nodeVersion, data[x]); +}; + + +/***/ }), +/* 390 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var bind = __webpack_require__(391); + +module.exports = bind.call(Function.call, Object.prototype.hasOwnProperty); + + +/***/ }), +/* 391 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var implementation = __webpack_require__(392); + +module.exports = Function.prototype.bind || implementation; + + +/***/ }), +/* 392 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +/* eslint no-invalid-this: 1 */ + +var ERROR_MESSAGE = 'Function.prototype.bind called on incompatible '; +var slice = Array.prototype.slice; +var toStr = Object.prototype.toString; +var funcType = '[object Function]'; + +module.exports = function bind(that) { + var target = this; + if (typeof target !== 'function' || toStr.call(target) !== funcType) { + throw new TypeError(ERROR_MESSAGE + target); + } + var args = slice.call(arguments, 1); + + var bound; + var binder = function () { + if (this instanceof bound) { + var result = target.apply( + this, + args.concat(slice.call(arguments)) + ); + if (Object(result) === result) { + return result; + } + return this; + } else { + return target.apply( + that, + args.concat(slice.call(arguments)) + ); + } + }; + + var boundLength = Math.max(0, target.length - args.length); + var boundArgs = []; + for (var i = 0; i < boundLength; i++) { + boundArgs.push('$' + i); + } + + bound = Function('binder', 'return function (' + boundArgs.join(',') + '){ return binder.apply(this,arguments); }')(binder); + + if (target.prototype) { + var Empty = function Empty() {}; + Empty.prototype = target.prototype; + bound.prototype = new Empty(); + Empty.prototype = null; + } + + return bound; +}; + + +/***/ }), +/* 393 */ +/***/ (function(module) { + +module.exports = JSON.parse("{\"assert\":true,\"node:assert\":[\">= 14.18 && < 15\",\">= 16\"],\"assert/strict\":\">= 15\",\"node:assert/strict\":\">= 16\",\"async_hooks\":\">= 8\",\"node:async_hooks\":[\">= 14.18 && < 15\",\">= 16\"],\"buffer_ieee754\":\"< 0.9.7\",\"buffer\":true,\"node:buffer\":[\">= 14.18 && < 15\",\">= 16\"],\"child_process\":true,\"node:child_process\":[\">= 14.18 && < 15\",\">= 16\"],\"cluster\":true,\"node:cluster\":[\">= 14.18 && < 15\",\">= 16\"],\"console\":true,\"node:console\":[\">= 14.18 && < 15\",\">= 16\"],\"constants\":true,\"node:constants\":[\">= 14.18 && < 15\",\">= 16\"],\"crypto\":true,\"node:crypto\":[\">= 14.18 && < 15\",\">= 16\"],\"_debug_agent\":\">= 1 && < 8\",\"_debugger\":\"< 8\",\"dgram\":true,\"node:dgram\":[\">= 14.18 && < 15\",\">= 16\"],\"diagnostics_channel\":[\">= 14.17 && < 15\",\">= 15.1\"],\"node:diagnostics_channel\":[\">= 14.18 && < 15\",\">= 16\"],\"dns\":true,\"node:dns\":[\">= 14.18 && < 15\",\">= 16\"],\"dns/promises\":\">= 15\",\"node:dns/promises\":\">= 16\",\"domain\":\">= 0.7.12\",\"node:domain\":[\">= 14.18 && < 15\",\">= 16\"],\"events\":true,\"node:events\":[\">= 14.18 && < 15\",\">= 16\"],\"freelist\":\"< 6\",\"fs\":true,\"node:fs\":[\">= 14.18 && < 15\",\">= 16\"],\"fs/promises\":[\">= 10 && < 10.1\",\">= 14\"],\"node:fs/promises\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_agent\":\">= 0.11.1\",\"node:_http_agent\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_client\":\">= 0.11.1\",\"node:_http_client\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_common\":\">= 0.11.1\",\"node:_http_common\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_incoming\":\">= 0.11.1\",\"node:_http_incoming\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_outgoing\":\">= 0.11.1\",\"node:_http_outgoing\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_server\":\">= 0.11.1\",\"node:_http_server\":[\">= 14.18 && < 15\",\">= 16\"],\"http\":true,\"node:http\":[\">= 14.18 && < 15\",\">= 16\"],\"http2\":\">= 8.8\",\"node:http2\":[\">= 14.18 && < 15\",\">= 16\"],\"https\":true,\"node:https\":[\">= 14.18 && < 15\",\">= 16\"],\"inspector\":\">= 8\",\"node:inspector\":[\">= 14.18 && < 15\",\">= 16\"],\"_linklist\":\"< 8\",\"module\":true,\"node:module\":[\">= 14.18 && < 15\",\">= 16\"],\"net\":true,\"node:net\":[\">= 14.18 && < 15\",\">= 16\"],\"node-inspect/lib/_inspect\":\">= 7.6 && < 12\",\"node-inspect/lib/internal/inspect_client\":\">= 7.6 && < 12\",\"node-inspect/lib/internal/inspect_repl\":\">= 7.6 && < 12\",\"os\":true,\"node:os\":[\">= 14.18 && < 15\",\">= 16\"],\"path\":true,\"node:path\":[\">= 14.18 && < 15\",\">= 16\"],\"path/posix\":\">= 15.3\",\"node:path/posix\":\">= 16\",\"path/win32\":\">= 15.3\",\"node:path/win32\":\">= 16\",\"perf_hooks\":\">= 8.5\",\"node:perf_hooks\":[\">= 14.18 && < 15\",\">= 16\"],\"process\":\">= 1\",\"node:process\":[\">= 14.18 && < 15\",\">= 16\"],\"punycode\":true,\"node:punycode\":[\">= 14.18 && < 15\",\">= 16\"],\"querystring\":true,\"node:querystring\":[\">= 14.18 && < 15\",\">= 16\"],\"readline\":true,\"node:readline\":[\">= 14.18 && < 15\",\">= 16\"],\"repl\":true,\"node:repl\":[\">= 14.18 && < 15\",\">= 16\"],\"smalloc\":\">= 0.11.5 && < 3\",\"_stream_duplex\":\">= 0.9.4\",\"node:_stream_duplex\":[\">= 14.18 && < 15\",\">= 16\"],\"_stream_transform\":\">= 0.9.4\",\"node:_stream_transform\":[\">= 14.18 && < 15\",\">= 16\"],\"_stream_wrap\":\">= 1.4.1\",\"node:_stream_wrap\":[\">= 14.18 && < 15\",\">= 16\"],\"_stream_passthrough\":\">= 0.9.4\",\"node:_stream_passthrough\":[\">= 14.18 && < 15\",\">= 16\"],\"_stream_readable\":\">= 0.9.4\",\"node:_stream_readable\":[\">= 14.18 && < 15\",\">= 16\"],\"_stream_writable\":\">= 0.9.4\",\"node:_stream_writable\":[\">= 14.18 && < 15\",\">= 16\"],\"stream\":true,\"node:stream\":[\">= 14.18 && < 15\",\">= 16\"],\"stream/consumers\":\">= 16.7\",\"node:stream/consumers\":\">= 16.7\",\"stream/promises\":\">= 15\",\"node:stream/promises\":\">= 16\",\"stream/web\":\">= 16.5\",\"node:stream/web\":\">= 16.5\",\"string_decoder\":true,\"node:string_decoder\":[\">= 14.18 && < 15\",\">= 16\"],\"sys\":[\">= 0.6 && < 0.7\",\">= 0.8\"],\"node:sys\":[\">= 14.18 && < 15\",\">= 16\"],\"timers\":true,\"node:timers\":[\">= 14.18 && < 15\",\">= 16\"],\"timers/promises\":\">= 15\",\"node:timers/promises\":\">= 16\",\"_tls_common\":\">= 0.11.13\",\"node:_tls_common\":[\">= 14.18 && < 15\",\">= 16\"],\"_tls_legacy\":\">= 0.11.3 && < 10\",\"_tls_wrap\":\">= 0.11.3\",\"node:_tls_wrap\":[\">= 14.18 && < 15\",\">= 16\"],\"tls\":true,\"node:tls\":[\">= 14.18 && < 15\",\">= 16\"],\"trace_events\":\">= 10\",\"node:trace_events\":[\">= 14.18 && < 15\",\">= 16\"],\"tty\":true,\"node:tty\":[\">= 14.18 && < 15\",\">= 16\"],\"url\":true,\"node:url\":[\">= 14.18 && < 15\",\">= 16\"],\"util\":true,\"node:util\":[\">= 14.18 && < 15\",\">= 16\"],\"util/types\":\">= 15.3\",\"node:util/types\":\">= 16\",\"v8/tools/arguments\":\">= 10 && < 12\",\"v8/tools/codemap\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8/tools/consarray\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8/tools/csvparser\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8/tools/logreader\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8/tools/profile_view\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8/tools/splaytree\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8\":\">= 1\",\"node:v8\":[\">= 14.18 && < 15\",\">= 16\"],\"vm\":true,\"node:vm\":[\">= 14.18 && < 15\",\">= 16\"],\"wasi\":\">= 13.4 && < 13.5\",\"worker_threads\":\">= 11.7\",\"node:worker_threads\":[\">= 14.18 && < 15\",\">= 16\"],\"zlib\":true,\"node:zlib\":[\">= 14.18 && < 15\",\">= 16\"]}"); + +/***/ }), +/* 394 */ +/***/ (function(module, exports, __webpack_require__) { + +var current = (process.versions && process.versions.node && process.versions.node.split('.')) || []; + +function specifierIncluded(specifier) { + var parts = specifier.split(' '); + var op = parts.length > 1 ? parts[0] : '='; + var versionParts = (parts.length > 1 ? parts[1] : parts[0]).split('.'); + + for (var i = 0; i < 3; ++i) { + var cur = parseInt(current[i] || 0, 10); + var ver = parseInt(versionParts[i] || 0, 10); + if (cur === ver) { + continue; // eslint-disable-line no-restricted-syntax, no-continue + } + if (op === '<') { + return cur < ver; + } else if (op === '>=') { + return cur >= ver; + } else { + return false; + } + } + return op === '>='; +} + +function matchesRange(range) { + var specifiers = range.split(/ ?&& ?/); + if (specifiers.length === 0) { return false; } + for (var i = 0; i < specifiers.length; ++i) { + if (!specifierIncluded(specifiers[i])) { return false; } + } + return true; +} + +function versionIncluded(specifierValue) { + if (typeof specifierValue === 'boolean') { return specifierValue; } + if (specifierValue && typeof specifierValue === 'object') { + for (var i = 0; i < specifierValue.length; ++i) { + if (matchesRange(specifierValue[i])) { return true; } + } + return false; + } + return matchesRange(specifierValue); +} + +var data = __webpack_require__(395); + +var core = {}; +for (var mod in data) { // eslint-disable-line no-restricted-syntax + if (Object.prototype.hasOwnProperty.call(data, mod)) { + core[mod] = versionIncluded(data[mod]); + } +} +module.exports = core; + + +/***/ }), +/* 395 */ +/***/ (function(module) { + +module.exports = JSON.parse("{\"assert\":true,\"assert/strict\":\">= 15\",\"async_hooks\":\">= 8\",\"buffer_ieee754\":\"< 0.9.7\",\"buffer\":true,\"child_process\":true,\"cluster\":true,\"console\":true,\"constants\":true,\"crypto\":true,\"_debug_agent\":\">= 1 && < 8\",\"_debugger\":\"< 8\",\"dgram\":true,\"diagnostics_channel\":\">= 15.1\",\"dns\":true,\"dns/promises\":\">= 15\",\"domain\":\">= 0.7.12\",\"events\":true,\"freelist\":\"< 6\",\"fs\":true,\"fs/promises\":[\">= 10 && < 10.1\",\">= 14\"],\"_http_agent\":\">= 0.11.1\",\"_http_client\":\">= 0.11.1\",\"_http_common\":\">= 0.11.1\",\"_http_incoming\":\">= 0.11.1\",\"_http_outgoing\":\">= 0.11.1\",\"_http_server\":\">= 0.11.1\",\"http\":true,\"http2\":\">= 8.8\",\"https\":true,\"inspector\":\">= 8.0.0\",\"_linklist\":\"< 8\",\"module\":true,\"net\":true,\"node-inspect/lib/_inspect\":\">= 7.6.0 && < 12\",\"node-inspect/lib/internal/inspect_client\":\">= 7.6.0 && < 12\",\"node-inspect/lib/internal/inspect_repl\":\">= 7.6.0 && < 12\",\"os\":true,\"path\":true,\"path/posix\":\">= 15.3\",\"path/win32\":\">= 15.3\",\"perf_hooks\":\">= 8.5\",\"process\":\">= 1\",\"punycode\":true,\"querystring\":true,\"readline\":true,\"repl\":true,\"smalloc\":\">= 0.11.5 && < 3\",\"_stream_duplex\":\">= 0.9.4\",\"_stream_transform\":\">= 0.9.4\",\"_stream_wrap\":\">= 1.4.1\",\"_stream_passthrough\":\">= 0.9.4\",\"_stream_readable\":\">= 0.9.4\",\"_stream_writable\":\">= 0.9.4\",\"stream\":true,\"stream/promises\":\">= 15\",\"string_decoder\":true,\"sys\":[\">= 0.6 && < 0.7\",\">= 0.8\"],\"timers\":true,\"timers/promises\":\">= 15\",\"_tls_common\":\">= 0.11.13\",\"_tls_legacy\":\">= 0.11.3 && < 10\",\"_tls_wrap\":\">= 0.11.3\",\"tls\":true,\"trace_events\":\">= 10\",\"tty\":true,\"url\":true,\"util\":true,\"util/types\":\">= 15.3\",\"v8/tools/arguments\":\">= 10 && < 12\",\"v8/tools/codemap\":[\">= 4.4.0 && < 5\",\">= 5.2.0 && < 12\"],\"v8/tools/consarray\":[\">= 4.4.0 && < 5\",\">= 5.2.0 && < 12\"],\"v8/tools/csvparser\":[\">= 4.4.0 && < 5\",\">= 5.2.0 && < 12\"],\"v8/tools/logreader\":[\">= 4.4.0 && < 5\",\">= 5.2.0 && < 12\"],\"v8/tools/profile_view\":[\">= 4.4.0 && < 5\",\">= 5.2.0 && < 12\"],\"v8/tools/splaytree\":[\">= 4.4.0 && < 5\",\">= 5.2.0 && < 12\"],\"v8\":\">= 1\",\"vm\":true,\"wasi\":\">= 13.4 && < 13.5\",\"worker_threads\":\">= 11.7\",\"zlib\":true}"); + +/***/ }), +/* 396 */ +/***/ (function(module, exports, __webpack_require__) { + +var isCoreModule = __webpack_require__(389); + +module.exports = function isCore(x) { + return isCoreModule(x); +}; + + +/***/ }), +/* 397 */ +/***/ (function(module, exports, __webpack_require__) { + +var isCore = __webpack_require__(389); +var fs = __webpack_require__(132); +var path = __webpack_require__(4); +var caller = __webpack_require__(385); +var nodeModulesPaths = __webpack_require__(386); +var normalizeOptions = __webpack_require__(388); + +var realpathFS = fs.realpathSync && typeof fs.realpathSync.native === 'function' ? fs.realpathSync.native : fs.realpathSync; + +var defaultIsFile = function isFile(file) { + try { + var stat = fs.statSync(file); + } catch (e) { + if (e && (e.code === 'ENOENT' || e.code === 'ENOTDIR')) return false; + throw e; + } + return stat.isFile() || stat.isFIFO(); +}; + +var defaultIsDir = function isDirectory(dir) { + try { + var stat = fs.statSync(dir); + } catch (e) { + if (e && (e.code === 'ENOENT' || e.code === 'ENOTDIR')) return false; + throw e; + } + return stat.isDirectory(); +}; + +var defaultRealpathSync = function realpathSync(x) { + try { + return realpathFS(x); + } catch (realpathErr) { + if (realpathErr.code !== 'ENOENT') { + throw realpathErr; + } + } + return x; +}; + +var maybeRealpathSync = function maybeRealpathSync(realpathSync, x, opts) { + if (opts && opts.preserveSymlinks === false) { + return realpathSync(x); + } + return x; +}; + +var defaultReadPackageSync = function defaultReadPackageSync(readFileSync, pkgfile) { + var body = readFileSync(pkgfile); + try { + var pkg = JSON.parse(body); + return pkg; + } catch (jsonErr) {} +}; + +var getPackageCandidates = function getPackageCandidates(x, start, opts) { + var dirs = nodeModulesPaths(start, opts, x); + for (var i = 0; i < dirs.length; i++) { + dirs[i] = path.join(dirs[i], x); + } + return dirs; +}; + +module.exports = function resolveSync(x, options) { + if (typeof x !== 'string') { + throw new TypeError('Path must be a string.'); + } + var opts = normalizeOptions(x, options); + + var isFile = opts.isFile || defaultIsFile; + var readFileSync = opts.readFileSync || fs.readFileSync; + var isDirectory = opts.isDirectory || defaultIsDir; + var realpathSync = opts.realpathSync || defaultRealpathSync; + var readPackageSync = opts.readPackageSync || defaultReadPackageSync; + if (opts.readFileSync && opts.readPackageSync) { + throw new TypeError('`readFileSync` and `readPackageSync` are mutually exclusive.'); + } + var packageIterator = opts.packageIterator; + + var extensions = opts.extensions || ['.js']; + var includeCoreModules = opts.includeCoreModules !== false; + var basedir = opts.basedir || path.dirname(caller()); + var parent = opts.filename || basedir; + + opts.paths = opts.paths || []; + + // ensure that `basedir` is an absolute path at this point, resolving against the process' current working directory + var absoluteStart = maybeRealpathSync(realpathSync, path.resolve(basedir), opts); + + if ((/^(?:\.\.?(?:\/|$)|\/|([A-Za-z]:)?[/\\])/).test(x)) { + var res = path.resolve(absoluteStart, x); + if (x === '.' || x === '..' || x.slice(-1) === '/') res += '/'; + var m = loadAsFileSync(res) || loadAsDirectorySync(res); + if (m) return maybeRealpathSync(realpathSync, m, opts); + } else if (includeCoreModules && isCore(x)) { + return x; + } else { + var n = loadNodeModulesSync(x, absoluteStart); + if (n) return maybeRealpathSync(realpathSync, n, opts); + } + + var err = new Error("Cannot find module '" + x + "' from '" + parent + "'"); + err.code = 'MODULE_NOT_FOUND'; + throw err; + + function loadAsFileSync(x) { + var pkg = loadpkg(path.dirname(x)); + + if (pkg && pkg.dir && pkg.pkg && opts.pathFilter) { + var rfile = path.relative(pkg.dir, x); + var r = opts.pathFilter(pkg.pkg, x, rfile); + if (r) { + x = path.resolve(pkg.dir, r); // eslint-disable-line no-param-reassign + } + } + + if (isFile(x)) { + return x; + } + + for (var i = 0; i < extensions.length; i++) { + var file = x + extensions[i]; + if (isFile(file)) { + return file; + } + } + } + + function loadpkg(dir) { + if (dir === '' || dir === '/') return; + if (process.platform === 'win32' && (/^\w:[/\\]*$/).test(dir)) { + return; + } + if ((/[/\\]node_modules[/\\]*$/).test(dir)) return; + + var pkgfile = path.join(maybeRealpathSync(realpathSync, dir, opts), 'package.json'); + + if (!isFile(pkgfile)) { + return loadpkg(path.dirname(dir)); + } + + var pkg = readPackageSync(readFileSync, pkgfile); + + if (pkg && opts.packageFilter) { + // v2 will pass pkgfile + pkg = opts.packageFilter(pkg, /*pkgfile,*/ dir); // eslint-disable-line spaced-comment + } + + return { pkg: pkg, dir: dir }; + } + + function loadAsDirectorySync(x) { + var pkgfile = path.join(maybeRealpathSync(realpathSync, x, opts), '/package.json'); + if (isFile(pkgfile)) { + try { + var pkg = readPackageSync(readFileSync, pkgfile); + } catch (e) {} + + if (pkg && opts.packageFilter) { + // v2 will pass pkgfile + pkg = opts.packageFilter(pkg, /*pkgfile,*/ x); // eslint-disable-line spaced-comment + } + + if (pkg && pkg.main) { + if (typeof pkg.main !== 'string') { + var mainError = new TypeError('package “' + pkg.name + '” `main` must be a string'); + mainError.code = 'INVALID_PACKAGE_MAIN'; + throw mainError; + } + if (pkg.main === '.' || pkg.main === './') { + pkg.main = 'index'; + } + try { + var m = loadAsFileSync(path.resolve(x, pkg.main)); + if (m) return m; + var n = loadAsDirectorySync(path.resolve(x, pkg.main)); + if (n) return n; + } catch (e) {} + } + } + + return loadAsFileSync(path.join(x, '/index')); + } + + function loadNodeModulesSync(x, start) { + var thunk = function () { return getPackageCandidates(x, start, opts); }; + var dirs = packageIterator ? packageIterator(x, start, thunk, opts) : thunk(); + + for (var i = 0; i < dirs.length; i++) { + var dir = dirs[i]; + if (isDirectory(path.dirname(dir))) { + var m = loadAsFileSync(dir); + if (m) return m; + var n = loadAsDirectorySync(dir); + if (n) return n; + } + } + } +}; + + +/***/ }), +/* 398 */ +/***/ (function(module, exports) { + +module.exports = extractDescription + +// Extracts description from contents of a readme file in markdown format +function extractDescription (d) { + if (!d) return; + if (d === "ERROR: No README data found!") return; + // the first block of text before the first heading + // that isn't the first line heading + d = d.trim().split('\n') + for (var s = 0; d[s] && d[s].trim().match(/^(#|$)/); s ++); + var l = d.length + for (var e = s + 1; e < l && d[e].trim(); e ++); + return d.slice(s, e).join(' ').trim() } -Parser.prototype = parser;parser.Parser = Parser; -return new Parser; -})(); -if (true) { -exports.parser = spdxparse; -exports.Parser = spdxparse.Parser; -exports.parse = function () { return spdxparse.parse.apply(spdxparse, arguments); }; -exports.main = function commonjsMain(args) { - if (!args[1]) { - console.log('Usage: '+args[0]+' FILE'); - process.exit(1); - } - var source = __webpack_require__(132).readFileSync(__webpack_require__(4).normalize(args[1]), "utf8"); - return exports.parser.parse(source); -}; -if ( true && __webpack_require__.c[__webpack_require__.s] === module) { - exports.main(process.argv.slice(1)); +/***/ }), +/* 399 */ +/***/ (function(module) { + +module.exports = JSON.parse("{\"topLevel\":{\"dependancies\":\"dependencies\",\"dependecies\":\"dependencies\",\"depdenencies\":\"dependencies\",\"devEependencies\":\"devDependencies\",\"depends\":\"dependencies\",\"dev-dependencies\":\"devDependencies\",\"devDependences\":\"devDependencies\",\"devDepenencies\":\"devDependencies\",\"devdependencies\":\"devDependencies\",\"repostitory\":\"repository\",\"repo\":\"repository\",\"prefereGlobal\":\"preferGlobal\",\"hompage\":\"homepage\",\"hampage\":\"homepage\",\"autohr\":\"author\",\"autor\":\"author\",\"contributers\":\"contributors\",\"publicationConfig\":\"publishConfig\",\"script\":\"scripts\"},\"bugs\":{\"web\":\"url\",\"name\":\"url\"},\"script\":{\"server\":\"start\",\"tests\":\"test\"}}"); + +/***/ }), +/* 400 */ +/***/ (function(module, exports, __webpack_require__) { + +var util = __webpack_require__(113) +var messages = __webpack_require__(401) + +module.exports = function() { + var args = Array.prototype.slice.call(arguments, 0) + var warningName = args.shift() + if (warningName == "typo") { + return makeTypoWarning.apply(null,args) + } + else { + var msgTemplate = messages[warningName] ? messages[warningName] : warningName + ": '%s'" + args.unshift(msgTemplate) + return util.format.apply(null, args) + } } + +function makeTypoWarning (providedName, probableName, field) { + if (field) { + providedName = field + "['" + providedName + "']" + probableName = field + "['" + probableName + "']" + } + return util.format(messages.typo, providedName, probableName) } -/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(116)(module))) /***/ }), -/* 372 */ +/* 401 */ +/***/ (function(module) { + +module.exports = JSON.parse("{\"repositories\":\"'repositories' (plural) Not supported. Please pick one as the 'repository' field\",\"missingRepository\":\"No repository field.\",\"brokenGitUrl\":\"Probably broken git url: %s\",\"nonObjectScripts\":\"scripts must be an object\",\"nonStringScript\":\"script values must be string commands\",\"nonArrayFiles\":\"Invalid 'files' member\",\"invalidFilename\":\"Invalid filename in 'files' list: %s\",\"nonArrayBundleDependencies\":\"Invalid 'bundleDependencies' list. Must be array of package names\",\"nonStringBundleDependency\":\"Invalid bundleDependencies member: %s\",\"nonDependencyBundleDependency\":\"Non-dependency in bundleDependencies: %s\",\"nonObjectDependencies\":\"%s field must be an object\",\"nonStringDependency\":\"Invalid dependency: %s %s\",\"deprecatedArrayDependencies\":\"specifying %s as array is deprecated\",\"deprecatedModules\":\"modules field is deprecated\",\"nonArrayKeywords\":\"keywords should be an array of strings\",\"nonStringKeyword\":\"keywords should be an array of strings\",\"conflictingName\":\"%s is also the name of a node core module.\",\"nonStringDescription\":\"'description' field should be a string\",\"missingDescription\":\"No description\",\"missingReadme\":\"No README data\",\"missingLicense\":\"No license field.\",\"nonEmailUrlBugsString\":\"Bug string field must be url, email, or {email,url}\",\"nonUrlBugsUrlField\":\"bugs.url field must be a string url. Deleted.\",\"nonEmailBugsEmailField\":\"bugs.email field must be a string email. Deleted.\",\"emptyNormalizedBugs\":\"Normalized value of bugs field is an empty object. Deleted.\",\"nonUrlHomepage\":\"homepage field must be a string url. Deleted.\",\"invalidLicense\":\"license should be a valid SPDX license expression\",\"typo\":\"%s should probably be %s.\"}"); + +/***/ }), +/* 402 */ /***/ (function(module, exports, __webpack_require__) { -var licenseIDs = __webpack_require__(373); +"use strict"; + +const path = __webpack_require__(4); +const writeJsonFile = __webpack_require__(403); +const sortKeys = __webpack_require__(407); -function valid(string) { - return licenseIDs.indexOf(string) > -1; +const dependencyKeys = new Set([ + 'dependencies', + 'devDependencies', + 'optionalDependencies', + 'peerDependencies' +]); + +function normalize(packageJson) { + const result = {}; + + for (const key of Object.keys(packageJson)) { + if (!dependencyKeys.has(key)) { + result[key] = packageJson[key]; + } else if (Object.keys(packageJson[key]).length !== 0) { + result[key] = sortKeys(packageJson[key]); + } + } + + return result; } -// Common transpositions of license identifier acronyms -var transpositions = [ - ['APGL', 'AGPL'], - ['Gpl', 'GPL'], - ['GLP', 'GPL'], - ['APL', 'Apache'], - ['ISD', 'ISC'], - ['GLP', 'GPL'], - ['IST', 'ISC'], - ['Claude', 'Clause'], - [' or later', '+'], - [' International', ''], - ['GNU', 'GPL'], - ['GUN', 'GPL'], - ['+', ''], - ['GNU GPL', 'GPL'], - ['GNU/GPL', 'GPL'], - ['GNU GLP', 'GPL'], - ['GNU General Public License', 'GPL'], - ['Gnu public license', 'GPL'], - ['GNU Public License', 'GPL'], - ['GNU GENERAL PUBLIC LICENSE', 'GPL'], - ['MTI', 'MIT'], - ['Mozilla Public License', 'MPL'], - ['WTH', 'WTF'], - ['-License', ''] -]; +module.exports = async (filePath, data, options) => { + if (typeof filePath !== 'string') { + options = data; + data = filePath; + filePath = '.'; + } -var TRANSPOSED = 0; -var CORRECT = 1; + options = { + normalize: true, + ...options, + detectIndent: true + }; -// Simple corrections to nearly valid identifiers. -var transforms = [ - // e.g. 'mit' - function(argument) { - return argument.toUpperCase(); - }, - // e.g. 'MIT ' - function(argument) { - return argument.trim(); - }, - // e.g. 'M.I.T.' - function(argument) { - return argument.replace(/\./g, ''); - }, - // e.g. 'Apache- 2.0' - function(argument) { - return argument.replace(/\s+/g, ''); - }, - // e.g. 'CC BY 4.0'' - function(argument) { - return argument.replace(/\s+/g, '-'); - }, - // e.g. 'LGPLv2.1' - function(argument) { - return argument.replace('v', '-'); - }, - // e.g. 'Apache 2.0' - function(argument) { - return argument.replace(/,?\s*(\d)/, '-$1'); - }, - // e.g. 'GPL 2' - function(argument) { - return argument.replace(/,?\s*(\d)/, '-$1.0'); - }, - // e.g. 'Apache Version 2.0' - function(argument) { - return argument.replace(/,?\s*(V\.|v\.|V|v|Version|version)\s*(\d)/, '-$2'); - }, - // e.g. 'Apache Version 2' - function(argument) { - return argument.replace(/,?\s*(V\.|v\.|V|v|Version|version)\s*(\d)/, '-$2.0'); - }, - // e.g. 'ZLIB' - function(argument) { - return argument[0].toUpperCase() + argument.slice(1); - }, - // e.g. 'MPL/2.0' - function(argument) { - return argument.replace('/', '-'); - }, - // e.g. 'Apache 2' - function(argument) { - return argument - .replace(/\s*V\s*(\d)/, '-$1') - .replace(/(\d)$/, '$1.0'); - }, - // e.g. 'GPL-2.0-' - function(argument) { - return argument.slice(0, argument.length - 1); - }, - // e.g. 'GPL2' - function(argument) { - return argument.replace(/(\d)$/, '-$1.0'); - }, - // e.g. 'BSD 3' - function(argument) { - return argument.replace(/(-| )?(\d)$/, '-$2-Clause'); - }, - // e.g. 'BSD clause 3' - function(argument) { - return argument.replace(/(-| )clause(-| )(\d)/, '-$3-Clause'); - }, - // e.g. 'BY-NC-4.0' - function(argument) { - return 'CC-' + argument; - }, - // e.g. 'BY-NC' - function(argument) { - return 'CC-' + argument + '-4.0'; - }, - // e.g. 'Attribution-NonCommercial' - function(argument) { - return argument - .replace('Attribution', 'BY') - .replace('NonCommercial', 'NC') - .replace('NoDerivatives', 'ND') - .replace(/ (\d)/, '-$1') - .replace(/ ?International/, ''); - }, - // e.g. 'Attribution-NonCommercial' - function(argument) { - return 'CC-' + - argument - .replace('Attribution', 'BY') - .replace('NonCommercial', 'NC') - .replace('NoDerivatives', 'ND') - .replace(/ (\d)/, '-$1') - .replace(/ ?International/, '') + - '-4.0'; + filePath = path.basename(filePath) === 'package.json' ? filePath : path.join(filePath, 'package.json'); + + data = options.normalize ? normalize(data) : data; + + return writeJsonFile(filePath, data, options); +}; + +module.exports.sync = (filePath, data, options) => { + if (typeof filePath !== 'string') { + options = data; + data = filePath; + filePath = '.'; + } + + options = { + normalize: true, + ...options, + detectIndent: true + }; + + filePath = path.basename(filePath) === 'package.json' ? filePath : path.join(filePath, 'package.json'); + + data = options.normalize ? normalize(data) : data; + + writeJsonFile.sync(filePath, data, options); +}; + + +/***/ }), +/* 403 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +const path = __webpack_require__(4); +const fs = __webpack_require__(233); +const writeFileAtomic = __webpack_require__(404); +const sortKeys = __webpack_require__(407); +const makeDir = __webpack_require__(409); +const pify = __webpack_require__(410); +const detectIndent = __webpack_require__(412); + +const init = (fn, filePath, data, options) => { + if (!filePath) { + throw new TypeError('Expected a filepath'); + } + + if (data === undefined) { + throw new TypeError('Expected data to stringify'); + } + + options = Object.assign({ + indent: '\t', + sortKeys: false + }, options); + + if (options.sortKeys) { + data = sortKeys(data, { + deep: true, + compare: typeof options.sortKeys === 'function' ? options.sortKeys : undefined + }); + } + + return fn(filePath, data, options); +}; + +const readFile = filePath => pify(fs.readFile)(filePath, 'utf8').catch(() => {}); + +const main = (filePath, data, options) => { + return (options.detectIndent ? readFile(filePath) : Promise.resolve()) + .then(string => { + const indent = string ? detectIndent(string).indent : options.indent; + const json = JSON.stringify(data, options.replacer, indent); + + return pify(writeFileAtomic)(filePath, `${json}\n`, {mode: options.mode}); + }); +}; + +const mainSync = (filePath, data, options) => { + let {indent} = options; + + if (options.detectIndent) { + try { + const file = fs.readFileSync(filePath, 'utf8'); + indent = detectIndent(file).indent; + } catch (error) { + if (error.code !== 'ENOENT') { + throw error; + } + } + } + + const json = JSON.stringify(data, options.replacer, indent); + + return writeFileAtomic.sync(filePath, `${json}\n`, {mode: options.mode}); +}; + +const writeJsonFile = (filePath, data, options) => { + return makeDir(path.dirname(filePath), {fs}) + .then(() => init(main, filePath, data, options)); +}; + +module.exports = writeJsonFile; +// TODO: Remove this for the next major release +module.exports.default = writeJsonFile; +module.exports.sync = (filePath, data, options) => { + makeDir.sync(path.dirname(filePath), {fs}); + init(mainSync, filePath, data, options); +}; + + +/***/ }), +/* 404 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +module.exports = writeFile +module.exports.sync = writeFileSync +module.exports._getTmpname = getTmpname // for testing +module.exports._cleanupOnExit = cleanupOnExit + +var fs = __webpack_require__(233) +var MurmurHash3 = __webpack_require__(405) +var onExit = __webpack_require__(161) +var path = __webpack_require__(4) +var activeFiles = {} + +// if we run inside of a worker_thread, `process.pid` is not unique +/* istanbul ignore next */ +var threadId = (function getId () { + try { + var workerThreads = __webpack_require__(406) + + /// if we are in main thread, this is set to `0` + return workerThreads.threadId + } catch (e) { + // worker_threads are not available, fallback to 0 + return 0 } -]; +})() -// If all else fails, guess that strings containing certain substrings -// meant to identify certain licenses. -var lastResorts = [ - ['UNLI', 'Unlicense'], - ['WTF', 'WTFPL'], - ['2 CLAUSE', 'BSD-2-Clause'], - ['2-CLAUSE', 'BSD-2-Clause'], - ['3 CLAUSE', 'BSD-3-Clause'], - ['3-CLAUSE', 'BSD-3-Clause'], - ['AFFERO', 'AGPL-3.0'], - ['AGPL', 'AGPL-3.0'], - ['APACHE', 'Apache-2.0'], - ['ARTISTIC', 'Artistic-2.0'], - ['Affero', 'AGPL-3.0'], - ['BEER', 'Beerware'], - ['BOOST', 'BSL-1.0'], - ['BSD', 'BSD-2-Clause'], - ['ECLIPSE', 'EPL-1.0'], - ['FUCK', 'WTFPL'], - ['GNU', 'GPL-3.0'], - ['LGPL', 'LGPL-3.0'], - ['GPL', 'GPL-3.0'], - ['MIT', 'MIT'], - ['MPL', 'MPL-2.0'], - ['X11', 'X11'], - ['ZLIB', 'Zlib'] -]; +var invocations = 0 +function getTmpname (filename) { + return filename + '.' + + MurmurHash3(__filename) + .hash(String(process.pid)) + .hash(String(threadId)) + .hash(String(++invocations)) + .result() +} -var SUBSTRING = 0; -var IDENTIFIER = 1; +function cleanupOnExit (tmpfile) { + return function () { + try { + fs.unlinkSync(typeof tmpfile === 'function' ? tmpfile() : tmpfile) + } catch (_) {} + } +} -var validTransformation = function(identifier) { - for (var i = 0; i < transforms.length; i++) { - var transformed = transforms[i](identifier); - if (transformed !== identifier && valid(transformed)) { - return transformed; +function writeFile (filename, data, options, callback) { + if (options) { + if (options instanceof Function) { + callback = options + options = {} + } else if (typeof options === 'string') { + options = { encoding: options } + } + } else { + options = {} + } + + var Promise = options.Promise || global.Promise + var truename + var fd + var tmpfile + /* istanbul ignore next -- The closure only gets called when onExit triggers */ + var removeOnExitHandler = onExit(cleanupOnExit(() => tmpfile)) + var absoluteName = path.resolve(filename) + + new Promise(function serializeSameFile (resolve) { + // make a queue if it doesn't already exist + if (!activeFiles[absoluteName]) activeFiles[absoluteName] = [] + + activeFiles[absoluteName].push(resolve) // add this job to the queue + if (activeFiles[absoluteName].length === 1) resolve() // kick off the first one + }).then(function getRealPath () { + return new Promise(function (resolve) { + fs.realpath(filename, function (_, realname) { + truename = realname || filename + tmpfile = getTmpname(truename) + resolve() + }) + }) + }).then(function stat () { + return new Promise(function stat (resolve) { + if (options.mode && options.chown) resolve() + else { + // Either mode or chown is not explicitly set + // Default behavior is to copy it from original file + fs.stat(truename, function (err, stats) { + if (err || !stats) resolve() + else { + options = Object.assign({}, options) + + if (options.mode == null) { + options.mode = stats.mode + } + if (options.chown == null && process.getuid) { + options.chown = { uid: stats.uid, gid: stats.gid } + } + resolve() + } + }) + } + }) + }).then(function thenWriteFile () { + return new Promise(function (resolve, reject) { + fs.open(tmpfile, 'w', options.mode, function (err, _fd) { + fd = _fd + if (err) reject(err) + else resolve() + }) + }) + }).then(function write () { + return new Promise(function (resolve, reject) { + if (Buffer.isBuffer(data)) { + fs.write(fd, data, 0, data.length, 0, function (err) { + if (err) reject(err) + else resolve() + }) + } else if (data != null) { + fs.write(fd, String(data), 0, String(options.encoding || 'utf8'), function (err) { + if (err) reject(err) + else resolve() + }) + } else resolve() + }) + }).then(function syncAndClose () { + return new Promise(function (resolve, reject) { + if (options.fsync !== false) { + fs.fsync(fd, function (err) { + if (err) fs.close(fd, () => reject(err)) + else fs.close(fd, resolve) + }) + } else { + fs.close(fd, resolve) + } + }) + }).then(function chown () { + fd = null + if (options.chown) { + return new Promise(function (resolve, reject) { + fs.chown(tmpfile, options.chown.uid, options.chown.gid, function (err) { + if (err) reject(err) + else resolve() + }) + }) } - } - return null; -}; - -var validLastResort = function(identifier) { - var upperCased = identifier.toUpperCase(); - for (var i = 0; i < lastResorts.length; i++) { - var lastResort = lastResorts[i]; - if (upperCased.indexOf(lastResort[SUBSTRING]) > -1) { - return lastResort[IDENTIFIER]; + }).then(function chmod () { + if (options.mode) { + return new Promise(function (resolve, reject) { + fs.chmod(tmpfile, options.mode, function (err) { + if (err) reject(err) + else resolve() + }) + }) } + }).then(function rename () { + return new Promise(function (resolve, reject) { + fs.rename(tmpfile, truename, function (err) { + if (err) reject(err) + else resolve() + }) + }) + }).then(function success () { + removeOnExitHandler() + callback() + }, function fail (err) { + return new Promise(resolve => { + return fd ? fs.close(fd, resolve) : resolve() + }).then(() => { + removeOnExitHandler() + fs.unlink(tmpfile, function () { + callback(err) + }) + }) + }).then(function checkQueue () { + activeFiles[absoluteName].shift() // remove the element added by serializeSameFile + if (activeFiles[absoluteName].length > 0) { + activeFiles[absoluteName][0]() // start next job if one is pending + } else delete activeFiles[absoluteName] + }) +} + +function writeFileSync (filename, data, options) { + if (typeof options === 'string') options = { encoding: options } + else if (!options) options = {} + try { + filename = fs.realpathSync(filename) + } catch (ex) { + // it's ok, it'll happen on a not yet existing file } - return null; -}; + var tmpfile = getTmpname(filename) -var anyCorrection = function(identifier, check) { - for (var i = 0; i < transpositions.length; i++) { - var transposition = transpositions[i]; - var transposed = transposition[TRANSPOSED]; - if (identifier.indexOf(transposed) > -1) { - var corrected = identifier.replace( - transposed, - transposition[CORRECT] - ); - var checked = check(corrected); - if (checked !== null) { - return checked; + if (!options.mode || !options.chown) { + // Either mode or chown is not explicitly set + // Default behavior is to copy it from original file + try { + var stats = fs.statSync(filename) + options = Object.assign({}, options) + if (!options.mode) { + options.mode = stats.mode + } + if (!options.chown && process.getuid) { + options.chown = { uid: stats.uid, gid: stats.gid } } + } catch (ex) { + // ignore stat errors } } - return null; -}; -module.exports = function(identifier) { - identifier = identifier.replace(/\+$/, ''); - if (valid(identifier)) { - return identifier; - } - var transformed = validTransformation(identifier); - if (transformed !== null) { - return transformed; - } - transformed = anyCorrection(identifier, function(argument) { - if (valid(argument)) { - return argument; + var fd + var cleanup = cleanupOnExit(tmpfile) + var removeOnExitHandler = onExit(cleanup) + + try { + fd = fs.openSync(tmpfile, 'w', options.mode) + if (Buffer.isBuffer(data)) { + fs.writeSync(fd, data, 0, data.length, 0) + } else if (data != null) { + fs.writeSync(fd, String(data), 0, String(options.encoding || 'utf8')) } - return validTransformation(argument); - }); - if (transformed !== null) { - return transformed; - } - transformed = validLastResort(identifier); - if (transformed !== null) { - return transformed; - } - transformed = anyCorrection(identifier, validLastResort); - if (transformed !== null) { - return transformed; + if (options.fsync !== false) { + fs.fsyncSync(fd) + } + fs.closeSync(fd) + if (options.chown) fs.chownSync(tmpfile, options.chown.uid, options.chown.gid) + if (options.mode) fs.chmodSync(tmpfile, options.mode) + fs.renameSync(tmpfile, filename) + removeOnExitHandler() + } catch (err) { + if (fd) { + try { + fs.closeSync(fd) + } catch (ex) { + // ignore close errors at this stage, error may have closed fd already. + } + } + removeOnExitHandler() + cleanup() + throw err } - return null; -}; - - -/***/ }), -/* 373 */ -/***/ (function(module) { +} -module.exports = JSON.parse("[\"Glide\",\"Abstyles\",\"AFL-1.1\",\"AFL-1.2\",\"AFL-2.0\",\"AFL-2.1\",\"AFL-3.0\",\"AMPAS\",\"APL-1.0\",\"Adobe-Glyph\",\"APAFML\",\"Adobe-2006\",\"AGPL-1.0\",\"Afmparse\",\"Aladdin\",\"ADSL\",\"AMDPLPA\",\"ANTLR-PD\",\"Apache-1.0\",\"Apache-1.1\",\"Apache-2.0\",\"AML\",\"APSL-1.0\",\"APSL-1.1\",\"APSL-1.2\",\"APSL-2.0\",\"Artistic-1.0\",\"Artistic-1.0-Perl\",\"Artistic-1.0-cl8\",\"Artistic-2.0\",\"AAL\",\"Bahyph\",\"Barr\",\"Beerware\",\"BitTorrent-1.0\",\"BitTorrent-1.1\",\"BSL-1.0\",\"Borceux\",\"BSD-2-Clause\",\"BSD-2-Clause-FreeBSD\",\"BSD-2-Clause-NetBSD\",\"BSD-3-Clause\",\"BSD-3-Clause-Clear\",\"BSD-4-Clause\",\"BSD-Protection\",\"BSD-Source-Code\",\"BSD-3-Clause-Attribution\",\"0BSD\",\"BSD-4-Clause-UC\",\"bzip2-1.0.5\",\"bzip2-1.0.6\",\"Caldera\",\"CECILL-1.0\",\"CECILL-1.1\",\"CECILL-2.0\",\"CECILL-2.1\",\"CECILL-B\",\"CECILL-C\",\"ClArtistic\",\"MIT-CMU\",\"CNRI-Jython\",\"CNRI-Python\",\"CNRI-Python-GPL-Compatible\",\"CPOL-1.02\",\"CDDL-1.0\",\"CDDL-1.1\",\"CPAL-1.0\",\"CPL-1.0\",\"CATOSL-1.1\",\"Condor-1.1\",\"CC-BY-1.0\",\"CC-BY-2.0\",\"CC-BY-2.5\",\"CC-BY-3.0\",\"CC-BY-4.0\",\"CC-BY-ND-1.0\",\"CC-BY-ND-2.0\",\"CC-BY-ND-2.5\",\"CC-BY-ND-3.0\",\"CC-BY-ND-4.0\",\"CC-BY-NC-1.0\",\"CC-BY-NC-2.0\",\"CC-BY-NC-2.5\",\"CC-BY-NC-3.0\",\"CC-BY-NC-4.0\",\"CC-BY-NC-ND-1.0\",\"CC-BY-NC-ND-2.0\",\"CC-BY-NC-ND-2.5\",\"CC-BY-NC-ND-3.0\",\"CC-BY-NC-ND-4.0\",\"CC-BY-NC-SA-1.0\",\"CC-BY-NC-SA-2.0\",\"CC-BY-NC-SA-2.5\",\"CC-BY-NC-SA-3.0\",\"CC-BY-NC-SA-4.0\",\"CC-BY-SA-1.0\",\"CC-BY-SA-2.0\",\"CC-BY-SA-2.5\",\"CC-BY-SA-3.0\",\"CC-BY-SA-4.0\",\"CC0-1.0\",\"Crossword\",\"CrystalStacker\",\"CUA-OPL-1.0\",\"Cube\",\"curl\",\"D-FSL-1.0\",\"diffmark\",\"WTFPL\",\"DOC\",\"Dotseqn\",\"DSDP\",\"dvipdfm\",\"EPL-1.0\",\"ECL-1.0\",\"ECL-2.0\",\"eGenix\",\"EFL-1.0\",\"EFL-2.0\",\"MIT-advertising\",\"MIT-enna\",\"Entessa\",\"ErlPL-1.1\",\"EUDatagrid\",\"EUPL-1.0\",\"EUPL-1.1\",\"Eurosym\",\"Fair\",\"MIT-feh\",\"Frameworx-1.0\",\"FreeImage\",\"FTL\",\"FSFAP\",\"FSFUL\",\"FSFULLR\",\"Giftware\",\"GL2PS\",\"Glulxe\",\"AGPL-3.0\",\"GFDL-1.1\",\"GFDL-1.2\",\"GFDL-1.3\",\"GPL-1.0\",\"GPL-2.0\",\"GPL-3.0\",\"LGPL-2.1\",\"LGPL-3.0\",\"LGPL-2.0\",\"gnuplot\",\"gSOAP-1.3b\",\"HaskellReport\",\"HPND\",\"IBM-pibs\",\"IPL-1.0\",\"ICU\",\"ImageMagick\",\"iMatix\",\"Imlib2\",\"IJG\",\"Info-ZIP\",\"Intel-ACPI\",\"Intel\",\"Interbase-1.0\",\"IPA\",\"ISC\",\"JasPer-2.0\",\"JSON\",\"LPPL-1.0\",\"LPPL-1.1\",\"LPPL-1.2\",\"LPPL-1.3a\",\"LPPL-1.3c\",\"Latex2e\",\"BSD-3-Clause-LBNL\",\"Leptonica\",\"LGPLLR\",\"Libpng\",\"libtiff\",\"LAL-1.2\",\"LAL-1.3\",\"LiLiQ-P-1.1\",\"LiLiQ-Rplus-1.1\",\"LiLiQ-R-1.1\",\"LPL-1.02\",\"LPL-1.0\",\"MakeIndex\",\"MTLL\",\"MS-PL\",\"MS-RL\",\"MirOS\",\"MITNFA\",\"MIT\",\"Motosoto\",\"MPL-1.0\",\"MPL-1.1\",\"MPL-2.0\",\"MPL-2.0-no-copyleft-exception\",\"mpich2\",\"Multics\",\"Mup\",\"NASA-1.3\",\"Naumen\",\"NBPL-1.0\",\"NetCDF\",\"NGPL\",\"NOSL\",\"NPL-1.0\",\"NPL-1.1\",\"Newsletr\",\"NLPL\",\"Nokia\",\"NPOSL-3.0\",\"NLOD-1.0\",\"Noweb\",\"NRL\",\"NTP\",\"Nunit\",\"OCLC-2.0\",\"ODbL-1.0\",\"PDDL-1.0\",\"OCCT-PL\",\"OGTSL\",\"OLDAP-2.2.2\",\"OLDAP-1.1\",\"OLDAP-1.2\",\"OLDAP-1.3\",\"OLDAP-1.4\",\"OLDAP-2.0\",\"OLDAP-2.0.1\",\"OLDAP-2.1\",\"OLDAP-2.2\",\"OLDAP-2.2.1\",\"OLDAP-2.3\",\"OLDAP-2.4\",\"OLDAP-2.5\",\"OLDAP-2.6\",\"OLDAP-2.7\",\"OLDAP-2.8\",\"OML\",\"OPL-1.0\",\"OSL-1.0\",\"OSL-1.1\",\"OSL-2.0\",\"OSL-2.1\",\"OSL-3.0\",\"OpenSSL\",\"OSET-PL-2.1\",\"PHP-3.0\",\"PHP-3.01\",\"Plexus\",\"PostgreSQL\",\"psfrag\",\"psutils\",\"Python-2.0\",\"QPL-1.0\",\"Qhull\",\"Rdisc\",\"RPSL-1.0\",\"RPL-1.1\",\"RPL-1.5\",\"RHeCos-1.1\",\"RSCPL\",\"RSA-MD\",\"Ruby\",\"SAX-PD\",\"Saxpath\",\"SCEA\",\"SWL\",\"SMPPL\",\"Sendmail\",\"SGI-B-1.0\",\"SGI-B-1.1\",\"SGI-B-2.0\",\"OFL-1.0\",\"OFL-1.1\",\"SimPL-2.0\",\"Sleepycat\",\"SNIA\",\"Spencer-86\",\"Spencer-94\",\"Spencer-99\",\"SMLNJ\",\"SugarCRM-1.1.3\",\"SISSL\",\"SISSL-1.2\",\"SPL-1.0\",\"Watcom-1.0\",\"TCL\",\"Unlicense\",\"TMate\",\"TORQUE-1.1\",\"TOSL\",\"Unicode-TOU\",\"UPL-1.0\",\"NCSA\",\"Vim\",\"VOSTROM\",\"VSL-1.0\",\"W3C-19980720\",\"W3C\",\"Wsuipa\",\"Xnet\",\"X11\",\"Xerox\",\"XFree86-1.1\",\"xinetd\",\"xpp\",\"XSkat\",\"YPL-1.0\",\"YPL-1.1\",\"Zed\",\"Zend-2.0\",\"Zimbra-1.3\",\"Zimbra-1.4\",\"Zlib\",\"zlib-acknowledgement\",\"ZPL-1.1\",\"ZPL-2.0\",\"ZPL-2.1\",\"BSD-3-Clause-No-Nuclear-License\",\"BSD-3-Clause-No-Nuclear-Warranty\",\"BSD-3-Clause-No-Nuclear-License-2014\",\"eCos-2.0\",\"GPL-2.0-with-autoconf-exception\",\"GPL-2.0-with-bison-exception\",\"GPL-2.0-with-classpath-exception\",\"GPL-2.0-with-font-exception\",\"GPL-2.0-with-GCC-exception\",\"GPL-3.0-with-autoconf-exception\",\"GPL-3.0-with-GCC-exception\",\"StandardML-NJ\",\"WXwindows\"]"); /***/ }), -/* 374 */ +/* 405 */ /***/ (function(module, exports, __webpack_require__) { -"use strict"; +/** + * @preserve + * JS Implementation of incremental MurmurHash3 (r150) (as of May 10, 2013) + * + * @author
Jens Taylor + * @see http://github.com/homebrewing/brauhaus-diff + * @author Gary Court + * @see http://github.com/garycourt/murmurhash-js + * @author Austin Appleby + * @see http://sites.google.com/site/murmurhash/ + */ +(function(){ + var cache; -var url = __webpack_require__(203) -var gitHosts = __webpack_require__(375) -var GitHost = module.exports = __webpack_require__(376) + // Call this function without `new` to use the cached object (good for + // single-threaded environments), or with `new` to create a new object. + // + // @param {string} key A UTF-16 or ASCII string + // @param {number} seed An optional positive integer + // @return {object} A MurmurHash3 object for incremental hashing + function MurmurHash3(key, seed) { + var m = this instanceof MurmurHash3 ? this : cache; + m.reset(seed) + if (typeof key === 'string' && key.length > 0) { + m.hash(key); + } -var protocolToRepresentationMap = { - 'git+ssh:': 'sshurl', - 'git+https:': 'https', - 'ssh:': 'sshurl', - 'git:': 'git' -} + if (m !== this) { + return m; + } + }; -function protocolToRepresentation (protocol) { - return protocolToRepresentationMap[protocol] || protocol.slice(0, -1) -} + // Incrementally add a string to this hash + // + // @param {string} key A UTF-16 or ASCII string + // @return {object} this + MurmurHash3.prototype.hash = function(key) { + var h1, k1, i, top, len; -var authProtocols = { - 'git:': true, - 'https:': true, - 'git+https:': true, - 'http:': true, - 'git+http:': true -} + len = key.length; + this.len += len; -var cache = {} + k1 = this.k1; + i = 0; + switch (this.rem) { + case 0: k1 ^= len > i ? (key.charCodeAt(i++) & 0xffff) : 0; + case 1: k1 ^= len > i ? (key.charCodeAt(i++) & 0xffff) << 8 : 0; + case 2: k1 ^= len > i ? (key.charCodeAt(i++) & 0xffff) << 16 : 0; + case 3: + k1 ^= len > i ? (key.charCodeAt(i) & 0xff) << 24 : 0; + k1 ^= len > i ? (key.charCodeAt(i++) & 0xff00) >> 8 : 0; + } -module.exports.fromUrl = function (giturl, opts) { - if (typeof giturl !== 'string') return - var key = giturl + JSON.stringify(opts || {}) + this.rem = (len + this.rem) & 3; // & 3 is same as % 4 + len -= this.rem; + if (len > 0) { + h1 = this.h1; + while (1) { + k1 = (k1 * 0x2d51 + (k1 & 0xffff) * 0xcc9e0000) & 0xffffffff; + k1 = (k1 << 15) | (k1 >>> 17); + k1 = (k1 * 0x3593 + (k1 & 0xffff) * 0x1b870000) & 0xffffffff; - if (!(key in cache)) { - cache[key] = fromUrl(giturl, opts) - } + h1 ^= k1; + h1 = (h1 << 13) | (h1 >>> 19); + h1 = (h1 * 5 + 0xe6546b64) & 0xffffffff; - return cache[key] -} + if (i >= len) { + break; + } -function fromUrl (giturl, opts) { - if (giturl == null || giturl === '') return - var url = fixupUnqualifiedGist( - isGitHubShorthand(giturl) ? 'github:' + giturl : giturl - ) - var parsed = parseGitUrl(url) - var shortcutMatch = url.match(/^([^:]+):(?:[^@]+@)?(?:([^/]*)\/)?([^#]+)/) - var matches = Object.keys(gitHosts).map(function (gitHostName) { - try { - var gitHostInfo = gitHosts[gitHostName] - var auth = null - if (parsed.auth && authProtocols[parsed.protocol]) { - auth = parsed.auth - } - var committish = parsed.hash ? decodeURIComponent(parsed.hash.substr(1)) : null - var user = null - var project = null - var defaultRepresentation = null - if (shortcutMatch && shortcutMatch[1] === gitHostName) { - user = shortcutMatch[2] && decodeURIComponent(shortcutMatch[2]) - project = decodeURIComponent(shortcutMatch[3].replace(/\.git$/, '')) - defaultRepresentation = 'shortcut' - } else { - if (parsed.host && parsed.host !== gitHostInfo.domain && parsed.host.replace(/^www[.]/, '') !== gitHostInfo.domain) return - if (!gitHostInfo.protocols_re.test(parsed.protocol)) return - if (!parsed.path) return - var pathmatch = gitHostInfo.pathmatch - var matched = parsed.path.match(pathmatch) - if (!matched) return - /* istanbul ignore else */ - if (matched[1] !== null && matched[1] !== undefined) { - user = decodeURIComponent(matched[1].replace(/^:/, '')) - } - project = decodeURIComponent(matched[2]) - defaultRepresentation = protocolToRepresentation(parsed.protocol) - } - return new GitHost(gitHostName, user, auth, project, committish, defaultRepresentation, opts) - } catch (ex) { - /* istanbul ignore else */ - if (ex instanceof URIError) { - } else throw ex - } - }).filter(function (gitHostInfo) { return gitHostInfo }) - if (matches.length !== 1) return - return matches[0] -} + k1 = ((key.charCodeAt(i++) & 0xffff)) ^ + ((key.charCodeAt(i++) & 0xffff) << 8) ^ + ((key.charCodeAt(i++) & 0xffff) << 16); + top = key.charCodeAt(i++); + k1 ^= ((top & 0xff) << 24) ^ + ((top & 0xff00) >> 8); + } -function isGitHubShorthand (arg) { - // Note: This does not fully test the git ref format. - // See https://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html - // - // The only way to do this properly would be to shell out to - // git-check-ref-format, and as this is a fast sync function, - // we don't want to do that. Just let git fail if it turns - // out that the commit-ish is invalid. - // GH usernames cannot start with . or - - return /^[^:@%/\s.-][^:@%/\s]*[/][^:@\s/%]+(?:#.*)?$/.test(arg) -} + k1 = 0; + switch (this.rem) { + case 3: k1 ^= (key.charCodeAt(i + 2) & 0xffff) << 16; + case 2: k1 ^= (key.charCodeAt(i + 1) & 0xffff) << 8; + case 1: k1 ^= (key.charCodeAt(i) & 0xffff); + } -function fixupUnqualifiedGist (giturl) { - // necessary for round-tripping gists - var parsed = url.parse(giturl) - if (parsed.protocol === 'gist:' && parsed.host && !parsed.path) { - return parsed.protocol + '/' + parsed.host - } else { - return giturl - } -} + this.h1 = h1; + } -function parseGitUrl (giturl) { - var matched = giturl.match(/^([^@]+)@([^:/]+):[/]?((?:[^/]+[/])?[^/]+?)(?:[.]git)?(#.*)?$/) - if (!matched) { - var legacy = url.parse(giturl) - // If we don't have url.URL, then sorry, this is just not fixable. - // This affects Node <= 6.12. - if (legacy.auth && typeof url.URL === 'function') { - // git urls can be in the form of scp-style/ssh-connect strings, like - // git+ssh://user@host.com:some/path, which the legacy url parser - // supports, but WhatWG url.URL class does not. However, the legacy - // parser de-urlencodes the username and password, so something like - // https://user%3An%40me:p%40ss%3Aword@x.com/ becomes - // https://user:n@me:p@ss:word@x.com/ which is all kinds of wrong. - // Pull off just the auth and host, so we dont' get the confusing - // scp-style URL, then pass that to the WhatWG parser to get the - // auth properly escaped. - var authmatch = giturl.match(/[^@]+@[^:/]+/) - /* istanbul ignore else - this should be impossible */ - if (authmatch) { - var whatwg = new url.URL(authmatch[0]) - legacy.auth = whatwg.username || '' - if (whatwg.password) legacy.auth += ':' + whatwg.password - } - } - return legacy - } - return { - protocol: 'git+ssh:', - slashes: true, - auth: matched[1], - host: matched[2], - port: null, - hostname: matched[2], - hash: matched[4], - search: null, - query: null, - pathname: '/' + matched[3], - path: '/' + matched[3], - href: 'git+ssh://' + matched[1] + '@' + matched[2] + - '/' + matched[3] + (matched[4] || '') - } -} + this.k1 = k1; + return this; + }; + // Get the result of this hash + // + // @return {number} The 32-bit hash + MurmurHash3.prototype.result = function() { + var k1, h1; + + k1 = this.k1; + h1 = this.h1; -/***/ }), -/* 375 */ -/***/ (function(module, exports, __webpack_require__) { + if (k1 > 0) { + k1 = (k1 * 0x2d51 + (k1 & 0xffff) * 0xcc9e0000) & 0xffffffff; + k1 = (k1 << 15) | (k1 >>> 17); + k1 = (k1 * 0x3593 + (k1 & 0xffff) * 0x1b870000) & 0xffffffff; + h1 ^= k1; + } -"use strict"; + h1 ^= this.len; + h1 ^= h1 >>> 16; + h1 = (h1 * 0xca6b + (h1 & 0xffff) * 0x85eb0000) & 0xffffffff; + h1 ^= h1 >>> 13; + h1 = (h1 * 0xae35 + (h1 & 0xffff) * 0xc2b20000) & 0xffffffff; + h1 ^= h1 >>> 16; -var gitHosts = module.exports = { - github: { - // First two are insecure and generally shouldn't be used any more, but - // they are still supported. - 'protocols': [ 'git', 'http', 'git+ssh', 'git+https', 'ssh', 'https' ], - 'domain': 'github.com', - 'treepath': 'tree', - 'filetemplate': 'https://{auth@}raw.githubusercontent.com/{user}/{project}/{committish}/{path}', - 'bugstemplate': 'https://{domain}/{user}/{project}/issues', - 'gittemplate': 'git://{auth@}{domain}/{user}/{project}.git{#committish}', - 'tarballtemplate': 'https://codeload.{domain}/{user}/{project}/tar.gz/{committish}' - }, - bitbucket: { - 'protocols': [ 'git+ssh', 'git+https', 'ssh', 'https' ], - 'domain': 'bitbucket.org', - 'treepath': 'src', - 'tarballtemplate': 'https://{domain}/{user}/{project}/get/{committish}.tar.gz' - }, - gitlab: { - 'protocols': [ 'git+ssh', 'git+https', 'ssh', 'https' ], - 'domain': 'gitlab.com', - 'treepath': 'tree', - 'bugstemplate': 'https://{domain}/{user}/{project}/issues', - 'httpstemplate': 'git+https://{auth@}{domain}/{user}/{projectPath}.git{#committish}', - 'tarballtemplate': 'https://{domain}/{user}/{project}/repository/archive.tar.gz?ref={committish}', - 'pathmatch': /^[/]([^/]+)[/]((?!.*(\/-\/|\/repository\/archive\.tar\.gz\?=.*|\/repository\/[^/]+\/archive.tar.gz$)).*?)(?:[.]git|[/])?$/ - }, - gist: { - 'protocols': [ 'git', 'git+ssh', 'git+https', 'ssh', 'https' ], - 'domain': 'gist.github.com', - 'pathmatch': /^[/](?:([^/]+)[/])?([a-z0-9]{32,})(?:[.]git)?$/, - 'filetemplate': 'https://gist.githubusercontent.com/{user}/{project}/raw{/committish}/{path}', - 'bugstemplate': 'https://{domain}/{project}', - 'gittemplate': 'git://{domain}/{project}.git{#committish}', - 'sshtemplate': 'git@{domain}:/{project}.git{#committish}', - 'sshurltemplate': 'git+ssh://git@{domain}/{project}.git{#committish}', - 'browsetemplate': 'https://{domain}/{project}{/committish}', - 'browsefiletemplate': 'https://{domain}/{project}{/committish}{#path}', - 'docstemplate': 'https://{domain}/{project}{/committish}', - 'httpstemplate': 'git+https://{domain}/{project}.git{#committish}', - 'shortcuttemplate': '{type}:{project}{#committish}', - 'pathtemplate': '{project}{#committish}', - 'tarballtemplate': 'https://codeload.github.com/gist/{project}/tar.gz/{committish}', - 'hashformat': function (fragment) { - return 'file-' + formatHashFragment(fragment) - } - } -} + return h1 >>> 0; + }; -var gitHostDefaults = { - 'sshtemplate': 'git@{domain}:{user}/{project}.git{#committish}', - 'sshurltemplate': 'git+ssh://git@{domain}/{user}/{project}.git{#committish}', - 'browsetemplate': 'https://{domain}/{user}/{project}{/tree/committish}', - 'browsefiletemplate': 'https://{domain}/{user}/{project}/{treepath}/{committish}/{path}{#fragment}', - 'docstemplate': 'https://{domain}/{user}/{project}{/tree/committish}#readme', - 'httpstemplate': 'git+https://{auth@}{domain}/{user}/{project}.git{#committish}', - 'filetemplate': 'https://{domain}/{user}/{project}/raw/{committish}/{path}', - 'shortcuttemplate': '{type}:{user}/{project}{#committish}', - 'pathtemplate': '{user}/{project}{#committish}', - 'pathmatch': /^[/]([^/]+)[/]([^/]+?)(?:[.]git|[/])?$/, - 'hashformat': formatHashFragment -} + // Reset the hash object for reuse + // + // @param {number} seed An optional positive integer + MurmurHash3.prototype.reset = function(seed) { + this.h1 = typeof seed === 'number' ? seed : 0; + this.rem = this.k1 = this.len = 0; + return this; + }; -Object.keys(gitHosts).forEach(function (name) { - Object.keys(gitHostDefaults).forEach(function (key) { - if (gitHosts[name][key]) return - gitHosts[name][key] = gitHostDefaults[key] - }) - gitHosts[name].protocols_re = RegExp('^(' + - gitHosts[name].protocols.map(function (protocol) { - return protocol.replace(/([\\+*{}()[\]$^|])/g, '\\$1') - }).join('|') + '):$') -}) + // A cached object to use. This can be safely used if you're in a single- + // threaded environment, otherwise you need to create new hashes to use. + cache = new MurmurHash3(); -function formatHashFragment (fragment) { - return fragment.toLowerCase().replace(/^\W+|\/|\W+$/g, '').replace(/\W+/g, '-') -} + if (true) { + module.exports = MurmurHash3; + } else {} +}()); /***/ }), -/* 376 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -var gitHosts = __webpack_require__(375) -/* eslint-disable node/no-deprecated-api */ - -// copy-pasta util._extend from node's source, to avoid pulling -// the whole util module into peoples' webpack bundles. -/* istanbul ignore next */ -var extend = Object.assign || function _extend (target, source) { - // Don't do anything if source isn't an object - if (source === null || typeof source !== 'object') return target +/* 406 */ +/***/ (function(module, exports) { - var keys = Object.keys(source) - var i = keys.length - while (i--) { - target[keys[i]] = source[keys[i]] - } - return target -} +module.exports = require(undefined); -module.exports = GitHost -function GitHost (type, user, auth, project, committish, defaultRepresentation, opts) { - var gitHostInfo = this - gitHostInfo.type = type - Object.keys(gitHosts[type]).forEach(function (key) { - gitHostInfo[key] = gitHosts[type][key] - }) - gitHostInfo.user = user - gitHostInfo.auth = auth - gitHostInfo.project = project - gitHostInfo.committish = committish - gitHostInfo.default = defaultRepresentation - gitHostInfo.opts = opts || {} -} +/***/ }), +/* 407 */ +/***/ (function(module, exports, __webpack_require__) { -GitHost.prototype.hash = function () { - return this.committish ? '#' + this.committish : '' -} +"use strict"; -GitHost.prototype._fill = function (template, opts) { - if (!template) return - var vars = extend({}, opts) - vars.path = vars.path ? vars.path.replace(/^[/]+/g, '') : '' - opts = extend(extend({}, this.opts), opts) - var self = this - Object.keys(this).forEach(function (key) { - if (self[key] != null && vars[key] == null) vars[key] = self[key] - }) - var rawAuth = vars.auth - var rawcommittish = vars.committish - var rawFragment = vars.fragment - var rawPath = vars.path - var rawProject = vars.project - Object.keys(vars).forEach(function (key) { - var value = vars[key] - if ((key === 'path' || key === 'project') && typeof value === 'string') { - vars[key] = value.split('/').map(function (pathComponent) { - return encodeURIComponent(pathComponent) - }).join('/') - } else { - vars[key] = encodeURIComponent(value) - } - }) - vars['auth@'] = rawAuth ? rawAuth + '@' : '' - vars['#fragment'] = rawFragment ? '#' + this.hashformat(rawFragment) : '' - vars.fragment = vars.fragment ? vars.fragment : '' - vars['#path'] = rawPath ? '#' + this.hashformat(rawPath) : '' - vars['/path'] = vars.path ? '/' + vars.path : '' - vars.projectPath = rawProject.split('/').map(encodeURIComponent).join('/') - if (opts.noCommittish) { - vars['#committish'] = '' - vars['/tree/committish'] = '' - vars['/committish'] = '' - vars.committish = '' - } else { - vars['#committish'] = rawcommittish ? '#' + rawcommittish : '' - vars['/tree/committish'] = vars.committish - ? '/' + vars.treepath + '/' + vars.committish - : '' - vars['/committish'] = vars.committish ? '/' + vars.committish : '' - vars.committish = vars.committish || 'master' - } - var res = template - Object.keys(vars).forEach(function (key) { - res = res.replace(new RegExp('[{]' + key + '[}]', 'g'), vars[key]) - }) - if (opts.noGitPlus) { - return res.replace(/^git[+]/, '') - } else { - return res - } -} +const isPlainObj = __webpack_require__(408); -GitHost.prototype.ssh = function (opts) { - return this._fill(this.sshtemplate, opts) -} +module.exports = (obj, opts) => { + if (!isPlainObj(obj)) { + throw new TypeError('Expected a plain object'); + } -GitHost.prototype.sshurl = function (opts) { - return this._fill(this.sshurltemplate, opts) -} + opts = opts || {}; -GitHost.prototype.browse = function (P, F, opts) { - if (typeof P === 'string') { - if (typeof F !== 'string') { - opts = F - F = null - } - return this._fill(this.browsefiletemplate, extend({ - fragment: F, - path: P - }, opts)) - } else { - return this._fill(this.browsetemplate, P) - } -} + // DEPRECATED + if (typeof opts === 'function') { + throw new TypeError('Specify the compare function as an option instead'); + } -GitHost.prototype.docs = function (opts) { - return this._fill(this.docstemplate, opts) -} + const deep = opts.deep; + const seenInput = []; + const seenOutput = []; -GitHost.prototype.bugs = function (opts) { - return this._fill(this.bugstemplate, opts) -} + const sortKeys = x => { + const seenIndex = seenInput.indexOf(x); -GitHost.prototype.https = function (opts) { - return this._fill(this.httpstemplate, opts) -} + if (seenIndex !== -1) { + return seenOutput[seenIndex]; + } -GitHost.prototype.git = function (opts) { - return this._fill(this.gittemplate, opts) -} + const ret = {}; + const keys = Object.keys(x).sort(opts.compare); -GitHost.prototype.shortcut = function (opts) { - return this._fill(this.shortcuttemplate, opts) -} + seenInput.push(x); + seenOutput.push(ret); -GitHost.prototype.path = function (opts) { - return this._fill(this.pathtemplate, opts) -} + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + const val = x[key]; -GitHost.prototype.tarball = function (opts_) { - var opts = extend({}, opts_, { noCommittish: false }) - return this._fill(this.tarballtemplate, opts) -} + if (deep && Array.isArray(val)) { + const retArr = []; -GitHost.prototype.file = function (P, opts) { - return this._fill(this.filetemplate, extend({ path: P }, opts)) -} + for (let j = 0; j < val.length; j++) { + retArr[j] = isPlainObj(val[j]) ? sortKeys(val[j]) : val[j]; + } -GitHost.prototype.getDefaultRepresentation = function () { - return this.default -} + ret[key] = retArr; + continue; + } -GitHost.prototype.toString = function (opts) { - if (this.default && typeof this[this.default] === 'function') return this[this.default](opts) - return this.sshurl(opts) -} + ret[key] = deep && isPlainObj(val) ? sortKeys(val) : val; + } + + return ret; + }; + + return sortKeys(obj); +}; /***/ }), -/* 377 */ +/* 408 */ /***/ (function(module, exports, __webpack_require__) { -var async = __webpack_require__(378); -async.core = __webpack_require__(388); -async.isCore = __webpack_require__(390); -async.sync = __webpack_require__(391); +"use strict"; -module.exports = async; +var toString = Object.prototype.toString; + +module.exports = function (x) { + var prototype; + return toString.call(x) === '[object Object]' && (prototype = Object.getPrototypeOf(x), prototype === null || prototype === Object.getPrototypeOf({})); +}; /***/ }), -/* 378 */ +/* 409 */ /***/ (function(module, exports, __webpack_require__) { -var fs = __webpack_require__(132); -var path = __webpack_require__(4); -var caller = __webpack_require__(379); -var nodeModulesPaths = __webpack_require__(380); -var normalizeOptions = __webpack_require__(382); -var isCore = __webpack_require__(383); +"use strict"; -var realpathFS = fs.realpath && typeof fs.realpath.native === 'function' ? fs.realpath.native : fs.realpath; +const fs = __webpack_require__(132); +const path = __webpack_require__(4); +const pify = __webpack_require__(410); +const semver = __webpack_require__(411); -var defaultIsFile = function isFile(file, cb) { - fs.stat(file, function (err, stat) { - if (!err) { - return cb(null, stat.isFile() || stat.isFIFO()); - } - if (err.code === 'ENOENT' || err.code === 'ENOTDIR') return cb(null, false); - return cb(err); - }); +const defaults = { + mode: 0o777 & (~process.umask()), + fs }; -var defaultIsDir = function isDirectory(dir, cb) { - fs.stat(dir, function (err, stat) { - if (!err) { - return cb(null, stat.isDirectory()); - } - if (err.code === 'ENOENT' || err.code === 'ENOTDIR') return cb(null, false); - return cb(err); - }); -}; +const useNativeRecursiveOption = semver.satisfies(process.version, '>=10.12.0'); -var defaultRealpath = function realpath(x, cb) { - realpathFS(x, function (realpathErr, realPath) { - if (realpathErr && realpathErr.code !== 'ENOENT') cb(realpathErr); - else cb(null, realpathErr ? x : realPath); - }); -}; +// https://github.com/nodejs/node/issues/8987 +// https://github.com/libuv/libuv/pull/1088 +const checkPath = pth => { + if (process.platform === 'win32') { + const pathHasInvalidWinCharacters = /[<>:"|?*]/.test(pth.replace(path.parse(pth).root, '')); -var maybeRealpath = function maybeRealpath(realpath, x, opts, cb) { - if (opts && opts.preserveSymlinks === false) { - realpath(x, cb); - } else { - cb(null, x); - } + if (pathHasInvalidWinCharacters) { + const error = new Error(`Path contains invalid characters: ${pth}`); + error.code = 'EINVAL'; + throw error; + } + } }; -var defaultReadPackage = function defaultReadPackage(readFile, pkgfile, cb) { - readFile(pkgfile, function (readFileErr, body) { - if (readFileErr) cb(readFileErr); - else { - try { - var pkg = JSON.parse(body); - cb(null, pkg); - } catch (jsonErr) { - cb(null); - } - } - }); +const permissionError = pth => { + // This replicates the exception of `fs.mkdir` with native the + // `recusive` option when run on an invalid drive under Windows. + const error = new Error(`operation not permitted, mkdir '${pth}'`); + error.code = 'EPERM'; + error.errno = -4048; + error.path = pth; + error.syscall = 'mkdir'; + return error; }; -var getPackageCandidates = function getPackageCandidates(x, start, opts) { - var dirs = nodeModulesPaths(start, opts, x); - for (var i = 0; i < dirs.length; i++) { - dirs[i] = path.join(dirs[i], x); - } - return dirs; -}; +const makeDir = (input, options) => Promise.resolve().then(() => { + checkPath(input); + options = Object.assign({}, defaults, options); -module.exports = function resolve(x, options, callback) { - var cb = callback; - var opts = options; - if (typeof options === 'function') { - cb = opts; - opts = {}; - } - if (typeof x !== 'string') { - var err = new TypeError('Path must be a string.'); - return process.nextTick(function () { - cb(err); - }); - } + // TODO: Use util.promisify when targeting Node.js 8 + const mkdir = pify(options.fs.mkdir); + const stat = pify(options.fs.stat); - opts = normalizeOptions(x, opts); + if (useNativeRecursiveOption && options.fs.mkdir === fs.mkdir) { + const pth = path.resolve(input); - var isFile = opts.isFile || defaultIsFile; - var isDirectory = opts.isDirectory || defaultIsDir; - var readFile = opts.readFile || fs.readFile; - var realpath = opts.realpath || defaultRealpath; - var readPackage = opts.readPackage || defaultReadPackage; - if (opts.readFile && opts.readPackage) { - var conflictErr = new TypeError('`readFile` and `readPackage` are mutually exclusive.'); - return process.nextTick(function () { - cb(conflictErr); - }); - } - var packageIterator = opts.packageIterator; + return mkdir(pth, { + mode: options.mode, + recursive: true + }).then(() => pth); + } - var extensions = opts.extensions || ['.js']; - var includeCoreModules = opts.includeCoreModules !== false; - var basedir = opts.basedir || path.dirname(caller()); - var parent = opts.filename || basedir; + const make = pth => { + return mkdir(pth, options.mode) + .then(() => pth) + .catch(error => { + if (error.code === 'EPERM') { + throw error; + } - opts.paths = opts.paths || []; + if (error.code === 'ENOENT') { + if (path.dirname(pth) === pth) { + throw permissionError(pth); + } - // ensure that `basedir` is an absolute path at this point, resolving against the process' current working directory - var absoluteStart = path.resolve(basedir); + if (error.message.includes('null bytes')) { + throw error; + } - maybeRealpath( - realpath, - absoluteStart, - opts, - function (err, realStart) { - if (err) cb(err); - else init(realStart); - } - ); + return make(path.dirname(pth)).then(() => make(pth)); + } - var res; - function init(basedir) { - if ((/^(?:\.\.?(?:\/|$)|\/|([A-Za-z]:)?[/\\])/).test(x)) { - res = path.resolve(basedir, x); - if (x === '.' || x === '..' || x.slice(-1) === '/') res += '/'; - if ((/\/$/).test(x) && res === basedir) { - loadAsDirectory(res, opts.package, onfile); - } else loadAsFile(res, opts.package, onfile); - } else if (includeCoreModules && isCore(x)) { - return cb(null, x); - } else loadNodeModules(x, basedir, function (err, n, pkg) { - if (err) cb(err); - else if (n) { - return maybeRealpath(realpath, n, opts, function (err, realN) { - if (err) { - cb(err); - } else { - cb(null, realN, pkg); - } - }); - } else { - var moduleError = new Error("Cannot find module '" + x + "' from '" + parent + "'"); - moduleError.code = 'MODULE_NOT_FOUND'; - cb(moduleError); - } - }); - } + return stat(pth) + .then(stats => stats.isDirectory() ? pth : Promise.reject()) + .catch(() => { + throw error; + }); + }); + }; - function onfile(err, m, pkg) { - if (err) cb(err); - else if (m) cb(null, m, pkg); - else loadAsDirectory(res, function (err, d, pkg) { - if (err) cb(err); - else if (d) { - maybeRealpath(realpath, d, opts, function (err, realD) { - if (err) { - cb(err); - } else { - cb(null, realD, pkg); - } - }); - } else { - var moduleError = new Error("Cannot find module '" + x + "' from '" + parent + "'"); - moduleError.code = 'MODULE_NOT_FOUND'; - cb(moduleError); - } - }); - } + return make(path.resolve(input)); +}); - function loadAsFile(x, thePackage, callback) { - var loadAsFilePackage = thePackage; - var cb = callback; - if (typeof loadAsFilePackage === 'function') { - cb = loadAsFilePackage; - loadAsFilePackage = undefined; - } +module.exports = makeDir; +module.exports.default = makeDir; - var exts = [''].concat(extensions); - load(exts, x, loadAsFilePackage); +module.exports.sync = (input, options) => { + checkPath(input); + options = Object.assign({}, defaults, options); - function load(exts, x, loadPackage) { - if (exts.length === 0) return cb(null, undefined, loadPackage); - var file = x + exts[0]; + if (useNativeRecursiveOption && options.fs.mkdirSync === fs.mkdirSync) { + const pth = path.resolve(input); - var pkg = loadPackage; - if (pkg) onpkg(null, pkg); - else loadpkg(path.dirname(file), onpkg); + fs.mkdirSync(pth, { + mode: options.mode, + recursive: true + }); - function onpkg(err, pkg_, dir) { - pkg = pkg_; - if (err) return cb(err); - if (dir && pkg && opts.pathFilter) { - var rfile = path.relative(dir, file); - var rel = rfile.slice(0, rfile.length - exts[0].length); - var r = opts.pathFilter(pkg, x, rel); - if (r) return load( - [''].concat(extensions.slice()), - path.resolve(dir, r), - pkg - ); - } - isFile(file, onex); - } - function onex(err, ex) { - if (err) return cb(err); - if (ex) return cb(null, file, pkg); - load(exts.slice(1), x, pkg); - } - } - } + return pth; + } - function loadpkg(dir, cb) { - if (dir === '' || dir === '/') return cb(null); - if (process.platform === 'win32' && (/^\w:[/\\]*$/).test(dir)) { - return cb(null); - } - if ((/[/\\]node_modules[/\\]*$/).test(dir)) return cb(null); + const make = pth => { + try { + options.fs.mkdirSync(pth, options.mode); + } catch (error) { + if (error.code === 'EPERM') { + throw error; + } - maybeRealpath(realpath, dir, opts, function (unwrapErr, pkgdir) { - if (unwrapErr) return loadpkg(path.dirname(dir), cb); - var pkgfile = path.join(pkgdir, 'package.json'); - isFile(pkgfile, function (err, ex) { - // on err, ex is false - if (!ex) return loadpkg(path.dirname(dir), cb); + if (error.code === 'ENOENT') { + if (path.dirname(pth) === pth) { + throw permissionError(pth); + } + + if (error.message.includes('null bytes')) { + throw error; + } + + make(path.dirname(pth)); + return make(pth); + } + + try { + if (!options.fs.statSync(pth).isDirectory()) { + throw new Error('The path is not a directory'); + } + } catch (_) { + throw error; + } + } + + return pth; + }; + + return make(path.resolve(input)); +}; + + +/***/ }), +/* 410 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +const processFn = (fn, options) => function (...args) { + const P = options.promiseModule; + + return new P((resolve, reject) => { + if (options.multiArgs) { + args.push((...result) => { + if (options.errorFirst) { + if (result[0]) { + reject(result); + } else { + result.shift(); + resolve(result); + } + } else { + resolve(result); + } + }); + } else if (options.errorFirst) { + args.push((error, result) => { + if (error) { + reject(error); + } else { + resolve(result); + } + }); + } else { + args.push(resolve); + } + + fn.apply(this, args); + }); +}; + +module.exports = (input, options) => { + options = Object.assign({ + exclude: [/.+(Sync|Stream)$/], + errorFirst: true, + promiseModule: Promise + }, options); - readPackage(readFile, pkgfile, function (err, pkgParam) { - if (err) cb(err); + const objType = typeof input; + if (!(input !== null && (objType === 'object' || objType === 'function'))) { + throw new TypeError(`Expected \`input\` to be a \`Function\` or \`Object\`, got \`${input === null ? 'null' : objType}\``); + } - var pkg = pkgParam; + const filter = key => { + const match = pattern => typeof pattern === 'string' ? key === pattern : pattern.test(key); + return options.include ? options.include.some(match) : !options.exclude.some(match); + }; - if (pkg && opts.packageFilter) { - pkg = opts.packageFilter(pkg, pkgfile); - } - cb(null, pkg, dir); - }); - }); - }); - } + let ret; + if (objType === 'function') { + ret = function (...args) { + return options.excludeMain ? input(...args) : processFn(input, options).apply(this, args); + }; + } else { + ret = Object.create(Object.getPrototypeOf(input)); + } - function loadAsDirectory(x, loadAsDirectoryPackage, callback) { - var cb = callback; - var fpkg = loadAsDirectoryPackage; - if (typeof fpkg === 'function') { - cb = fpkg; - fpkg = opts.package; - } + for (const key in input) { // eslint-disable-line guard-for-in + const property = input[key]; + ret[key] = typeof property === 'function' && filter(key) ? processFn(property, options) : property; + } - maybeRealpath(realpath, x, opts, function (unwrapErr, pkgdir) { - if (unwrapErr) return cb(unwrapErr); - var pkgfile = path.join(pkgdir, 'package.json'); - isFile(pkgfile, function (err, ex) { - if (err) return cb(err); - if (!ex) return loadAsFile(path.join(x, 'index'), fpkg, cb); + return ret; +}; - readPackage(readFile, pkgfile, function (err, pkgParam) { - if (err) return cb(err); - var pkg = pkgParam; +/***/ }), +/* 411 */ +/***/ (function(module, exports) { - if (pkg && opts.packageFilter) { - pkg = opts.packageFilter(pkg, pkgfile); - } +exports = module.exports = SemVer - if (pkg && pkg.main) { - if (typeof pkg.main !== 'string') { - var mainError = new TypeError('package “' + pkg.name + '” `main` must be a string'); - mainError.code = 'INVALID_PACKAGE_MAIN'; - return cb(mainError); - } - if (pkg.main === '.' || pkg.main === './') { - pkg.main = 'index'; - } - loadAsFile(path.resolve(x, pkg.main), pkg, function (err, m, pkg) { - if (err) return cb(err); - if (m) return cb(null, m, pkg); - if (!pkg) return loadAsFile(path.join(x, 'index'), pkg, cb); +var debug +/* istanbul ignore next */ +if (typeof process === 'object' && + process.env && + process.env.NODE_DEBUG && + /\bsemver\b/i.test(process.env.NODE_DEBUG)) { + debug = function () { + var args = Array.prototype.slice.call(arguments, 0) + args.unshift('SEMVER') + console.log.apply(console, args) + } +} else { + debug = function () {} +} - var dir = path.resolve(x, pkg.main); - loadAsDirectory(dir, pkg, function (err, n, pkg) { - if (err) return cb(err); - if (n) return cb(null, n, pkg); - loadAsFile(path.join(x, 'index'), pkg, cb); - }); - }); - return; - } +// Note: this is the semver.org version of the spec that it implements +// Not necessarily the package version of this code. +exports.SEMVER_SPEC_VERSION = '2.0.0' - loadAsFile(path.join(x, '/index'), pkg, cb); - }); - }); - }); - } +var MAX_LENGTH = 256 +var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || + /* istanbul ignore next */ 9007199254740991 - function processDirs(cb, dirs) { - if (dirs.length === 0) return cb(null, undefined); - var dir = dirs[0]; +// Max safe segment length for coercion. +var MAX_SAFE_COMPONENT_LENGTH = 16 - isDirectory(path.dirname(dir), isdir); +// The actual regexps go on exports.re +var re = exports.re = [] +var src = exports.src = [] +var R = 0 - function isdir(err, isdir) { - if (err) return cb(err); - if (!isdir) return processDirs(cb, dirs.slice(1)); - loadAsFile(dir, opts.package, onfile); - } +// The following Regular Expressions can be used for tokenizing, +// validating, and parsing SemVer version strings. - function onfile(err, m, pkg) { - if (err) return cb(err); - if (m) return cb(null, m, pkg); - loadAsDirectory(dir, opts.package, ondir); - } +// ## Numeric Identifier +// A single `0`, or a non-zero digit followed by zero or more digits. - function ondir(err, n, pkg) { - if (err) return cb(err); - if (n) return cb(null, n, pkg); - processDirs(cb, dirs.slice(1)); - } - } - function loadNodeModules(x, start, cb) { - var thunk = function () { return getPackageCandidates(x, start, opts); }; - processDirs( - cb, - packageIterator ? packageIterator(x, start, thunk, opts) : thunk() - ); - } -}; +var NUMERICIDENTIFIER = R++ +src[NUMERICIDENTIFIER] = '0|[1-9]\\d*' +var NUMERICIDENTIFIERLOOSE = R++ +src[NUMERICIDENTIFIERLOOSE] = '[0-9]+' +// ## Non-numeric Identifier +// Zero or more digits, followed by a letter or hyphen, and then zero or +// more letters, digits, or hyphens. -/***/ }), -/* 379 */ -/***/ (function(module, exports) { +var NONNUMERICIDENTIFIER = R++ +src[NONNUMERICIDENTIFIER] = '\\d*[a-zA-Z-][a-zA-Z0-9-]*' -module.exports = function () { - // see https://code.google.com/p/v8/wiki/JavaScriptStackTraceApi - var origPrepareStackTrace = Error.prepareStackTrace; - Error.prepareStackTrace = function (_, stack) { return stack; }; - var stack = (new Error()).stack; - Error.prepareStackTrace = origPrepareStackTrace; - return stack[2].getFileName(); -}; +// ## Main Version +// Three dot-separated numeric identifiers. +var MAINVERSION = R++ +src[MAINVERSION] = '(' + src[NUMERICIDENTIFIER] + ')\\.' + + '(' + src[NUMERICIDENTIFIER] + ')\\.' + + '(' + src[NUMERICIDENTIFIER] + ')' -/***/ }), -/* 380 */ -/***/ (function(module, exports, __webpack_require__) { +var MAINVERSIONLOOSE = R++ +src[MAINVERSIONLOOSE] = '(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' + + '(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' + + '(' + src[NUMERICIDENTIFIERLOOSE] + ')' -var path = __webpack_require__(4); -var parse = path.parse || __webpack_require__(381); +// ## Pre-release Version Identifier +// A numeric identifier, or a non-numeric identifier. -var getNodeModulesDirs = function getNodeModulesDirs(absoluteStart, modules) { - var prefix = '/'; - if ((/^([A-Za-z]:)/).test(absoluteStart)) { - prefix = ''; - } else if ((/^\\\\/).test(absoluteStart)) { - prefix = '\\\\'; - } +var PRERELEASEIDENTIFIER = R++ +src[PRERELEASEIDENTIFIER] = '(?:' + src[NUMERICIDENTIFIER] + + '|' + src[NONNUMERICIDENTIFIER] + ')' - var paths = [absoluteStart]; - var parsed = parse(absoluteStart); - while (parsed.dir !== paths[paths.length - 1]) { - paths.push(parsed.dir); - parsed = parse(parsed.dir); - } +var PRERELEASEIDENTIFIERLOOSE = R++ +src[PRERELEASEIDENTIFIERLOOSE] = '(?:' + src[NUMERICIDENTIFIERLOOSE] + + '|' + src[NONNUMERICIDENTIFIER] + ')' - return paths.reduce(function (dirs, aPath) { - return dirs.concat(modules.map(function (moduleDir) { - return path.resolve(prefix, aPath, moduleDir); - })); - }, []); -}; +// ## Pre-release Version +// Hyphen, followed by one or more dot-separated pre-release version +// identifiers. -module.exports = function nodeModulesPaths(start, opts, request) { - var modules = opts && opts.moduleDirectory - ? [].concat(opts.moduleDirectory) - : ['node_modules']; +var PRERELEASE = R++ +src[PRERELEASE] = '(?:-(' + src[PRERELEASEIDENTIFIER] + + '(?:\\.' + src[PRERELEASEIDENTIFIER] + ')*))' - if (opts && typeof opts.paths === 'function') { - return opts.paths( - request, - start, - function () { return getNodeModulesDirs(start, modules); }, - opts - ); - } +var PRERELEASELOOSE = R++ +src[PRERELEASELOOSE] = '(?:-?(' + src[PRERELEASEIDENTIFIERLOOSE] + + '(?:\\.' + src[PRERELEASEIDENTIFIERLOOSE] + ')*))' - var dirs = getNodeModulesDirs(start, modules); - return opts && opts.paths ? dirs.concat(opts.paths) : dirs; -}; +// ## Build Metadata Identifier +// Any combination of digits, letters, or hyphens. +var BUILDIDENTIFIER = R++ +src[BUILDIDENTIFIER] = '[0-9A-Za-z-]+' -/***/ }), -/* 381 */ -/***/ (function(module, exports, __webpack_require__) { +// ## Build Metadata +// Plus sign, followed by one or more period-separated build metadata +// identifiers. -"use strict"; +var BUILD = R++ +src[BUILD] = '(?:\\+(' + src[BUILDIDENTIFIER] + + '(?:\\.' + src[BUILDIDENTIFIER] + ')*))' +// ## Full Version String +// A main version, followed optionally by a pre-release version and +// build metadata. -var isWindows = process.platform === 'win32'; +// Note that the only major, minor, patch, and pre-release sections of +// the version string are capturing groups. The build metadata is not a +// capturing group, because it should not ever be used in version +// comparison. -// Regex to split a windows path into into [dir, root, basename, name, ext] -var splitWindowsRe = - /^(((?:[a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?[\\\/]?)(?:[^\\\/]*[\\\/])*)((\.{1,2}|[^\\\/]+?|)(\.[^.\/\\]*|))[\\\/]*$/; +var FULL = R++ +var FULLPLAIN = 'v?' + src[MAINVERSION] + + src[PRERELEASE] + '?' + + src[BUILD] + '?' -var win32 = {}; +src[FULL] = '^' + FULLPLAIN + '$' -function win32SplitPath(filename) { - return splitWindowsRe.exec(filename).slice(1); -} +// like full, but allows v1.2.3 and =1.2.3, which people do sometimes. +// also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty +// common in the npm registry. +var LOOSEPLAIN = '[v=\\s]*' + src[MAINVERSIONLOOSE] + + src[PRERELEASELOOSE] + '?' + + src[BUILD] + '?' -win32.parse = function(pathString) { - if (typeof pathString !== 'string') { - throw new TypeError( - "Parameter 'pathString' must be a string, not " + typeof pathString - ); - } - var allParts = win32SplitPath(pathString); - if (!allParts || allParts.length !== 5) { - throw new TypeError("Invalid path '" + pathString + "'"); - } - return { - root: allParts[1], - dir: allParts[0] === allParts[1] ? allParts[0] : allParts[0].slice(0, -1), - base: allParts[2], - ext: allParts[4], - name: allParts[3] - }; -}; +var LOOSE = R++ +src[LOOSE] = '^' + LOOSEPLAIN + '$' +var GTLT = R++ +src[GTLT] = '((?:<|>)?=?)' +// Something like "2.*" or "1.2.x". +// Note that "x.x" is a valid xRange identifer, meaning "any version" +// Only the first item is strictly required. +var XRANGEIDENTIFIERLOOSE = R++ +src[XRANGEIDENTIFIERLOOSE] = src[NUMERICIDENTIFIERLOOSE] + '|x|X|\\*' +var XRANGEIDENTIFIER = R++ +src[XRANGEIDENTIFIER] = src[NUMERICIDENTIFIER] + '|x|X|\\*' -// Split a filename into [dir, root, basename, name, ext], unix version -// 'root' is just a slash, or nothing. -var splitPathRe = - /^((\/?)(?:[^\/]*\/)*)((\.{1,2}|[^\/]+?|)(\.[^.\/]*|))[\/]*$/; -var posix = {}; +var XRANGEPLAIN = R++ +src[XRANGEPLAIN] = '[v=\\s]*(' + src[XRANGEIDENTIFIER] + ')' + + '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' + + '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' + + '(?:' + src[PRERELEASE] + ')?' + + src[BUILD] + '?' + + ')?)?' +var XRANGEPLAINLOOSE = R++ +src[XRANGEPLAINLOOSE] = '[v=\\s]*(' + src[XRANGEIDENTIFIERLOOSE] + ')' + + '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' + + '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' + + '(?:' + src[PRERELEASELOOSE] + ')?' + + src[BUILD] + '?' + + ')?)?' -function posixSplitPath(filename) { - return splitPathRe.exec(filename).slice(1); -} +var XRANGE = R++ +src[XRANGE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAIN] + '$' +var XRANGELOOSE = R++ +src[XRANGELOOSE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAINLOOSE] + '$' +// Coercion. +// Extract anything that could conceivably be a part of a valid semver +var COERCE = R++ +src[COERCE] = '(?:^|[^\\d])' + + '(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '})' + + '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + + '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + + '(?:$|[^\\d])' -posix.parse = function(pathString) { - if (typeof pathString !== 'string') { - throw new TypeError( - "Parameter 'pathString' must be a string, not " + typeof pathString - ); - } - var allParts = posixSplitPath(pathString); - if (!allParts || allParts.length !== 5) { - throw new TypeError("Invalid path '" + pathString + "'"); - } - - return { - root: allParts[1], - dir: allParts[0].slice(0, -1), - base: allParts[2], - ext: allParts[4], - name: allParts[3], - }; -}; +// Tilde ranges. +// Meaning is "reasonably at or greater than" +var LONETILDE = R++ +src[LONETILDE] = '(?:~>?)' +var TILDETRIM = R++ +src[TILDETRIM] = '(\\s*)' + src[LONETILDE] + '\\s+' +re[TILDETRIM] = new RegExp(src[TILDETRIM], 'g') +var tildeTrimReplace = '$1~' -if (isWindows) - module.exports = win32.parse; -else /* posix */ - module.exports = posix.parse; +var TILDE = R++ +src[TILDE] = '^' + src[LONETILDE] + src[XRANGEPLAIN] + '$' +var TILDELOOSE = R++ +src[TILDELOOSE] = '^' + src[LONETILDE] + src[XRANGEPLAINLOOSE] + '$' -module.exports.posix = posix.parse; -module.exports.win32 = win32.parse; +// Caret ranges. +// Meaning is "at least and backwards compatible with" +var LONECARET = R++ +src[LONECARET] = '(?:\\^)' +var CARETTRIM = R++ +src[CARETTRIM] = '(\\s*)' + src[LONECARET] + '\\s+' +re[CARETTRIM] = new RegExp(src[CARETTRIM], 'g') +var caretTrimReplace = '$1^' -/***/ }), -/* 382 */ -/***/ (function(module, exports) { +var CARET = R++ +src[CARET] = '^' + src[LONECARET] + src[XRANGEPLAIN] + '$' +var CARETLOOSE = R++ +src[CARETLOOSE] = '^' + src[LONECARET] + src[XRANGEPLAINLOOSE] + '$' -module.exports = function (x, opts) { - /** - * This file is purposefully a passthrough. It's expected that third-party - * environments will override it at runtime in order to inject special logic - * into `resolve` (by manipulating the options). One such example is the PnP - * code path in Yarn. - */ +// A simple gt/lt/eq thing, or just "" to indicate "any version" +var COMPARATORLOOSE = R++ +src[COMPARATORLOOSE] = '^' + src[GTLT] + '\\s*(' + LOOSEPLAIN + ')$|^$' +var COMPARATOR = R++ +src[COMPARATOR] = '^' + src[GTLT] + '\\s*(' + FULLPLAIN + ')$|^$' - return opts || {}; -}; +// An expression to strip any whitespace between the gtlt and the thing +// it modifies, so that `> 1.2.3` ==> `>1.2.3` +var COMPARATORTRIM = R++ +src[COMPARATORTRIM] = '(\\s*)' + src[GTLT] + + '\\s*(' + LOOSEPLAIN + '|' + src[XRANGEPLAIN] + ')' +// this one has to use the /g flag +re[COMPARATORTRIM] = new RegExp(src[COMPARATORTRIM], 'g') +var comparatorTrimReplace = '$1$2$3' -/***/ }), -/* 383 */ -/***/ (function(module, exports, __webpack_require__) { +// Something like `1.2.3 - 1.2.4` +// Note that these all use the loose form, because they'll be +// checked against either the strict or loose comparator form +// later. +var HYPHENRANGE = R++ +src[HYPHENRANGE] = '^\\s*(' + src[XRANGEPLAIN] + ')' + + '\\s+-\\s+' + + '(' + src[XRANGEPLAIN] + ')' + + '\\s*$' -"use strict"; +var HYPHENRANGELOOSE = R++ +src[HYPHENRANGELOOSE] = '^\\s*(' + src[XRANGEPLAINLOOSE] + ')' + + '\\s+-\\s+' + + '(' + src[XRANGEPLAINLOOSE] + ')' + + '\\s*$' +// Star ranges basically just allow anything at all. +var STAR = R++ +src[STAR] = '(<|>)?=?\\s*\\*' -var has = __webpack_require__(384); +// Compile to actual regexp objects. +// All are flag-free, unless they were created above with a flag. +for (var i = 0; i < R; i++) { + debug(i, src[i]) + if (!re[i]) { + re[i] = new RegExp(src[i]) + } +} -function specifierIncluded(current, specifier) { - var nodeParts = current.split('.'); - var parts = specifier.split(' '); - var op = parts.length > 1 ? parts[0] : '='; - var versionParts = (parts.length > 1 ? parts[1] : parts[0]).split('.'); +exports.parse = parse +function parse (version, options) { + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false + } + } - for (var i = 0; i < 3; ++i) { - var cur = parseInt(nodeParts[i] || 0, 10); - var ver = parseInt(versionParts[i] || 0, 10); - if (cur === ver) { - continue; // eslint-disable-line no-restricted-syntax, no-continue - } - if (op === '<') { - return cur < ver; - } - if (op === '>=') { - return cur >= ver; - } - return false; - } - return op === '>='; -} + if (version instanceof SemVer) { + return version + } -function matchesRange(current, range) { - var specifiers = range.split(/ ?&& ?/); - if (specifiers.length === 0) { - return false; - } - for (var i = 0; i < specifiers.length; ++i) { - if (!specifierIncluded(current, specifiers[i])) { - return false; - } - } - return true; -} + if (typeof version !== 'string') { + return null + } -function versionIncluded(nodeVersion, specifierValue) { - if (typeof specifierValue === 'boolean') { - return specifierValue; - } + if (version.length > MAX_LENGTH) { + return null + } - var current = typeof nodeVersion === 'undefined' - ? process.versions && process.versions.node - : nodeVersion; + var r = options.loose ? re[LOOSE] : re[FULL] + if (!r.test(version)) { + return null + } - if (typeof current !== 'string') { - throw new TypeError(typeof nodeVersion === 'undefined' ? 'Unable to determine current node version' : 'If provided, a valid node version is required'); - } + try { + return new SemVer(version, options) + } catch (er) { + return null + } +} - if (specifierValue && typeof specifierValue === 'object') { - for (var i = 0; i < specifierValue.length; ++i) { - if (matchesRange(current, specifierValue[i])) { - return true; - } - } - return false; - } - return matchesRange(current, specifierValue); +exports.valid = valid +function valid (version, options) { + var v = parse(version, options) + return v ? v.version : null } -var data = __webpack_require__(387); +exports.clean = clean +function clean (version, options) { + var s = parse(version.trim().replace(/^[=v]+/, ''), options) + return s ? s.version : null +} -module.exports = function isCore(x, nodeVersion) { - return has(data, x) && versionIncluded(nodeVersion, data[x]); -}; +exports.SemVer = SemVer +function SemVer (version, options) { + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false + } + } + if (version instanceof SemVer) { + if (version.loose === options.loose) { + return version + } else { + version = version.version + } + } else if (typeof version !== 'string') { + throw new TypeError('Invalid Version: ' + version) + } -/***/ }), -/* 384 */ -/***/ (function(module, exports, __webpack_require__) { + if (version.length > MAX_LENGTH) { + throw new TypeError('version is longer than ' + MAX_LENGTH + ' characters') + } -"use strict"; + if (!(this instanceof SemVer)) { + return new SemVer(version, options) + } + + debug('SemVer', version, options) + this.options = options + this.loose = !!options.loose + var m = version.trim().match(options.loose ? re[LOOSE] : re[FULL]) -var bind = __webpack_require__(385); + if (!m) { + throw new TypeError('Invalid Version: ' + version) + } -module.exports = bind.call(Function.call, Object.prototype.hasOwnProperty); + this.raw = version + // these are actually numbers + this.major = +m[1] + this.minor = +m[2] + this.patch = +m[3] -/***/ }), -/* 385 */ -/***/ (function(module, exports, __webpack_require__) { + if (this.major > MAX_SAFE_INTEGER || this.major < 0) { + throw new TypeError('Invalid major version') + } -"use strict"; + if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) { + throw new TypeError('Invalid minor version') + } + + if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) { + throw new TypeError('Invalid patch version') + } + // numberify any prerelease numeric ids + if (!m[4]) { + this.prerelease = [] + } else { + this.prerelease = m[4].split('.').map(function (id) { + if (/^[0-9]+$/.test(id)) { + var num = +id + if (num >= 0 && num < MAX_SAFE_INTEGER) { + return num + } + } + return id + }) + } -var implementation = __webpack_require__(386); + this.build = m[5] ? m[5].split('.') : [] + this.format() +} -module.exports = Function.prototype.bind || implementation; +SemVer.prototype.format = function () { + this.version = this.major + '.' + this.minor + '.' + this.patch + if (this.prerelease.length) { + this.version += '-' + this.prerelease.join('.') + } + return this.version +} +SemVer.prototype.toString = function () { + return this.version +} -/***/ }), -/* 386 */ -/***/ (function(module, exports, __webpack_require__) { +SemVer.prototype.compare = function (other) { + debug('SemVer.compare', this.version, this.options, other) + if (!(other instanceof SemVer)) { + other = new SemVer(other, this.options) + } -"use strict"; + return this.compareMain(other) || this.comparePre(other) +} +SemVer.prototype.compareMain = function (other) { + if (!(other instanceof SemVer)) { + other = new SemVer(other, this.options) + } -/* eslint no-invalid-this: 1 */ + return compareIdentifiers(this.major, other.major) || + compareIdentifiers(this.minor, other.minor) || + compareIdentifiers(this.patch, other.patch) +} -var ERROR_MESSAGE = 'Function.prototype.bind called on incompatible '; -var slice = Array.prototype.slice; -var toStr = Object.prototype.toString; -var funcType = '[object Function]'; +SemVer.prototype.comparePre = function (other) { + if (!(other instanceof SemVer)) { + other = new SemVer(other, this.options) + } -module.exports = function bind(that) { - var target = this; - if (typeof target !== 'function' || toStr.call(target) !== funcType) { - throw new TypeError(ERROR_MESSAGE + target); + // NOT having a prerelease is > having one + if (this.prerelease.length && !other.prerelease.length) { + return -1 + } else if (!this.prerelease.length && other.prerelease.length) { + return 1 + } else if (!this.prerelease.length && !other.prerelease.length) { + return 0 + } + + var i = 0 + do { + var a = this.prerelease[i] + var b = other.prerelease[i] + debug('prerelease compare', i, a, b) + if (a === undefined && b === undefined) { + return 0 + } else if (b === undefined) { + return 1 + } else if (a === undefined) { + return -1 + } else if (a === b) { + continue + } else { + return compareIdentifiers(a, b) } - var args = slice.call(arguments, 1); + } while (++i) +} - var bound; - var binder = function () { - if (this instanceof bound) { - var result = target.apply( - this, - args.concat(slice.call(arguments)) - ); - if (Object(result) === result) { - return result; - } - return this; +// preminor will bump the version up to the next minor release, and immediately +// down to pre-release. premajor and prepatch work the same way. +SemVer.prototype.inc = function (release, identifier) { + switch (release) { + case 'premajor': + this.prerelease.length = 0 + this.patch = 0 + this.minor = 0 + this.major++ + this.inc('pre', identifier) + break + case 'preminor': + this.prerelease.length = 0 + this.patch = 0 + this.minor++ + this.inc('pre', identifier) + break + case 'prepatch': + // If this is already a prerelease, it will bump to the next version + // drop any prereleases that might already exist, since they are not + // relevant at this point. + this.prerelease.length = 0 + this.inc('patch', identifier) + this.inc('pre', identifier) + break + // If the input is a non-prerelease version, this acts the same as + // prepatch. + case 'prerelease': + if (this.prerelease.length === 0) { + this.inc('patch', identifier) + } + this.inc('pre', identifier) + break + + case 'major': + // If this is a pre-major version, bump up to the same major version. + // Otherwise increment major. + // 1.0.0-5 bumps to 1.0.0 + // 1.1.0 bumps to 2.0.0 + if (this.minor !== 0 || + this.patch !== 0 || + this.prerelease.length === 0) { + this.major++ + } + this.minor = 0 + this.patch = 0 + this.prerelease = [] + break + case 'minor': + // If this is a pre-minor version, bump up to the same minor version. + // Otherwise increment minor. + // 1.2.0-5 bumps to 1.2.0 + // 1.2.1 bumps to 1.3.0 + if (this.patch !== 0 || this.prerelease.length === 0) { + this.minor++ + } + this.patch = 0 + this.prerelease = [] + break + case 'patch': + // If this is not a pre-release version, it will increment the patch. + // If it is a pre-release it will bump up to the same patch version. + // 1.2.0-5 patches to 1.2.0 + // 1.2.0 patches to 1.2.1 + if (this.prerelease.length === 0) { + this.patch++ + } + this.prerelease = [] + break + // This probably shouldn't be used publicly. + // 1.0.0 "pre" would become 1.0.0-0 which is the wrong direction. + case 'pre': + if (this.prerelease.length === 0) { + this.prerelease = [0] + } else { + var i = this.prerelease.length + while (--i >= 0) { + if (typeof this.prerelease[i] === 'number') { + this.prerelease[i]++ + i = -2 + } + } + if (i === -1) { + // didn't increment anything + this.prerelease.push(0) + } + } + if (identifier) { + // 1.2.0-beta.1 bumps to 1.2.0-beta.2, + // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0 + if (this.prerelease[0] === identifier) { + if (isNaN(this.prerelease[1])) { + this.prerelease = [identifier, 0] + } } else { - return target.apply( - that, - args.concat(slice.call(arguments)) - ); + this.prerelease = [identifier, 0] } - }; - - var boundLength = Math.max(0, target.length - args.length); - var boundArgs = []; - for (var i = 0; i < boundLength; i++) { - boundArgs.push('$' + i); - } + } + break - bound = Function('binder', 'return function (' + boundArgs.join(',') + '){ return binder.apply(this,arguments); }')(binder); + default: + throw new Error('invalid increment argument: ' + release) + } + this.format() + this.raw = this.version + return this +} - if (target.prototype) { - var Empty = function Empty() {}; - Empty.prototype = target.prototype; - bound.prototype = new Empty(); - Empty.prototype = null; - } +exports.inc = inc +function inc (version, release, loose, identifier) { + if (typeof (loose) === 'string') { + identifier = loose + loose = undefined + } - return bound; -}; + try { + return new SemVer(version, loose).inc(release, identifier).version + } catch (er) { + return null + } +} +exports.diff = diff +function diff (version1, version2) { + if (eq(version1, version2)) { + return null + } else { + var v1 = parse(version1) + var v2 = parse(version2) + var prefix = '' + if (v1.prerelease.length || v2.prerelease.length) { + prefix = 'pre' + var defaultResult = 'prerelease' + } + for (var key in v1) { + if (key === 'major' || key === 'minor' || key === 'patch') { + if (v1[key] !== v2[key]) { + return prefix + key + } + } + } + return defaultResult // may be undefined + } +} -/***/ }), -/* 387 */ -/***/ (function(module) { +exports.compareIdentifiers = compareIdentifiers -module.exports = JSON.parse("{\"assert\":true,\"node:assert\":[\">= 14.18 && < 15\",\">= 16\"],\"assert/strict\":\">= 15\",\"node:assert/strict\":\">= 16\",\"async_hooks\":\">= 8\",\"node:async_hooks\":[\">= 14.18 && < 15\",\">= 16\"],\"buffer_ieee754\":\"< 0.9.7\",\"buffer\":true,\"node:buffer\":[\">= 14.18 && < 15\",\">= 16\"],\"child_process\":true,\"node:child_process\":[\">= 14.18 && < 15\",\">= 16\"],\"cluster\":true,\"node:cluster\":[\">= 14.18 && < 15\",\">= 16\"],\"console\":true,\"node:console\":[\">= 14.18 && < 15\",\">= 16\"],\"constants\":true,\"node:constants\":[\">= 14.18 && < 15\",\">= 16\"],\"crypto\":true,\"node:crypto\":[\">= 14.18 && < 15\",\">= 16\"],\"_debug_agent\":\">= 1 && < 8\",\"_debugger\":\"< 8\",\"dgram\":true,\"node:dgram\":[\">= 14.18 && < 15\",\">= 16\"],\"diagnostics_channel\":[\">= 14.17 && < 15\",\">= 15.1\"],\"node:diagnostics_channel\":[\">= 14.18 && < 15\",\">= 16\"],\"dns\":true,\"node:dns\":[\">= 14.18 && < 15\",\">= 16\"],\"dns/promises\":\">= 15\",\"node:dns/promises\":\">= 16\",\"domain\":\">= 0.7.12\",\"node:domain\":[\">= 14.18 && < 15\",\">= 16\"],\"events\":true,\"node:events\":[\">= 14.18 && < 15\",\">= 16\"],\"freelist\":\"< 6\",\"fs\":true,\"node:fs\":[\">= 14.18 && < 15\",\">= 16\"],\"fs/promises\":[\">= 10 && < 10.1\",\">= 14\"],\"node:fs/promises\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_agent\":\">= 0.11.1\",\"node:_http_agent\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_client\":\">= 0.11.1\",\"node:_http_client\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_common\":\">= 0.11.1\",\"node:_http_common\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_incoming\":\">= 0.11.1\",\"node:_http_incoming\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_outgoing\":\">= 0.11.1\",\"node:_http_outgoing\":[\">= 14.18 && < 15\",\">= 16\"],\"_http_server\":\">= 0.11.1\",\"node:_http_server\":[\">= 14.18 && < 15\",\">= 16\"],\"http\":true,\"node:http\":[\">= 14.18 && < 15\",\">= 16\"],\"http2\":\">= 8.8\",\"node:http2\":[\">= 14.18 && < 15\",\">= 16\"],\"https\":true,\"node:https\":[\">= 14.18 && < 15\",\">= 16\"],\"inspector\":\">= 8\",\"node:inspector\":[\">= 14.18 && < 15\",\">= 16\"],\"_linklist\":\"< 8\",\"module\":true,\"node:module\":[\">= 14.18 && < 15\",\">= 16\"],\"net\":true,\"node:net\":[\">= 14.18 && < 15\",\">= 16\"],\"node-inspect/lib/_inspect\":\">= 7.6 && < 12\",\"node-inspect/lib/internal/inspect_client\":\">= 7.6 && < 12\",\"node-inspect/lib/internal/inspect_repl\":\">= 7.6 && < 12\",\"os\":true,\"node:os\":[\">= 14.18 && < 15\",\">= 16\"],\"path\":true,\"node:path\":[\">= 14.18 && < 15\",\">= 16\"],\"path/posix\":\">= 15.3\",\"node:path/posix\":\">= 16\",\"path/win32\":\">= 15.3\",\"node:path/win32\":\">= 16\",\"perf_hooks\":\">= 8.5\",\"node:perf_hooks\":[\">= 14.18 && < 15\",\">= 16\"],\"process\":\">= 1\",\"node:process\":[\">= 14.18 && < 15\",\">= 16\"],\"punycode\":true,\"node:punycode\":[\">= 14.18 && < 15\",\">= 16\"],\"querystring\":true,\"node:querystring\":[\">= 14.18 && < 15\",\">= 16\"],\"readline\":true,\"node:readline\":[\">= 14.18 && < 15\",\">= 16\"],\"repl\":true,\"node:repl\":[\">= 14.18 && < 15\",\">= 16\"],\"smalloc\":\">= 0.11.5 && < 3\",\"_stream_duplex\":\">= 0.9.4\",\"node:_stream_duplex\":[\">= 14.18 && < 15\",\">= 16\"],\"_stream_transform\":\">= 0.9.4\",\"node:_stream_transform\":[\">= 14.18 && < 15\",\">= 16\"],\"_stream_wrap\":\">= 1.4.1\",\"node:_stream_wrap\":[\">= 14.18 && < 15\",\">= 16\"],\"_stream_passthrough\":\">= 0.9.4\",\"node:_stream_passthrough\":[\">= 14.18 && < 15\",\">= 16\"],\"_stream_readable\":\">= 0.9.4\",\"node:_stream_readable\":[\">= 14.18 && < 15\",\">= 16\"],\"_stream_writable\":\">= 0.9.4\",\"node:_stream_writable\":[\">= 14.18 && < 15\",\">= 16\"],\"stream\":true,\"node:stream\":[\">= 14.18 && < 15\",\">= 16\"],\"stream/consumers\":\">= 16.7\",\"node:stream/consumers\":\">= 16.7\",\"stream/promises\":\">= 15\",\"node:stream/promises\":\">= 16\",\"stream/web\":\">= 16.5\",\"node:stream/web\":\">= 16.5\",\"string_decoder\":true,\"node:string_decoder\":[\">= 14.18 && < 15\",\">= 16\"],\"sys\":[\">= 0.6 && < 0.7\",\">= 0.8\"],\"node:sys\":[\">= 14.18 && < 15\",\">= 16\"],\"timers\":true,\"node:timers\":[\">= 14.18 && < 15\",\">= 16\"],\"timers/promises\":\">= 15\",\"node:timers/promises\":\">= 16\",\"_tls_common\":\">= 0.11.13\",\"node:_tls_common\":[\">= 14.18 && < 15\",\">= 16\"],\"_tls_legacy\":\">= 0.11.3 && < 10\",\"_tls_wrap\":\">= 0.11.3\",\"node:_tls_wrap\":[\">= 14.18 && < 15\",\">= 16\"],\"tls\":true,\"node:tls\":[\">= 14.18 && < 15\",\">= 16\"],\"trace_events\":\">= 10\",\"node:trace_events\":[\">= 14.18 && < 15\",\">= 16\"],\"tty\":true,\"node:tty\":[\">= 14.18 && < 15\",\">= 16\"],\"url\":true,\"node:url\":[\">= 14.18 && < 15\",\">= 16\"],\"util\":true,\"node:util\":[\">= 14.18 && < 15\",\">= 16\"],\"util/types\":\">= 15.3\",\"node:util/types\":\">= 16\",\"v8/tools/arguments\":\">= 10 && < 12\",\"v8/tools/codemap\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8/tools/consarray\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8/tools/csvparser\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8/tools/logreader\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8/tools/profile_view\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8/tools/splaytree\":[\">= 4.4 && < 5\",\">= 5.2 && < 12\"],\"v8\":\">= 1\",\"node:v8\":[\">= 14.18 && < 15\",\">= 16\"],\"vm\":true,\"node:vm\":[\">= 14.18 && < 15\",\">= 16\"],\"wasi\":\">= 13.4 && < 13.5\",\"worker_threads\":\">= 11.7\",\"node:worker_threads\":[\">= 14.18 && < 15\",\">= 16\"],\"zlib\":true,\"node:zlib\":[\">= 14.18 && < 15\",\">= 16\"]}"); +var numeric = /^[0-9]+$/ +function compareIdentifiers (a, b) { + var anum = numeric.test(a) + var bnum = numeric.test(b) -/***/ }), -/* 388 */ -/***/ (function(module, exports, __webpack_require__) { + if (anum && bnum) { + a = +a + b = +b + } -var current = (process.versions && process.versions.node && process.versions.node.split('.')) || []; + return a === b ? 0 + : (anum && !bnum) ? -1 + : (bnum && !anum) ? 1 + : a < b ? -1 + : 1 +} -function specifierIncluded(specifier) { - var parts = specifier.split(' '); - var op = parts.length > 1 ? parts[0] : '='; - var versionParts = (parts.length > 1 ? parts[1] : parts[0]).split('.'); +exports.rcompareIdentifiers = rcompareIdentifiers +function rcompareIdentifiers (a, b) { + return compareIdentifiers(b, a) +} - for (var i = 0; i < 3; ++i) { - var cur = parseInt(current[i] || 0, 10); - var ver = parseInt(versionParts[i] || 0, 10); - if (cur === ver) { - continue; // eslint-disable-line no-restricted-syntax, no-continue - } - if (op === '<') { - return cur < ver; - } else if (op === '>=') { - return cur >= ver; - } else { - return false; - } - } - return op === '>='; +exports.major = major +function major (a, loose) { + return new SemVer(a, loose).major } -function matchesRange(range) { - var specifiers = range.split(/ ?&& ?/); - if (specifiers.length === 0) { return false; } - for (var i = 0; i < specifiers.length; ++i) { - if (!specifierIncluded(specifiers[i])) { return false; } - } - return true; +exports.minor = minor +function minor (a, loose) { + return new SemVer(a, loose).minor } -function versionIncluded(specifierValue) { - if (typeof specifierValue === 'boolean') { return specifierValue; } - if (specifierValue && typeof specifierValue === 'object') { - for (var i = 0; i < specifierValue.length; ++i) { - if (matchesRange(specifierValue[i])) { return true; } - } - return false; - } - return matchesRange(specifierValue); +exports.patch = patch +function patch (a, loose) { + return new SemVer(a, loose).patch } -var data = __webpack_require__(389); +exports.compare = compare +function compare (a, b, loose) { + return new SemVer(a, loose).compare(new SemVer(b, loose)) +} -var core = {}; -for (var mod in data) { // eslint-disable-line no-restricted-syntax - if (Object.prototype.hasOwnProperty.call(data, mod)) { - core[mod] = versionIncluded(data[mod]); - } +exports.compareLoose = compareLoose +function compareLoose (a, b) { + return compare(a, b, true) } -module.exports = core; +exports.rcompare = rcompare +function rcompare (a, b, loose) { + return compare(b, a, loose) +} -/***/ }), -/* 389 */ -/***/ (function(module) { +exports.sort = sort +function sort (list, loose) { + return list.sort(function (a, b) { + return exports.compare(a, b, loose) + }) +} -module.exports = JSON.parse("{\"assert\":true,\"assert/strict\":\">= 15\",\"async_hooks\":\">= 8\",\"buffer_ieee754\":\"< 0.9.7\",\"buffer\":true,\"child_process\":true,\"cluster\":true,\"console\":true,\"constants\":true,\"crypto\":true,\"_debug_agent\":\">= 1 && < 8\",\"_debugger\":\"< 8\",\"dgram\":true,\"diagnostics_channel\":\">= 15.1\",\"dns\":true,\"dns/promises\":\">= 15\",\"domain\":\">= 0.7.12\",\"events\":true,\"freelist\":\"< 6\",\"fs\":true,\"fs/promises\":[\">= 10 && < 10.1\",\">= 14\"],\"_http_agent\":\">= 0.11.1\",\"_http_client\":\">= 0.11.1\",\"_http_common\":\">= 0.11.1\",\"_http_incoming\":\">= 0.11.1\",\"_http_outgoing\":\">= 0.11.1\",\"_http_server\":\">= 0.11.1\",\"http\":true,\"http2\":\">= 8.8\",\"https\":true,\"inspector\":\">= 8.0.0\",\"_linklist\":\"< 8\",\"module\":true,\"net\":true,\"node-inspect/lib/_inspect\":\">= 7.6.0 && < 12\",\"node-inspect/lib/internal/inspect_client\":\">= 7.6.0 && < 12\",\"node-inspect/lib/internal/inspect_repl\":\">= 7.6.0 && < 12\",\"os\":true,\"path\":true,\"path/posix\":\">= 15.3\",\"path/win32\":\">= 15.3\",\"perf_hooks\":\">= 8.5\",\"process\":\">= 1\",\"punycode\":true,\"querystring\":true,\"readline\":true,\"repl\":true,\"smalloc\":\">= 0.11.5 && < 3\",\"_stream_duplex\":\">= 0.9.4\",\"_stream_transform\":\">= 0.9.4\",\"_stream_wrap\":\">= 1.4.1\",\"_stream_passthrough\":\">= 0.9.4\",\"_stream_readable\":\">= 0.9.4\",\"_stream_writable\":\">= 0.9.4\",\"stream\":true,\"stream/promises\":\">= 15\",\"string_decoder\":true,\"sys\":[\">= 0.6 && < 0.7\",\">= 0.8\"],\"timers\":true,\"timers/promises\":\">= 15\",\"_tls_common\":\">= 0.11.13\",\"_tls_legacy\":\">= 0.11.3 && < 10\",\"_tls_wrap\":\">= 0.11.3\",\"tls\":true,\"trace_events\":\">= 10\",\"tty\":true,\"url\":true,\"util\":true,\"util/types\":\">= 15.3\",\"v8/tools/arguments\":\">= 10 && < 12\",\"v8/tools/codemap\":[\">= 4.4.0 && < 5\",\">= 5.2.0 && < 12\"],\"v8/tools/consarray\":[\">= 4.4.0 && < 5\",\">= 5.2.0 && < 12\"],\"v8/tools/csvparser\":[\">= 4.4.0 && < 5\",\">= 5.2.0 && < 12\"],\"v8/tools/logreader\":[\">= 4.4.0 && < 5\",\">= 5.2.0 && < 12\"],\"v8/tools/profile_view\":[\">= 4.4.0 && < 5\",\">= 5.2.0 && < 12\"],\"v8/tools/splaytree\":[\">= 4.4.0 && < 5\",\">= 5.2.0 && < 12\"],\"v8\":\">= 1\",\"vm\":true,\"wasi\":\">= 13.4 && < 13.5\",\"worker_threads\":\">= 11.7\",\"zlib\":true}"); +exports.rsort = rsort +function rsort (list, loose) { + return list.sort(function (a, b) { + return exports.rcompare(a, b, loose) + }) +} -/***/ }), -/* 390 */ -/***/ (function(module, exports, __webpack_require__) { +exports.gt = gt +function gt (a, b, loose) { + return compare(a, b, loose) > 0 +} -var isCoreModule = __webpack_require__(383); +exports.lt = lt +function lt (a, b, loose) { + return compare(a, b, loose) < 0 +} -module.exports = function isCore(x) { - return isCoreModule(x); -}; +exports.eq = eq +function eq (a, b, loose) { + return compare(a, b, loose) === 0 +} +exports.neq = neq +function neq (a, b, loose) { + return compare(a, b, loose) !== 0 +} -/***/ }), -/* 391 */ -/***/ (function(module, exports, __webpack_require__) { +exports.gte = gte +function gte (a, b, loose) { + return compare(a, b, loose) >= 0 +} -var isCore = __webpack_require__(383); -var fs = __webpack_require__(132); -var path = __webpack_require__(4); -var caller = __webpack_require__(379); -var nodeModulesPaths = __webpack_require__(380); -var normalizeOptions = __webpack_require__(382); +exports.lte = lte +function lte (a, b, loose) { + return compare(a, b, loose) <= 0 +} -var realpathFS = fs.realpathSync && typeof fs.realpathSync.native === 'function' ? fs.realpathSync.native : fs.realpathSync; +exports.cmp = cmp +function cmp (a, op, b, loose) { + switch (op) { + case '===': + if (typeof a === 'object') + a = a.version + if (typeof b === 'object') + b = b.version + return a === b -var defaultIsFile = function isFile(file) { - try { - var stat = fs.statSync(file); - } catch (e) { - if (e && (e.code === 'ENOENT' || e.code === 'ENOTDIR')) return false; - throw e; - } - return stat.isFile() || stat.isFIFO(); -}; + case '!==': + if (typeof a === 'object') + a = a.version + if (typeof b === 'object') + b = b.version + return a !== b -var defaultIsDir = function isDirectory(dir) { - try { - var stat = fs.statSync(dir); - } catch (e) { - if (e && (e.code === 'ENOENT' || e.code === 'ENOTDIR')) return false; - throw e; - } - return stat.isDirectory(); -}; + case '': + case '=': + case '==': + return eq(a, b, loose) -var defaultRealpathSync = function realpathSync(x) { - try { - return realpathFS(x); - } catch (realpathErr) { - if (realpathErr.code !== 'ENOENT') { - throw realpathErr; - } - } - return x; -}; + case '!=': + return neq(a, b, loose) -var maybeRealpathSync = function maybeRealpathSync(realpathSync, x, opts) { - if (opts && opts.preserveSymlinks === false) { - return realpathSync(x); - } - return x; -}; + case '>': + return gt(a, b, loose) -var defaultReadPackageSync = function defaultReadPackageSync(readFileSync, pkgfile) { - var body = readFileSync(pkgfile); - try { - var pkg = JSON.parse(body); - return pkg; - } catch (jsonErr) {} -}; + case '>=': + return gte(a, b, loose) -var getPackageCandidates = function getPackageCandidates(x, start, opts) { - var dirs = nodeModulesPaths(start, opts, x); - for (var i = 0; i < dirs.length; i++) { - dirs[i] = path.join(dirs[i], x); - } - return dirs; -}; + case '<': + return lt(a, b, loose) -module.exports = function resolveSync(x, options) { - if (typeof x !== 'string') { - throw new TypeError('Path must be a string.'); + case '<=': + return lte(a, b, loose) + + default: + throw new TypeError('Invalid operator: ' + op) + } +} + +exports.Comparator = Comparator +function Comparator (comp, options) { + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false } - var opts = normalizeOptions(x, options); + } - var isFile = opts.isFile || defaultIsFile; - var readFileSync = opts.readFileSync || fs.readFileSync; - var isDirectory = opts.isDirectory || defaultIsDir; - var realpathSync = opts.realpathSync || defaultRealpathSync; - var readPackageSync = opts.readPackageSync || defaultReadPackageSync; - if (opts.readFileSync && opts.readPackageSync) { - throw new TypeError('`readFileSync` and `readPackageSync` are mutually exclusive.'); + if (comp instanceof Comparator) { + if (comp.loose === !!options.loose) { + return comp + } else { + comp = comp.value } - var packageIterator = opts.packageIterator; + } - var extensions = opts.extensions || ['.js']; - var includeCoreModules = opts.includeCoreModules !== false; - var basedir = opts.basedir || path.dirname(caller()); - var parent = opts.filename || basedir; + if (!(this instanceof Comparator)) { + return new Comparator(comp, options) + } - opts.paths = opts.paths || []; + debug('comparator', comp, options) + this.options = options + this.loose = !!options.loose + this.parse(comp) - // ensure that `basedir` is an absolute path at this point, resolving against the process' current working directory - var absoluteStart = maybeRealpathSync(realpathSync, path.resolve(basedir), opts); + if (this.semver === ANY) { + this.value = '' + } else { + this.value = this.operator + this.semver.version + } - if ((/^(?:\.\.?(?:\/|$)|\/|([A-Za-z]:)?[/\\])/).test(x)) { - var res = path.resolve(absoluteStart, x); - if (x === '.' || x === '..' || x.slice(-1) === '/') res += '/'; - var m = loadAsFileSync(res) || loadAsDirectorySync(res); - if (m) return maybeRealpathSync(realpathSync, m, opts); - } else if (includeCoreModules && isCore(x)) { - return x; - } else { - var n = loadNodeModulesSync(x, absoluteStart); - if (n) return maybeRealpathSync(realpathSync, n, opts); - } + debug('comp', this) +} - var err = new Error("Cannot find module '" + x + "' from '" + parent + "'"); - err.code = 'MODULE_NOT_FOUND'; - throw err; +var ANY = {} +Comparator.prototype.parse = function (comp) { + var r = this.options.loose ? re[COMPARATORLOOSE] : re[COMPARATOR] + var m = comp.match(r) - function loadAsFileSync(x) { - var pkg = loadpkg(path.dirname(x)); + if (!m) { + throw new TypeError('Invalid comparator: ' + comp) + } - if (pkg && pkg.dir && pkg.pkg && opts.pathFilter) { - var rfile = path.relative(pkg.dir, x); - var r = opts.pathFilter(pkg.pkg, x, rfile); - if (r) { - x = path.resolve(pkg.dir, r); // eslint-disable-line no-param-reassign - } - } + this.operator = m[1] + if (this.operator === '=') { + this.operator = '' + } - if (isFile(x)) { - return x; - } + // if it literally is just '>' or '' then allow anything. + if (!m[2]) { + this.semver = ANY + } else { + this.semver = new SemVer(m[2], this.options.loose) + } +} - for (var i = 0; i < extensions.length; i++) { - var file = x + extensions[i]; - if (isFile(file)) { - return file; - } - } - } +Comparator.prototype.toString = function () { + return this.value +} - function loadpkg(dir) { - if (dir === '' || dir === '/') return; - if (process.platform === 'win32' && (/^\w:[/\\]*$/).test(dir)) { - return; - } - if ((/[/\\]node_modules[/\\]*$/).test(dir)) return; +Comparator.prototype.test = function (version) { + debug('Comparator.test', version, this.options.loose) - var pkgfile = path.join(maybeRealpathSync(realpathSync, dir, opts), 'package.json'); + if (this.semver === ANY) { + return true + } - if (!isFile(pkgfile)) { - return loadpkg(path.dirname(dir)); - } + if (typeof version === 'string') { + version = new SemVer(version, this.options) + } - var pkg = readPackageSync(readFileSync, pkgfile); + return cmp(version, this.operator, this.semver, this.options) +} - if (pkg && opts.packageFilter) { - // v2 will pass pkgfile - pkg = opts.packageFilter(pkg, /*pkgfile,*/ dir); // eslint-disable-line spaced-comment - } +Comparator.prototype.intersects = function (comp, options) { + if (!(comp instanceof Comparator)) { + throw new TypeError('a Comparator is required') + } - return { pkg: pkg, dir: dir }; + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false } + } - function loadAsDirectorySync(x) { - var pkgfile = path.join(maybeRealpathSync(realpathSync, x, opts), '/package.json'); - if (isFile(pkgfile)) { - try { - var pkg = readPackageSync(readFileSync, pkgfile); - } catch (e) {} + var rangeTmp - if (pkg && opts.packageFilter) { - // v2 will pass pkgfile - pkg = opts.packageFilter(pkg, /*pkgfile,*/ x); // eslint-disable-line spaced-comment - } + if (this.operator === '') { + rangeTmp = new Range(comp.value, options) + return satisfies(this.value, rangeTmp, options) + } else if (comp.operator === '') { + rangeTmp = new Range(this.value, options) + return satisfies(comp.semver, rangeTmp, options) + } - if (pkg && pkg.main) { - if (typeof pkg.main !== 'string') { - var mainError = new TypeError('package “' + pkg.name + '” `main` must be a string'); - mainError.code = 'INVALID_PACKAGE_MAIN'; - throw mainError; - } - if (pkg.main === '.' || pkg.main === './') { - pkg.main = 'index'; - } - try { - var m = loadAsFileSync(path.resolve(x, pkg.main)); - if (m) return m; - var n = loadAsDirectorySync(path.resolve(x, pkg.main)); - if (n) return n; - } catch (e) {} - } - } + var sameDirectionIncreasing = + (this.operator === '>=' || this.operator === '>') && + (comp.operator === '>=' || comp.operator === '>') + var sameDirectionDecreasing = + (this.operator === '<=' || this.operator === '<') && + (comp.operator === '<=' || comp.operator === '<') + var sameSemVer = this.semver.version === comp.semver.version + var differentDirectionsInclusive = + (this.operator === '>=' || this.operator === '<=') && + (comp.operator === '>=' || comp.operator === '<=') + var oppositeDirectionsLessThan = + cmp(this.semver, '<', comp.semver, options) && + ((this.operator === '>=' || this.operator === '>') && + (comp.operator === '<=' || comp.operator === '<')) + var oppositeDirectionsGreaterThan = + cmp(this.semver, '>', comp.semver, options) && + ((this.operator === '<=' || this.operator === '<') && + (comp.operator === '>=' || comp.operator === '>')) - return loadAsFileSync(path.join(x, '/index')); - } + return sameDirectionIncreasing || sameDirectionDecreasing || + (sameSemVer && differentDirectionsInclusive) || + oppositeDirectionsLessThan || oppositeDirectionsGreaterThan +} - function loadNodeModulesSync(x, start) { - var thunk = function () { return getPackageCandidates(x, start, opts); }; - var dirs = packageIterator ? packageIterator(x, start, thunk, opts) : thunk(); +exports.Range = Range +function Range (range, options) { + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false + } + } - for (var i = 0; i < dirs.length; i++) { - var dir = dirs[i]; - if (isDirectory(path.dirname(dir))) { - var m = loadAsFileSync(dir); - if (m) return m; - var n = loadAsDirectorySync(dir); - if (n) return n; - } - } + if (range instanceof Range) { + if (range.loose === !!options.loose && + range.includePrerelease === !!options.includePrerelease) { + return range + } else { + return new Range(range.raw, options) } -}; + } + if (range instanceof Comparator) { + return new Range(range.value, options) + } -/***/ }), -/* 392 */ -/***/ (function(module, exports) { + if (!(this instanceof Range)) { + return new Range(range, options) + } -module.exports = extractDescription + this.options = options + this.loose = !!options.loose + this.includePrerelease = !!options.includePrerelease -// Extracts description from contents of a readme file in markdown format -function extractDescription (d) { - if (!d) return; - if (d === "ERROR: No README data found!") return; - // the first block of text before the first heading - // that isn't the first line heading - d = d.trim().split('\n') - for (var s = 0; d[s] && d[s].trim().match(/^(#|$)/); s ++); - var l = d.length - for (var e = s + 1; e < l && d[e].trim(); e ++); - return d.slice(s, e).join(' ').trim() + // First, split based on boolean or || + this.raw = range + this.set = range.split(/\s*\|\|\s*/).map(function (range) { + return this.parseRange(range.trim()) + }, this).filter(function (c) { + // throw out any that are not relevant for whatever reason + return c.length + }) + + if (!this.set.length) { + throw new TypeError('Invalid SemVer Range: ' + range) + } + + this.format() } +Range.prototype.format = function () { + this.range = this.set.map(function (comps) { + return comps.join(' ').trim() + }).join('||').trim() + return this.range +} -/***/ }), -/* 393 */ -/***/ (function(module) { +Range.prototype.toString = function () { + return this.range +} -module.exports = JSON.parse("{\"topLevel\":{\"dependancies\":\"dependencies\",\"dependecies\":\"dependencies\",\"depdenencies\":\"dependencies\",\"devEependencies\":\"devDependencies\",\"depends\":\"dependencies\",\"dev-dependencies\":\"devDependencies\",\"devDependences\":\"devDependencies\",\"devDepenencies\":\"devDependencies\",\"devdependencies\":\"devDependencies\",\"repostitory\":\"repository\",\"repo\":\"repository\",\"prefereGlobal\":\"preferGlobal\",\"hompage\":\"homepage\",\"hampage\":\"homepage\",\"autohr\":\"author\",\"autor\":\"author\",\"contributers\":\"contributors\",\"publicationConfig\":\"publishConfig\",\"script\":\"scripts\"},\"bugs\":{\"web\":\"url\",\"name\":\"url\"},\"script\":{\"server\":\"start\",\"tests\":\"test\"}}"); +Range.prototype.parseRange = function (range) { + var loose = this.options.loose + range = range.trim() + // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4` + var hr = loose ? re[HYPHENRANGELOOSE] : re[HYPHENRANGE] + range = range.replace(hr, hyphenReplace) + debug('hyphen replace', range) + // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5` + range = range.replace(re[COMPARATORTRIM], comparatorTrimReplace) + debug('comparator trim', range, re[COMPARATORTRIM]) -/***/ }), -/* 394 */ -/***/ (function(module, exports, __webpack_require__) { + // `~ 1.2.3` => `~1.2.3` + range = range.replace(re[TILDETRIM], tildeTrimReplace) -var util = __webpack_require__(113) -var messages = __webpack_require__(395) + // `^ 1.2.3` => `^1.2.3` + range = range.replace(re[CARETTRIM], caretTrimReplace) -module.exports = function() { - var args = Array.prototype.slice.call(arguments, 0) - var warningName = args.shift() - if (warningName == "typo") { - return makeTypoWarning.apply(null,args) - } - else { - var msgTemplate = messages[warningName] ? messages[warningName] : warningName + ": '%s'" - args.unshift(msgTemplate) - return util.format.apply(null, args) + // normalize spaces + range = range.split(/\s+/).join(' ') + + // At this point, the range is completely trimmed and + // ready to be split into comparators. + + var compRe = loose ? re[COMPARATORLOOSE] : re[COMPARATOR] + var set = range.split(' ').map(function (comp) { + return parseComparator(comp, this.options) + }, this).join(' ').split(/\s+/) + if (this.options.loose) { + // in loose mode, throw out any that are not valid comparators + set = set.filter(function (comp) { + return !!comp.match(compRe) + }) } + set = set.map(function (comp) { + return new Comparator(comp, this.options) + }, this) + + return set } -function makeTypoWarning (providedName, probableName, field) { - if (field) { - providedName = field + "['" + providedName + "']" - probableName = field + "['" + probableName + "']" +Range.prototype.intersects = function (range, options) { + if (!(range instanceof Range)) { + throw new TypeError('a Range is required') } - return util.format(messages.typo, providedName, probableName) + + return this.set.some(function (thisComparators) { + return thisComparators.every(function (thisComparator) { + return range.set.some(function (rangeComparators) { + return rangeComparators.every(function (rangeComparator) { + return thisComparator.intersects(rangeComparator, options) + }) + }) + }) + }) } +// Mostly just for testing and legacy API reasons +exports.toComparators = toComparators +function toComparators (range, options) { + return new Range(range, options).set.map(function (comp) { + return comp.map(function (c) { + return c.value + }).join(' ').trim().split(' ') + }) +} -/***/ }), -/* 395 */ -/***/ (function(module) { +// comprised of xranges, tildes, stars, and gtlt's at this point. +// already replaced the hyphen ranges +// turn into a set of JUST comparators. +function parseComparator (comp, options) { + debug('comp', comp, options) + comp = replaceCarets(comp, options) + debug('caret', comp) + comp = replaceTildes(comp, options) + debug('tildes', comp) + comp = replaceXRanges(comp, options) + debug('xrange', comp) + comp = replaceStars(comp, options) + debug('stars', comp) + return comp +} -module.exports = JSON.parse("{\"repositories\":\"'repositories' (plural) Not supported. Please pick one as the 'repository' field\",\"missingRepository\":\"No repository field.\",\"brokenGitUrl\":\"Probably broken git url: %s\",\"nonObjectScripts\":\"scripts must be an object\",\"nonStringScript\":\"script values must be string commands\",\"nonArrayFiles\":\"Invalid 'files' member\",\"invalidFilename\":\"Invalid filename in 'files' list: %s\",\"nonArrayBundleDependencies\":\"Invalid 'bundleDependencies' list. Must be array of package names\",\"nonStringBundleDependency\":\"Invalid bundleDependencies member: %s\",\"nonDependencyBundleDependency\":\"Non-dependency in bundleDependencies: %s\",\"nonObjectDependencies\":\"%s field must be an object\",\"nonStringDependency\":\"Invalid dependency: %s %s\",\"deprecatedArrayDependencies\":\"specifying %s as array is deprecated\",\"deprecatedModules\":\"modules field is deprecated\",\"nonArrayKeywords\":\"keywords should be an array of strings\",\"nonStringKeyword\":\"keywords should be an array of strings\",\"conflictingName\":\"%s is also the name of a node core module.\",\"nonStringDescription\":\"'description' field should be a string\",\"missingDescription\":\"No description\",\"missingReadme\":\"No README data\",\"missingLicense\":\"No license field.\",\"nonEmailUrlBugsString\":\"Bug string field must be url, email, or {email,url}\",\"nonUrlBugsUrlField\":\"bugs.url field must be a string url. Deleted.\",\"nonEmailBugsEmailField\":\"bugs.email field must be a string email. Deleted.\",\"emptyNormalizedBugs\":\"Normalized value of bugs field is an empty object. Deleted.\",\"nonUrlHomepage\":\"homepage field must be a string url. Deleted.\",\"invalidLicense\":\"license should be a valid SPDX license expression\",\"typo\":\"%s should probably be %s.\"}"); +function isX (id) { + return !id || id.toLowerCase() === 'x' || id === '*' +} -/***/ }), -/* 396 */ -/***/ (function(module, exports, __webpack_require__) { +// ~, ~> --> * (any, kinda silly) +// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0 +// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0 +// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0 +// ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0 +// ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0 +function replaceTildes (comp, options) { + return comp.trim().split(/\s+/).map(function (comp) { + return replaceTilde(comp, options) + }).join(' ') +} -"use strict"; +function replaceTilde (comp, options) { + var r = options.loose ? re[TILDELOOSE] : re[TILDE] + return comp.replace(r, function (_, M, m, p, pr) { + debug('tilde', comp, _, M, m, p, pr) + var ret -const path = __webpack_require__(4); -const writeJsonFile = __webpack_require__(397); -const sortKeys = __webpack_require__(401); + if (isX(M)) { + ret = '' + } else if (isX(m)) { + ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' + } else if (isX(p)) { + // ~1.2 == >=1.2.0 <1.3.0 + ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' + } else if (pr) { + debug('replaceTilde pr', pr) + ret = '>=' + M + '.' + m + '.' + p + '-' + pr + + ' <' + M + '.' + (+m + 1) + '.0' + } else { + // ~1.2.3 == >=1.2.3 <1.3.0 + ret = '>=' + M + '.' + m + '.' + p + + ' <' + M + '.' + (+m + 1) + '.0' + } -const dependencyKeys = new Set([ - 'dependencies', - 'devDependencies', - 'optionalDependencies', - 'peerDependencies' -]); + debug('tilde return', ret) + return ret + }) +} -function normalize(packageJson) { - const result = {}; +// ^ --> * (any, kinda silly) +// ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0 +// ^2.0, ^2.0.x --> >=2.0.0 <3.0.0 +// ^1.2, ^1.2.x --> >=1.2.0 <2.0.0 +// ^1.2.3 --> >=1.2.3 <2.0.0 +// ^1.2.0 --> >=1.2.0 <2.0.0 +function replaceCarets (comp, options) { + return comp.trim().split(/\s+/).map(function (comp) { + return replaceCaret(comp, options) + }).join(' ') +} - for (const key of Object.keys(packageJson)) { - if (!dependencyKeys.has(key)) { - result[key] = packageJson[key]; - } else if (Object.keys(packageJson[key]).length !== 0) { - result[key] = sortKeys(packageJson[key]); - } - } +function replaceCaret (comp, options) { + debug('caret', comp, options) + var r = options.loose ? re[CARETLOOSE] : re[CARET] + return comp.replace(r, function (_, M, m, p, pr) { + debug('caret', comp, _, M, m, p, pr) + var ret + + if (isX(M)) { + ret = '' + } else if (isX(m)) { + ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' + } else if (isX(p)) { + if (M === '0') { + ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' + } else { + ret = '>=' + M + '.' + m + '.0 <' + (+M + 1) + '.0.0' + } + } else if (pr) { + debug('replaceCaret pr', pr) + if (M === '0') { + if (m === '0') { + ret = '>=' + M + '.' + m + '.' + p + '-' + pr + + ' <' + M + '.' + m + '.' + (+p + 1) + } else { + ret = '>=' + M + '.' + m + '.' + p + '-' + pr + + ' <' + M + '.' + (+m + 1) + '.0' + } + } else { + ret = '>=' + M + '.' + m + '.' + p + '-' + pr + + ' <' + (+M + 1) + '.0.0' + } + } else { + debug('no pr') + if (M === '0') { + if (m === '0') { + ret = '>=' + M + '.' + m + '.' + p + + ' <' + M + '.' + m + '.' + (+p + 1) + } else { + ret = '>=' + M + '.' + m + '.' + p + + ' <' + M + '.' + (+m + 1) + '.0' + } + } else { + ret = '>=' + M + '.' + m + '.' + p + + ' <' + (+M + 1) + '.0.0' + } + } - return result; + debug('caret return', ret) + return ret + }) } -module.exports = async (filePath, data, options) => { - if (typeof filePath !== 'string') { - options = data; - data = filePath; - filePath = '.'; - } - - options = { - normalize: true, - ...options, - detectIndent: true - }; - - filePath = path.basename(filePath) === 'package.json' ? filePath : path.join(filePath, 'package.json'); - - data = options.normalize ? normalize(data) : data; +function replaceXRanges (comp, options) { + debug('replaceXRanges', comp, options) + return comp.split(/\s+/).map(function (comp) { + return replaceXRange(comp, options) + }).join(' ') +} - return writeJsonFile(filePath, data, options); -}; +function replaceXRange (comp, options) { + comp = comp.trim() + var r = options.loose ? re[XRANGELOOSE] : re[XRANGE] + return comp.replace(r, function (ret, gtlt, M, m, p, pr) { + debug('xRange', comp, ret, gtlt, M, m, p, pr) + var xM = isX(M) + var xm = xM || isX(m) + var xp = xm || isX(p) + var anyX = xp -module.exports.sync = (filePath, data, options) => { - if (typeof filePath !== 'string') { - options = data; - data = filePath; - filePath = '.'; - } + if (gtlt === '=' && anyX) { + gtlt = '' + } - options = { - normalize: true, - ...options, - detectIndent: true - }; + if (xM) { + if (gtlt === '>' || gtlt === '<') { + // nothing is allowed + ret = '<0.0.0' + } else { + // nothing is forbidden + ret = '*' + } + } else if (gtlt && anyX) { + // we know patch is an x, because we have any x at all. + // replace X with 0 + if (xm) { + m = 0 + } + p = 0 - filePath = path.basename(filePath) === 'package.json' ? filePath : path.join(filePath, 'package.json'); + if (gtlt === '>') { + // >1 => >=2.0.0 + // >1.2 => >=1.3.0 + // >1.2.3 => >= 1.2.4 + gtlt = '>=' + if (xm) { + M = +M + 1 + m = 0 + p = 0 + } else { + m = +m + 1 + p = 0 + } + } else if (gtlt === '<=') { + // <=0.7.x is actually <0.8.0, since any 0.7.x should + // pass. Similarly, <=7.x is actually <8.0.0, etc. + gtlt = '<' + if (xm) { + M = +M + 1 + } else { + m = +m + 1 + } + } - data = options.normalize ? normalize(data) : data; + ret = gtlt + M + '.' + m + '.' + p + } else if (xm) { + ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' + } else if (xp) { + ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' + } - writeJsonFile.sync(filePath, data, options); -}; + debug('xRange return', ret) + return ret + }) +} -/***/ }), -/* 397 */ -/***/ (function(module, exports, __webpack_require__) { +// Because * is AND-ed with everything else in the comparator, +// and '' means "any version", just remove the *s entirely. +function replaceStars (comp, options) { + debug('replaceStars', comp, options) + // Looseness is ignored here. star is always as loose as it gets! + return comp.trim().replace(re[STAR], '') +} -"use strict"; +// This function is passed to string.replace(re[HYPHENRANGE]) +// M, m, patch, prerelease, build +// 1.2 - 3.4.5 => >=1.2.0 <=3.4.5 +// 1.2.3 - 3.4 => >=1.2.0 <3.5.0 Any 3.4.x will do +// 1.2 - 3.4 => >=1.2.0 <3.5.0 +function hyphenReplace ($0, + from, fM, fm, fp, fpr, fb, + to, tM, tm, tp, tpr, tb) { + if (isX(fM)) { + from = '' + } else if (isX(fm)) { + from = '>=' + fM + '.0.0' + } else if (isX(fp)) { + from = '>=' + fM + '.' + fm + '.0' + } else { + from = '>=' + from + } -const path = __webpack_require__(4); -const fs = __webpack_require__(233); -const writeFileAtomic = __webpack_require__(398); -const sortKeys = __webpack_require__(401); -const makeDir = __webpack_require__(403); -const pify = __webpack_require__(404); -const detectIndent = __webpack_require__(406); + if (isX(tM)) { + to = '' + } else if (isX(tm)) { + to = '<' + (+tM + 1) + '.0.0' + } else if (isX(tp)) { + to = '<' + tM + '.' + (+tm + 1) + '.0' + } else if (tpr) { + to = '<=' + tM + '.' + tm + '.' + tp + '-' + tpr + } else { + to = '<=' + to + } -const init = (fn, filePath, data, options) => { - if (!filePath) { - throw new TypeError('Expected a filepath'); - } + return (from + ' ' + to).trim() +} - if (data === undefined) { - throw new TypeError('Expected data to stringify'); - } +// if ANY of the sets match ALL of its comparators, then pass +Range.prototype.test = function (version) { + if (!version) { + return false + } - options = Object.assign({ - indent: '\t', - sortKeys: false - }, options); + if (typeof version === 'string') { + version = new SemVer(version, this.options) + } - if (options.sortKeys) { - data = sortKeys(data, { - deep: true, - compare: typeof options.sortKeys === 'function' ? options.sortKeys : undefined - }); - } + for (var i = 0; i < this.set.length; i++) { + if (testSet(this.set[i], version, this.options)) { + return true + } + } + return false +} - return fn(filePath, data, options); -}; +function testSet (set, version, options) { + for (var i = 0; i < set.length; i++) { + if (!set[i].test(version)) { + return false + } + } -const readFile = filePath => pify(fs.readFile)(filePath, 'utf8').catch(() => {}); + if (version.prerelease.length && !options.includePrerelease) { + // Find the set of versions that are allowed to have prereleases + // For example, ^1.2.3-pr.1 desugars to >=1.2.3-pr.1 <2.0.0 + // That should allow `1.2.3-pr.2` to pass. + // However, `1.2.4-alpha.notready` should NOT be allowed, + // even though it's within the range set by the comparators. + for (i = 0; i < set.length; i++) { + debug(set[i].semver) + if (set[i].semver === ANY) { + continue + } -const main = (filePath, data, options) => { - return (options.detectIndent ? readFile(filePath) : Promise.resolve()) - .then(string => { - const indent = string ? detectIndent(string).indent : options.indent; - const json = JSON.stringify(data, options.replacer, indent); + if (set[i].semver.prerelease.length > 0) { + var allowed = set[i].semver + if (allowed.major === version.major && + allowed.minor === version.minor && + allowed.patch === version.patch) { + return true + } + } + } - return pify(writeFileAtomic)(filePath, `${json}\n`, {mode: options.mode}); - }); -}; + // Version has a -pre, but it's not one of the ones we like. + return false + } -const mainSync = (filePath, data, options) => { - let {indent} = options; + return true +} - if (options.detectIndent) { - try { - const file = fs.readFileSync(filePath, 'utf8'); - indent = detectIndent(file).indent; - } catch (error) { - if (error.code !== 'ENOENT') { - throw error; - } - } - } +exports.satisfies = satisfies +function satisfies (version, range, options) { + try { + range = new Range(range, options) + } catch (er) { + return false + } + return range.test(version) +} - const json = JSON.stringify(data, options.replacer, indent); +exports.maxSatisfying = maxSatisfying +function maxSatisfying (versions, range, options) { + var max = null + var maxSV = null + try { + var rangeObj = new Range(range, options) + } catch (er) { + return null + } + versions.forEach(function (v) { + if (rangeObj.test(v)) { + // satisfies(v, range, options) + if (!max || maxSV.compare(v) === -1) { + // compare(max, v, true) + max = v + maxSV = new SemVer(max, options) + } + } + }) + return max +} - return writeFileAtomic.sync(filePath, `${json}\n`, {mode: options.mode}); -}; +exports.minSatisfying = minSatisfying +function minSatisfying (versions, range, options) { + var min = null + var minSV = null + try { + var rangeObj = new Range(range, options) + } catch (er) { + return null + } + versions.forEach(function (v) { + if (rangeObj.test(v)) { + // satisfies(v, range, options) + if (!min || minSV.compare(v) === 1) { + // compare(min, v, true) + min = v + minSV = new SemVer(min, options) + } + } + }) + return min +} -const writeJsonFile = (filePath, data, options) => { - return makeDir(path.dirname(filePath), {fs}) - .then(() => init(main, filePath, data, options)); -}; +exports.minVersion = minVersion +function minVersion (range, loose) { + range = new Range(range, loose) -module.exports = writeJsonFile; -// TODO: Remove this for the next major release -module.exports.default = writeJsonFile; -module.exports.sync = (filePath, data, options) => { - makeDir.sync(path.dirname(filePath), {fs}); - init(mainSync, filePath, data, options); -}; + var minver = new SemVer('0.0.0') + if (range.test(minver)) { + return minver + } + minver = new SemVer('0.0.0-0') + if (range.test(minver)) { + return minver + } -/***/ }), -/* 398 */ -/***/ (function(module, exports, __webpack_require__) { + minver = null + for (var i = 0; i < range.set.length; ++i) { + var comparators = range.set[i] -"use strict"; + comparators.forEach(function (comparator) { + // Clone to avoid manipulating the comparator's semver object. + var compver = new SemVer(comparator.semver.version) + switch (comparator.operator) { + case '>': + if (compver.prerelease.length === 0) { + compver.patch++ + } else { + compver.prerelease.push(0) + } + compver.raw = compver.format() + /* fallthrough */ + case '': + case '>=': + if (!minver || gt(minver, compver)) { + minver = compver + } + break + case '<': + case '<=': + /* Ignore maximum versions */ + break + /* istanbul ignore next */ + default: + throw new Error('Unexpected operation: ' + comparator.operator) + } + }) + } -module.exports = writeFile -module.exports.sync = writeFileSync -module.exports._getTmpname = getTmpname // for testing -module.exports._cleanupOnExit = cleanupOnExit + if (minver && range.test(minver)) { + return minver + } -var fs = __webpack_require__(233) -var MurmurHash3 = __webpack_require__(399) -var onExit = __webpack_require__(161) -var path = __webpack_require__(4) -var activeFiles = {} + return null +} -// if we run inside of a worker_thread, `process.pid` is not unique -/* istanbul ignore next */ -var threadId = (function getId () { +exports.validRange = validRange +function validRange (range, options) { try { - var workerThreads = __webpack_require__(400) - - /// if we are in main thread, this is set to `0` - return workerThreads.threadId - } catch (e) { - // worker_threads are not available, fallback to 0 - return 0 + // Return '*' instead of '' so that truthiness works. + // This will throw if it's invalid anyway + return new Range(range, options).range || '*' + } catch (er) { + return null } -})() - -var invocations = 0 -function getTmpname (filename) { - return filename + '.' + - MurmurHash3(__filename) - .hash(String(process.pid)) - .hash(String(threadId)) - .hash(String(++invocations)) - .result() } -function cleanupOnExit (tmpfile) { - return function () { - try { - fs.unlinkSync(typeof tmpfile === 'function' ? tmpfile() : tmpfile) - } catch (_) {} - } +// Determine if version is less than all the versions possible in the range +exports.ltr = ltr +function ltr (version, range, options) { + return outside(version, range, '<', options) } -function writeFile (filename, data, options, callback) { - if (options) { - if (options instanceof Function) { - callback = options - options = {} - } else if (typeof options === 'string') { - options = { encoding: options } - } - } else { - options = {} - } +// Determine if version is greater than all the versions possible in the range. +exports.gtr = gtr +function gtr (version, range, options) { + return outside(version, range, '>', options) +} - var Promise = options.Promise || global.Promise - var truename - var fd - var tmpfile - /* istanbul ignore next -- The closure only gets called when onExit triggers */ - var removeOnExitHandler = onExit(cleanupOnExit(() => tmpfile)) - var absoluteName = path.resolve(filename) +exports.outside = outside +function outside (version, range, hilo, options) { + version = new SemVer(version, options) + range = new Range(range, options) - new Promise(function serializeSameFile (resolve) { - // make a queue if it doesn't already exist - if (!activeFiles[absoluteName]) activeFiles[absoluteName] = [] + var gtfn, ltefn, ltfn, comp, ecomp + switch (hilo) { + case '>': + gtfn = gt + ltefn = lte + ltfn = lt + comp = '>' + ecomp = '>=' + break + case '<': + gtfn = lt + ltefn = gte + ltfn = gt + comp = '<' + ecomp = '<=' + break + default: + throw new TypeError('Must provide a hilo val of "<" or ">"') + } - activeFiles[absoluteName].push(resolve) // add this job to the queue - if (activeFiles[absoluteName].length === 1) resolve() // kick off the first one - }).then(function getRealPath () { - return new Promise(function (resolve) { - fs.realpath(filename, function (_, realname) { - truename = realname || filename - tmpfile = getTmpname(truename) - resolve() - }) - }) - }).then(function stat () { - return new Promise(function stat (resolve) { - if (options.mode && options.chown) resolve() - else { - // Either mode or chown is not explicitly set - // Default behavior is to copy it from original file - fs.stat(truename, function (err, stats) { - if (err || !stats) resolve() - else { - options = Object.assign({}, options) + // If it satisifes the range it is not outside + if (satisfies(version, range, options)) { + return false + } - if (options.mode == null) { - options.mode = stats.mode - } - if (options.chown == null && process.getuid) { - options.chown = { uid: stats.uid, gid: stats.gid } - } - resolve() - } - }) - } - }) - }).then(function thenWriteFile () { - return new Promise(function (resolve, reject) { - fs.open(tmpfile, 'w', options.mode, function (err, _fd) { - fd = _fd - if (err) reject(err) - else resolve() - }) - }) - }).then(function write () { - return new Promise(function (resolve, reject) { - if (Buffer.isBuffer(data)) { - fs.write(fd, data, 0, data.length, 0, function (err) { - if (err) reject(err) - else resolve() - }) - } else if (data != null) { - fs.write(fd, String(data), 0, String(options.encoding || 'utf8'), function (err) { - if (err) reject(err) - else resolve() - }) - } else resolve() - }) - }).then(function syncAndClose () { - return new Promise(function (resolve, reject) { - if (options.fsync !== false) { - fs.fsync(fd, function (err) { - if (err) fs.close(fd, () => reject(err)) - else fs.close(fd, resolve) - }) - } else { - fs.close(fd, resolve) + // From now on, variable terms are as if we're in "gtr" mode. + // but note that everything is flipped for the "ltr" function. + + for (var i = 0; i < range.set.length; ++i) { + var comparators = range.set[i] + + var high = null + var low = null + + comparators.forEach(function (comparator) { + if (comparator.semver === ANY) { + comparator = new Comparator('>=0.0.0') + } + high = high || comparator + low = low || comparator + if (gtfn(comparator.semver, high.semver, options)) { + high = comparator + } else if (ltfn(comparator.semver, low.semver, options)) { + low = comparator } }) - }).then(function chown () { - fd = null - if (options.chown) { - return new Promise(function (resolve, reject) { - fs.chown(tmpfile, options.chown.uid, options.chown.gid, function (err) { - if (err) reject(err) - else resolve() - }) - }) + + // If the edge version comparator has a operator then our version + // isn't outside it + if (high.operator === comp || high.operator === ecomp) { + return false } - }).then(function chmod () { - if (options.mode) { - return new Promise(function (resolve, reject) { - fs.chmod(tmpfile, options.mode, function (err) { - if (err) reject(err) - else resolve() - }) - }) + + // If the lowest version comparator has an operator and our version + // is less than it then it isn't higher than the range + if ((!low.operator || low.operator === comp) && + ltefn(version, low.semver)) { + return false + } else if (low.operator === ecomp && ltfn(version, low.semver)) { + return false } - }).then(function rename () { - return new Promise(function (resolve, reject) { - fs.rename(tmpfile, truename, function (err) { - if (err) reject(err) - else resolve() - }) - }) - }).then(function success () { - removeOnExitHandler() - callback() - }, function fail (err) { - return new Promise(resolve => { - return fd ? fs.close(fd, resolve) : resolve() - }).then(() => { - removeOnExitHandler() - fs.unlink(tmpfile, function () { - callback(err) - }) - }) - }).then(function checkQueue () { - activeFiles[absoluteName].shift() // remove the element added by serializeSameFile - if (activeFiles[absoluteName].length > 0) { - activeFiles[absoluteName][0]() // start next job if one is pending - } else delete activeFiles[absoluteName] - }) + } + return true } -function writeFileSync (filename, data, options) { - if (typeof options === 'string') options = { encoding: options } - else if (!options) options = {} - try { - filename = fs.realpathSync(filename) - } catch (ex) { - // it's ok, it'll happen on a not yet existing file +exports.prerelease = prerelease +function prerelease (version, options) { + var parsed = parse(version, options) + return (parsed && parsed.prerelease.length) ? parsed.prerelease : null +} + +exports.intersects = intersects +function intersects (r1, r2, options) { + r1 = new Range(r1, options) + r2 = new Range(r2, options) + return r1.intersects(r2) +} + +exports.coerce = coerce +function coerce (version) { + if (version instanceof SemVer) { + return version } - var tmpfile = getTmpname(filename) - if (!options.mode || !options.chown) { - // Either mode or chown is not explicitly set - // Default behavior is to copy it from original file - try { - var stats = fs.statSync(filename) - options = Object.assign({}, options) - if (!options.mode) { - options.mode = stats.mode - } - if (!options.chown && process.getuid) { - options.chown = { uid: stats.uid, gid: stats.gid } - } - } catch (ex) { - // ignore stat errors - } + if (typeof version !== 'string') { + return null } - var fd - var cleanup = cleanupOnExit(tmpfile) - var removeOnExitHandler = onExit(cleanup) + var match = version.match(re[COERCE]) - try { - fd = fs.openSync(tmpfile, 'w', options.mode) - if (Buffer.isBuffer(data)) { - fs.writeSync(fd, data, 0, data.length, 0) - } else if (data != null) { - fs.writeSync(fd, String(data), 0, String(options.encoding || 'utf8')) - } - if (options.fsync !== false) { - fs.fsyncSync(fd) - } - fs.closeSync(fd) - if (options.chown) fs.chownSync(tmpfile, options.chown.uid, options.chown.gid) - if (options.mode) fs.chmodSync(tmpfile, options.mode) - fs.renameSync(tmpfile, filename) - removeOnExitHandler() - } catch (err) { - if (fd) { - try { - fs.closeSync(fd) - } catch (ex) { - // ignore close errors at this stage, error may have closed fd already. - } - } - removeOnExitHandler() - cleanup() - throw err + if (match == null) { + return null } + + return parse(match[1] + + '.' + (match[2] || '0') + + '.' + (match[3] || '0')) } /***/ }), -/* 399 */ +/* 412 */ /***/ (function(module, exports, __webpack_require__) { -/** - * @preserve - * JS Implementation of incremental MurmurHash3 (r150) (as of May 10, 2013) - * - * @author Jens Taylor - * @see http://github.com/homebrewing/brauhaus-diff - * @author Gary Court - * @see http://github.com/garycourt/murmurhash-js - * @author Austin Appleby - * @see http://sites.google.com/site/murmurhash/ - */ -(function(){ - var cache; +"use strict"; - // Call this function without `new` to use the cached object (good for - // single-threaded environments), or with `new` to create a new object. - // - // @param {string} key A UTF-16 or ASCII string - // @param {number} seed An optional positive integer - // @return {object} A MurmurHash3 object for incremental hashing - function MurmurHash3(key, seed) { - var m = this instanceof MurmurHash3 ? this : cache; - m.reset(seed) - if (typeof key === 'string' && key.length > 0) { - m.hash(key); - } - if (m !== this) { - return m; - } - }; +// detect either spaces or tabs but not both to properly handle tabs +// for indentation and spaces for alignment +const INDENT_RE = /^(?:( )+|\t+)/; - // Incrementally add a string to this hash - // - // @param {string} key A UTF-16 or ASCII string - // @return {object} this - MurmurHash3.prototype.hash = function(key) { - var h1, k1, i, top, len; +function getMostUsed(indents) { + let result = 0; + let maxUsed = 0; + let maxWeight = 0; - len = key.length; - this.len += len; + for (const entry of indents) { + // TODO: use destructuring when targeting Node.js 6 + const key = entry[0]; + const val = entry[1]; - k1 = this.k1; - i = 0; - switch (this.rem) { - case 0: k1 ^= len > i ? (key.charCodeAt(i++) & 0xffff) : 0; - case 1: k1 ^= len > i ? (key.charCodeAt(i++) & 0xffff) << 8 : 0; - case 2: k1 ^= len > i ? (key.charCodeAt(i++) & 0xffff) << 16 : 0; - case 3: - k1 ^= len > i ? (key.charCodeAt(i) & 0xff) << 24 : 0; - k1 ^= len > i ? (key.charCodeAt(i++) & 0xff00) >> 8 : 0; - } + const u = val[0]; + const w = val[1]; - this.rem = (len + this.rem) & 3; // & 3 is same as % 4 - len -= this.rem; - if (len > 0) { - h1 = this.h1; - while (1) { - k1 = (k1 * 0x2d51 + (k1 & 0xffff) * 0xcc9e0000) & 0xffffffff; - k1 = (k1 << 15) | (k1 >>> 17); - k1 = (k1 * 0x3593 + (k1 & 0xffff) * 0x1b870000) & 0xffffffff; + if (u > maxUsed || (u === maxUsed && w > maxWeight)) { + maxUsed = u; + maxWeight = w; + result = Number(key); + } + } - h1 ^= k1; - h1 = (h1 << 13) | (h1 >>> 19); - h1 = (h1 * 5 + 0xe6546b64) & 0xffffffff; + return result; +} - if (i >= len) { - break; - } +module.exports = str => { + if (typeof str !== 'string') { + throw new TypeError('Expected a string'); + } - k1 = ((key.charCodeAt(i++) & 0xffff)) ^ - ((key.charCodeAt(i++) & 0xffff) << 8) ^ - ((key.charCodeAt(i++) & 0xffff) << 16); - top = key.charCodeAt(i++); - k1 ^= ((top & 0xff) << 24) ^ - ((top & 0xff00) >> 8); - } + // used to see if tabs or spaces are the most used + let tabs = 0; + let spaces = 0; - k1 = 0; - switch (this.rem) { - case 3: k1 ^= (key.charCodeAt(i + 2) & 0xffff) << 16; - case 2: k1 ^= (key.charCodeAt(i + 1) & 0xffff) << 8; - case 1: k1 ^= (key.charCodeAt(i) & 0xffff); - } + // remember the size of previous line's indentation + let prev = 0; - this.h1 = h1; - } + // remember how many indents/unindents as occurred for a given size + // and how much lines follow a given indentation + // + // indents = { + // 3: [1, 0], + // 4: [1, 5], + // 5: [1, 0], + // 12: [1, 0], + // } + const indents = new Map(); - this.k1 = k1; - return this; - }; + // pointer to the array of last used indent + let current; - // Get the result of this hash - // - // @return {number} The 32-bit hash - MurmurHash3.prototype.result = function() { - var k1, h1; - - k1 = this.k1; - h1 = this.h1; + // whether the last action was an indent (opposed to an unindent) + let isIndent; - if (k1 > 0) { - k1 = (k1 * 0x2d51 + (k1 & 0xffff) * 0xcc9e0000) & 0xffffffff; - k1 = (k1 << 15) | (k1 >>> 17); - k1 = (k1 * 0x3593 + (k1 & 0xffff) * 0x1b870000) & 0xffffffff; - h1 ^= k1; - } + for (const line of str.split(/\n/g)) { + if (!line) { + // ignore empty lines + continue; + } - h1 ^= this.len; + let indent; + const matches = line.match(INDENT_RE); - h1 ^= h1 >>> 16; - h1 = (h1 * 0xca6b + (h1 & 0xffff) * 0x85eb0000) & 0xffffffff; - h1 ^= h1 >>> 13; - h1 = (h1 * 0xae35 + (h1 & 0xffff) * 0xc2b20000) & 0xffffffff; - h1 ^= h1 >>> 16; + if (matches) { + indent = matches[0].length; - return h1 >>> 0; - }; + if (matches[1]) { + spaces++; + } else { + tabs++; + } + } else { + indent = 0; + } - // Reset the hash object for reuse - // - // @param {number} seed An optional positive integer - MurmurHash3.prototype.reset = function(seed) { - this.h1 = typeof seed === 'number' ? seed : 0; - this.rem = this.k1 = this.len = 0; - return this; - }; + const diff = indent - prev; + prev = indent; - // A cached object to use. This can be safely used if you're in a single- - // threaded environment, otherwise you need to create new hashes to use. - cache = new MurmurHash3(); + if (diff) { + // an indent or unindent has been detected - if (true) { - module.exports = MurmurHash3; - } else {} -}()); + isIndent = diff > 0; + + current = indents.get(isIndent ? diff : -diff); + + if (current) { + current[0]++; + } else { + current = [1, 0]; + indents.set(diff, current); + } + } else if (current) { + // if the last action was an indent, increment the weight + current[1] += Number(isIndent); + } + } + + const amount = getMostUsed(indents); + + let type; + let indent; + if (!amount) { + type = null; + indent = ''; + } else if (spaces >= tabs) { + type = 'space'; + indent = ' '.repeat(amount); + } else { + type = 'tab'; + indent = '\t'.repeat(amount); + } + + return { + amount, + type, + indent + }; +}; /***/ }), -/* 400 */ -/***/ (function(module, exports) { +/* 413 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { -module.exports = require(undefined); +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "installInDir", function() { return installInDir; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "runScriptInPackage", function() { return runScriptInPackage; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "runScriptInPackageStreaming", function() { return runScriptInPackageStreaming; }); +/* harmony import */ var _child_process__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(221); +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +const YARN_EXEC = process.env.npm_execpath || 'yarn'; +/** + * Install all dependencies in the given directory + */ + +async function installInDir(directory, extraArgs = []) { + const options = ['install', '--non-interactive', ...extraArgs]; // We pass the mutex flag to ensure only one instance of yarn runs at any + // given time (e.g. to avoid conflicts). + + await Object(_child_process__WEBPACK_IMPORTED_MODULE_0__["spawn"])(YARN_EXEC, options, { + cwd: directory + }); +} +/** + * Run script in the given directory + */ + +async function runScriptInPackage(script, args, pkg) { + const execOpts = { + cwd: pkg.path + }; + await Object(_child_process__WEBPACK_IMPORTED_MODULE_0__["spawn"])(YARN_EXEC, ['run', script, ...args], execOpts); +} +/** + * Run script in the given directory + */ + +function runScriptInPackageStreaming({ + script, + args, + pkg, + debug +}) { + const execOpts = { + cwd: pkg.path + }; + return Object(_child_process__WEBPACK_IMPORTED_MODULE_0__["spawnStreaming"])(YARN_EXEC, ['run', script, ...args], execOpts, { + prefix: pkg.name, + debug + }); +} /***/ }), -/* 401 */ -/***/ (function(module, exports, __webpack_require__) { +/* 414 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "readYarnLock", function() { return readYarnLock; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "resolveDepsForProject", function() { return resolveDepsForProject; }); +/* harmony import */ var _yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(415); +/* harmony import */ var _yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _utils_fs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(231); +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +// @ts-expect-error published types are worthless -const isPlainObj = __webpack_require__(402); -module.exports = (obj, opts) => { - if (!isPlainObj(obj)) { - throw new TypeError('Expected a plain object'); - } +async function readYarnLock(kbn) { + try { + const contents = await Object(_utils_fs__WEBPACK_IMPORTED_MODULE_1__["readFile"])(kbn.getAbsolute('yarn.lock'), 'utf8'); + const yarnLock = Object(_yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__["parse"])(contents); - opts = opts || {}; + if (yarnLock.type === 'success') { + return yarnLock.object; + } - // DEPRECATED - if (typeof opts === 'function') { - throw new TypeError('Specify the compare function as an option instead'); - } + throw new Error('unable to read yarn.lock file, please run `yarn kbn bootstrap`'); + } catch (error) { + if (error.code !== 'ENOENT') { + throw error; + } + } - const deep = opts.deep; - const seenInput = []; - const seenOutput = []; + return {}; +} +/** + * Get a list of the absolute dependencies of this project, as resolved + * in the yarn.lock file, does not include other projects in the workspace + * or their dependencies + */ - const sortKeys = x => { - const seenIndex = seenInput.indexOf(x); +function resolveDepsForProject({ + project: rootProject, + yarnLock, + kbn, + log, + productionDepsOnly, + includeDependentProject +}) { + /** map of [name@range, { name, version }] */ + const resolved = new Map(); + const seenProjects = new Set(); + const projectQueue = [rootProject]; + const depQueue = []; - if (seenIndex !== -1) { - return seenOutput[seenIndex]; - } + while (projectQueue.length) { + const project = projectQueue.shift(); - const ret = {}; - const keys = Object.keys(x).sort(opts.compare); + if (seenProjects.has(project)) { + continue; + } + + seenProjects.add(project); + const projectDeps = Object.entries(productionDepsOnly ? project.productionDependencies : project.allDependencies); - seenInput.push(x); - seenOutput.push(ret); + for (const [name, versionRange] of projectDeps) { + depQueue.push([name, versionRange]); + } - for (let i = 0; i < keys.length; i++) { - const key = keys[i]; - const val = x[key]; + while (depQueue.length) { + const [name, versionRange] = depQueue.shift(); + const req = `${name}@${versionRange}`; - if (deep && Array.isArray(val)) { - const retArr = []; + if (resolved.has(req)) { + continue; + } - for (let j = 0; j < val.length; j++) { - retArr[j] = isPlainObj(val[j]) ? sortKeys(val[j]) : val[j]; - } + if (includeDependentProject && kbn.hasProject(name)) { + projectQueue.push(kbn.getProject(name)); + } - ret[key] = retArr; - continue; - } + if (!kbn.hasProject(name)) { + const pkg = yarnLock[req]; - ret[key] = deep && isPlainObj(val) ? sortKeys(val) : val; - } + if (!pkg) { + log.warning('yarn.lock file is out of date, please run `yarn kbn bootstrap` to re-enable caching'); + return; + } - return ret; - }; + resolved.set(req, { + name, + version: pkg.version + }); + const allDepsEntries = [...Object.entries(pkg.dependencies || {}), ...Object.entries(pkg.optionalDependencies || {})]; - return sortKeys(obj); -}; + for (const [childName, childVersionRange] of allDepsEntries) { + depQueue.push([childName, childVersionRange]); + } + } + } + } + return resolved; +} /***/ }), -/* 402 */ +/* 415 */ /***/ (function(module, exports, __webpack_require__) { -"use strict"; - -var toString = Object.prototype.toString; - -module.exports = function (x) { - var prototype; - return toString.call(x) === '[object Object]' && (prototype = Object.getPrototypeOf(x), prototype === null || prototype === Object.getPrototypeOf({})); -}; +module.exports = +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // identity function for calling harmony imports with the correct context +/******/ __webpack_require__.i = function(value) { return value; }; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { +/******/ configurable: false, +/******/ enumerable: true, +/******/ get: getter +/******/ }); +/******/ } +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = 14); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ (function(module, exports) { +module.exports = __webpack_require__(4); /***/ }), -/* 403 */ +/* 1 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const fs = __webpack_require__(132); -const path = __webpack_require__(4); -const pify = __webpack_require__(404); -const semver = __webpack_require__(405); -const defaults = { - mode: 0o777 & (~process.umask()), - fs -}; +exports.__esModule = true; -const useNativeRecursiveOption = semver.satisfies(process.version, '>=10.12.0'); +var _promise = __webpack_require__(173); -// https://github.com/nodejs/node/issues/8987 -// https://github.com/libuv/libuv/pull/1088 -const checkPath = pth => { - if (process.platform === 'win32') { - const pathHasInvalidWinCharacters = /[<>:"|?*]/.test(pth.replace(path.parse(pth).root, '')); +var _promise2 = _interopRequireDefault(_promise); - if (pathHasInvalidWinCharacters) { - const error = new Error(`Path contains invalid characters: ${pth}`); - error.code = 'EINVAL'; - throw error; - } - } -}; +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } -const permissionError = pth => { - // This replicates the exception of `fs.mkdir` with native the - // `recusive` option when run on an invalid drive under Windows. - const error = new Error(`operation not permitted, mkdir '${pth}'`); - error.code = 'EPERM'; - error.errno = -4048; - error.path = pth; - error.syscall = 'mkdir'; - return error; -}; +exports.default = function (fn) { + return function () { + var gen = fn.apply(this, arguments); + return new _promise2.default(function (resolve, reject) { + function step(key, arg) { + try { + var info = gen[key](arg); + var value = info.value; + } catch (error) { + reject(error); + return; + } -const makeDir = (input, options) => Promise.resolve().then(() => { - checkPath(input); - options = Object.assign({}, defaults, options); + if (info.done) { + resolve(value); + } else { + return _promise2.default.resolve(value).then(function (value) { + step("next", value); + }, function (err) { + step("throw", err); + }); + } + } - // TODO: Use util.promisify when targeting Node.js 8 - const mkdir = pify(options.fs.mkdir); - const stat = pify(options.fs.stat); + return step("next"); + }); + }; +}; - if (useNativeRecursiveOption && options.fs.mkdir === fs.mkdir) { - const pth = path.resolve(input); +/***/ }), +/* 2 */ +/***/ (function(module, exports) { - return mkdir(pth, { - mode: options.mode, - recursive: true - }).then(() => pth); - } +module.exports = __webpack_require__(113); - const make = pth => { - return mkdir(pth, options.mode) - .then(() => pth) - .catch(error => { - if (error.code === 'EPERM') { - throw error; - } +/***/ }), +/* 3 */ +/***/ (function(module, exports) { - if (error.code === 'ENOENT') { - if (path.dirname(pth) === pth) { - throw permissionError(pth); - } +module.exports = __webpack_require__(132); - if (error.message.includes('null bytes')) { - throw error; - } +/***/ }), +/* 4 */ +/***/ (function(module, exports, __webpack_require__) { - return make(path.dirname(pth)).then(() => make(pth)); - } +"use strict"; - return stat(pth) - .then(stats => stats.isDirectory() ? pth : Promise.reject()) - .catch(() => { - throw error; - }); - }); - }; - return make(path.resolve(input)); +Object.defineProperty(exports, "__esModule", { + value: true }); +class MessageError extends Error { + constructor(msg, code) { + super(msg); + this.code = code; + } -module.exports = makeDir; -module.exports.default = makeDir; - -module.exports.sync = (input, options) => { - checkPath(input); - options = Object.assign({}, defaults, options); - - if (useNativeRecursiveOption && options.fs.mkdirSync === fs.mkdirSync) { - const pth = path.resolve(input); - - fs.mkdirSync(pth, { - mode: options.mode, - recursive: true - }); - - return pth; - } - - const make = pth => { - try { - options.fs.mkdirSync(pth, options.mode); - } catch (error) { - if (error.code === 'EPERM') { - throw error; - } - - if (error.code === 'ENOENT') { - if (path.dirname(pth) === pth) { - throw permissionError(pth); - } +} - if (error.message.includes('null bytes')) { - throw error; - } +exports.MessageError = MessageError; +class ProcessSpawnError extends MessageError { + constructor(msg, code, process) { + super(msg, code); + this.process = process; + } - make(path.dirname(pth)); - return make(pth); - } +} - try { - if (!options.fs.statSync(pth).isDirectory()) { - throw new Error('The path is not a directory'); - } - } catch (_) { - throw error; - } - } +exports.ProcessSpawnError = ProcessSpawnError; +class SecurityError extends MessageError {} - return pth; - }; +exports.SecurityError = SecurityError; +class ProcessTermError extends MessageError {} - return make(path.resolve(input)); -}; +exports.ProcessTermError = ProcessTermError; +class ResponseError extends Error { + constructor(msg, responseCode) { + super(msg); + this.responseCode = responseCode; + } +} +exports.ResponseError = ResponseError; /***/ }), -/* 404 */ +/* 5 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const processFn = (fn, options) => function (...args) { - const P = options.promiseModule; - - return new P((resolve, reject) => { - if (options.multiArgs) { - args.push((...result) => { - if (options.errorFirst) { - if (result[0]) { - reject(result); - } else { - result.shift(); - resolve(result); - } - } else { - resolve(result); - } - }); - } else if (options.errorFirst) { - args.push((error, result) => { - if (error) { - reject(error); - } else { - resolve(result); - } - }); - } else { - args.push(resolve); - } - - fn.apply(this, args); - }); -}; - -module.exports = (input, options) => { - options = Object.assign({ - exclude: [/.+(Sync|Stream)$/], - errorFirst: true, - promiseModule: Promise - }, options); - - const objType = typeof input; - if (!(input !== null && (objType === 'object' || objType === 'function'))) { - throw new TypeError(`Expected \`input\` to be a \`Function\` or \`Object\`, got \`${input === null ? 'null' : objType}\``); - } - - const filter = key => { - const match = pattern => typeof pattern === 'string' ? key === pattern : pattern.test(key); - return options.include ? options.include.some(match) : !options.exclude.some(match); - }; - - let ret; - if (objType === 'function') { - ret = function (...args) { - return options.excludeMain ? input(...args) : processFn(input, options).apply(this, args); - }; - } else { - ret = Object.create(Object.getPrototypeOf(input)); - } +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.getFirstSuitableFolder = exports.readFirstAvailableStream = exports.makeTempDir = exports.hardlinksWork = exports.writeFilePreservingEol = exports.getFileSizeOnDisk = exports.walk = exports.symlink = exports.find = exports.readJsonAndFile = exports.readJson = exports.readFileAny = exports.hardlinkBulk = exports.copyBulk = exports.unlink = exports.glob = exports.link = exports.chmod = exports.lstat = exports.exists = exports.mkdirp = exports.stat = exports.access = exports.rename = exports.readdir = exports.realpath = exports.readlink = exports.writeFile = exports.open = exports.readFileBuffer = exports.lockQueue = exports.constants = undefined; - for (const key in input) { // eslint-disable-line guard-for-in - const property = input[key]; - ret[key] = typeof property === 'function' && filter(key) ? processFn(property, options) : property; - } +var _asyncToGenerator2; - return ret; -}; +function _load_asyncToGenerator() { + return _asyncToGenerator2 = _interopRequireDefault(__webpack_require__(1)); +} +let buildActionsForCopy = (() => { + var _ref = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (queue, events, possibleExtraneous, reporter) { -/***/ }), -/* 405 */ -/***/ (function(module, exports) { + // + let build = (() => { + var _ref5 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (data) { + const src = data.src, + dest = data.dest, + type = data.type; -exports = module.exports = SemVer + const onFresh = data.onFresh || noop; + const onDone = data.onDone || noop; -var debug -/* istanbul ignore next */ -if (typeof process === 'object' && - process.env && - process.env.NODE_DEBUG && - /\bsemver\b/i.test(process.env.NODE_DEBUG)) { - debug = function () { - var args = Array.prototype.slice.call(arguments, 0) - args.unshift('SEMVER') - console.log.apply(console, args) - } -} else { - debug = function () {} -} + // TODO https://github.com/yarnpkg/yarn/issues/3751 + // related to bundled dependencies handling + if (files.has(dest.toLowerCase())) { + reporter.verbose(`The case-insensitive file ${dest} shouldn't be copied twice in one bulk copy`); + } else { + files.add(dest.toLowerCase()); + } -// Note: this is the semver.org version of the spec that it implements -// Not necessarily the package version of this code. -exports.SEMVER_SPEC_VERSION = '2.0.0' + if (type === 'symlink') { + yield mkdirp((_path || _load_path()).default.dirname(dest)); + onFresh(); + actions.symlink.push({ + dest, + linkname: src + }); + onDone(); + return; + } -var MAX_LENGTH = 256 -var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || - /* istanbul ignore next */ 9007199254740991 + if (events.ignoreBasenames.indexOf((_path || _load_path()).default.basename(src)) >= 0) { + // ignored file + return; + } -// Max safe segment length for coercion. -var MAX_SAFE_COMPONENT_LENGTH = 16 + const srcStat = yield lstat(src); + let srcFiles; -// The actual regexps go on exports.re -var re = exports.re = [] -var src = exports.src = [] -var R = 0 + if (srcStat.isDirectory()) { + srcFiles = yield readdir(src); + } -// The following Regular Expressions can be used for tokenizing, -// validating, and parsing SemVer version strings. + let destStat; + try { + // try accessing the destination + destStat = yield lstat(dest); + } catch (e) { + // proceed if destination doesn't exist, otherwise error + if (e.code !== 'ENOENT') { + throw e; + } + } -// ## Numeric Identifier -// A single `0`, or a non-zero digit followed by zero or more digits. + // if destination exists + if (destStat) { + const bothSymlinks = srcStat.isSymbolicLink() && destStat.isSymbolicLink(); + const bothFolders = srcStat.isDirectory() && destStat.isDirectory(); + const bothFiles = srcStat.isFile() && destStat.isFile(); -var NUMERICIDENTIFIER = R++ -src[NUMERICIDENTIFIER] = '0|[1-9]\\d*' -var NUMERICIDENTIFIERLOOSE = R++ -src[NUMERICIDENTIFIERLOOSE] = '[0-9]+' + // EINVAL access errors sometimes happen which shouldn't because node shouldn't be giving + // us modes that aren't valid. investigate this, it's generally safe to proceed. -// ## Non-numeric Identifier -// Zero or more digits, followed by a letter or hyphen, and then zero or -// more letters, digits, or hyphens. + /* if (srcStat.mode !== destStat.mode) { + try { + await access(dest, srcStat.mode); + } catch (err) {} + } */ -var NONNUMERICIDENTIFIER = R++ -src[NONNUMERICIDENTIFIER] = '\\d*[a-zA-Z-][a-zA-Z0-9-]*' + if (bothFiles && artifactFiles.has(dest)) { + // this file gets changed during build, likely by a custom install script. Don't bother checking it. + onDone(); + reporter.verbose(reporter.lang('verboseFileSkipArtifact', src)); + return; + } -// ## Main Version -// Three dot-separated numeric identifiers. + if (bothFiles && srcStat.size === destStat.size && (0, (_fsNormalized || _load_fsNormalized()).fileDatesEqual)(srcStat.mtime, destStat.mtime)) { + // we can safely assume this is the same file + onDone(); + reporter.verbose(reporter.lang('verboseFileSkip', src, dest, srcStat.size, +srcStat.mtime)); + return; + } -var MAINVERSION = R++ -src[MAINVERSION] = '(' + src[NUMERICIDENTIFIER] + ')\\.' + - '(' + src[NUMERICIDENTIFIER] + ')\\.' + - '(' + src[NUMERICIDENTIFIER] + ')' + if (bothSymlinks) { + const srcReallink = yield readlink(src); + if (srcReallink === (yield readlink(dest))) { + // if both symlinks are the same then we can continue on + onDone(); + reporter.verbose(reporter.lang('verboseFileSkipSymlink', src, dest, srcReallink)); + return; + } + } -var MAINVERSIONLOOSE = R++ -src[MAINVERSIONLOOSE] = '(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' + - '(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' + - '(' + src[NUMERICIDENTIFIERLOOSE] + ')' + if (bothFolders) { + // mark files that aren't in this folder as possibly extraneous + const destFiles = yield readdir(dest); + invariant(srcFiles, 'src files not initialised'); -// ## Pre-release Version Identifier -// A numeric identifier, or a non-numeric identifier. + for (var _iterator4 = destFiles, _isArray4 = Array.isArray(_iterator4), _i4 = 0, _iterator4 = _isArray4 ? _iterator4 : _iterator4[Symbol.iterator]();;) { + var _ref6; -var PRERELEASEIDENTIFIER = R++ -src[PRERELEASEIDENTIFIER] = '(?:' + src[NUMERICIDENTIFIER] + - '|' + src[NONNUMERICIDENTIFIER] + ')' + if (_isArray4) { + if (_i4 >= _iterator4.length) break; + _ref6 = _iterator4[_i4++]; + } else { + _i4 = _iterator4.next(); + if (_i4.done) break; + _ref6 = _i4.value; + } -var PRERELEASEIDENTIFIERLOOSE = R++ -src[PRERELEASEIDENTIFIERLOOSE] = '(?:' + src[NUMERICIDENTIFIERLOOSE] + - '|' + src[NONNUMERICIDENTIFIER] + ')' + const file = _ref6; -// ## Pre-release Version -// Hyphen, followed by one or more dot-separated pre-release version -// identifiers. + if (srcFiles.indexOf(file) < 0) { + const loc = (_path || _load_path()).default.join(dest, file); + possibleExtraneous.add(loc); -var PRERELEASE = R++ -src[PRERELEASE] = '(?:-(' + src[PRERELEASEIDENTIFIER] + - '(?:\\.' + src[PRERELEASEIDENTIFIER] + ')*))' + if ((yield lstat(loc)).isDirectory()) { + for (var _iterator5 = yield readdir(loc), _isArray5 = Array.isArray(_iterator5), _i5 = 0, _iterator5 = _isArray5 ? _iterator5 : _iterator5[Symbol.iterator]();;) { + var _ref7; -var PRERELEASELOOSE = R++ -src[PRERELEASELOOSE] = '(?:-?(' + src[PRERELEASEIDENTIFIERLOOSE] + - '(?:\\.' + src[PRERELEASEIDENTIFIERLOOSE] + ')*))' + if (_isArray5) { + if (_i5 >= _iterator5.length) break; + _ref7 = _iterator5[_i5++]; + } else { + _i5 = _iterator5.next(); + if (_i5.done) break; + _ref7 = _i5.value; + } -// ## Build Metadata Identifier -// Any combination of digits, letters, or hyphens. + const file = _ref7; -var BUILDIDENTIFIER = R++ -src[BUILDIDENTIFIER] = '[0-9A-Za-z-]+' + possibleExtraneous.add((_path || _load_path()).default.join(loc, file)); + } + } + } + } + } + } -// ## Build Metadata -// Plus sign, followed by one or more period-separated build metadata -// identifiers. + if (destStat && destStat.isSymbolicLink()) { + yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(dest); + destStat = null; + } -var BUILD = R++ -src[BUILD] = '(?:\\+(' + src[BUILDIDENTIFIER] + - '(?:\\.' + src[BUILDIDENTIFIER] + ')*))' + if (srcStat.isSymbolicLink()) { + onFresh(); + const linkname = yield readlink(src); + actions.symlink.push({ + dest, + linkname + }); + onDone(); + } else if (srcStat.isDirectory()) { + if (!destStat) { + reporter.verbose(reporter.lang('verboseFileFolder', dest)); + yield mkdirp(dest); + } -// ## Full Version String -// A main version, followed optionally by a pre-release version and -// build metadata. + const destParts = dest.split((_path || _load_path()).default.sep); + while (destParts.length) { + files.add(destParts.join((_path || _load_path()).default.sep).toLowerCase()); + destParts.pop(); + } -// Note that the only major, minor, patch, and pre-release sections of -// the version string are capturing groups. The build metadata is not a -// capturing group, because it should not ever be used in version -// comparison. + // push all files to queue + invariant(srcFiles, 'src files not initialised'); + let remaining = srcFiles.length; + if (!remaining) { + onDone(); + } + for (var _iterator6 = srcFiles, _isArray6 = Array.isArray(_iterator6), _i6 = 0, _iterator6 = _isArray6 ? _iterator6 : _iterator6[Symbol.iterator]();;) { + var _ref8; -var FULL = R++ -var FULLPLAIN = 'v?' + src[MAINVERSION] + - src[PRERELEASE] + '?' + - src[BUILD] + '?' + if (_isArray6) { + if (_i6 >= _iterator6.length) break; + _ref8 = _iterator6[_i6++]; + } else { + _i6 = _iterator6.next(); + if (_i6.done) break; + _ref8 = _i6.value; + } -src[FULL] = '^' + FULLPLAIN + '$' + const file = _ref8; -// like full, but allows v1.2.3 and =1.2.3, which people do sometimes. -// also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty -// common in the npm registry. -var LOOSEPLAIN = '[v=\\s]*' + src[MAINVERSIONLOOSE] + - src[PRERELEASELOOSE] + '?' + - src[BUILD] + '?' + queue.push({ + dest: (_path || _load_path()).default.join(dest, file), + onFresh, + onDone: function (_onDone) { + function onDone() { + return _onDone.apply(this, arguments); + } -var LOOSE = R++ -src[LOOSE] = '^' + LOOSEPLAIN + '$' + onDone.toString = function () { + return _onDone.toString(); + }; -var GTLT = R++ -src[GTLT] = '((?:<|>)?=?)' + return onDone; + }(function () { + if (--remaining === 0) { + onDone(); + } + }), + src: (_path || _load_path()).default.join(src, file) + }); + } + } else if (srcStat.isFile()) { + onFresh(); + actions.file.push({ + src, + dest, + atime: srcStat.atime, + mtime: srcStat.mtime, + mode: srcStat.mode + }); + onDone(); + } else { + throw new Error(`unsure how to copy this: ${src}`); + } + }); -// Something like "2.*" or "1.2.x". -// Note that "x.x" is a valid xRange identifer, meaning "any version" -// Only the first item is strictly required. -var XRANGEIDENTIFIERLOOSE = R++ -src[XRANGEIDENTIFIERLOOSE] = src[NUMERICIDENTIFIERLOOSE] + '|x|X|\\*' -var XRANGEIDENTIFIER = R++ -src[XRANGEIDENTIFIER] = src[NUMERICIDENTIFIER] + '|x|X|\\*' + return function build(_x5) { + return _ref5.apply(this, arguments); + }; + })(); -var XRANGEPLAIN = R++ -src[XRANGEPLAIN] = '[v=\\s]*(' + src[XRANGEIDENTIFIER] + ')' + - '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' + - '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' + - '(?:' + src[PRERELEASE] + ')?' + - src[BUILD] + '?' + - ')?)?' + const artifactFiles = new Set(events.artifactFiles || []); + const files = new Set(); -var XRANGEPLAINLOOSE = R++ -src[XRANGEPLAINLOOSE] = '[v=\\s]*(' + src[XRANGEIDENTIFIERLOOSE] + ')' + - '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' + - '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' + - '(?:' + src[PRERELEASELOOSE] + ')?' + - src[BUILD] + '?' + - ')?)?' + // initialise events + for (var _iterator = queue, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { + var _ref2; -var XRANGE = R++ -src[XRANGE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAIN] + '$' -var XRANGELOOSE = R++ -src[XRANGELOOSE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAINLOOSE] + '$' + if (_isArray) { + if (_i >= _iterator.length) break; + _ref2 = _iterator[_i++]; + } else { + _i = _iterator.next(); + if (_i.done) break; + _ref2 = _i.value; + } -// Coercion. -// Extract anything that could conceivably be a part of a valid semver -var COERCE = R++ -src[COERCE] = '(?:^|[^\\d])' + - '(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '})' + - '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + - '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + - '(?:$|[^\\d])' + const item = _ref2; -// Tilde ranges. -// Meaning is "reasonably at or greater than" -var LONETILDE = R++ -src[LONETILDE] = '(?:~>?)' + const onDone = item.onDone; + item.onDone = function () { + events.onProgress(item.dest); + if (onDone) { + onDone(); + } + }; + } + events.onStart(queue.length); -var TILDETRIM = R++ -src[TILDETRIM] = '(\\s*)' + src[LONETILDE] + '\\s+' -re[TILDETRIM] = new RegExp(src[TILDETRIM], 'g') -var tildeTrimReplace = '$1~' + // start building actions + const actions = { + file: [], + symlink: [], + link: [] + }; -var TILDE = R++ -src[TILDE] = '^' + src[LONETILDE] + src[XRANGEPLAIN] + '$' -var TILDELOOSE = R++ -src[TILDELOOSE] = '^' + src[LONETILDE] + src[XRANGEPLAINLOOSE] + '$' + // custom concurrency logic as we're always executing stacks of CONCURRENT_QUEUE_ITEMS queue items + // at a time due to the requirement to push items onto the queue + while (queue.length) { + const items = queue.splice(0, CONCURRENT_QUEUE_ITEMS); + yield Promise.all(items.map(build)); + } -// Caret ranges. -// Meaning is "at least and backwards compatible with" -var LONECARET = R++ -src[LONECARET] = '(?:\\^)' + // simulate the existence of some files to prevent considering them extraneous + for (var _iterator2 = artifactFiles, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) { + var _ref3; -var CARETTRIM = R++ -src[CARETTRIM] = '(\\s*)' + src[LONECARET] + '\\s+' -re[CARETTRIM] = new RegExp(src[CARETTRIM], 'g') -var caretTrimReplace = '$1^' + if (_isArray2) { + if (_i2 >= _iterator2.length) break; + _ref3 = _iterator2[_i2++]; + } else { + _i2 = _iterator2.next(); + if (_i2.done) break; + _ref3 = _i2.value; + } -var CARET = R++ -src[CARET] = '^' + src[LONECARET] + src[XRANGEPLAIN] + '$' -var CARETLOOSE = R++ -src[CARETLOOSE] = '^' + src[LONECARET] + src[XRANGEPLAINLOOSE] + '$' + const file = _ref3; -// A simple gt/lt/eq thing, or just "" to indicate "any version" -var COMPARATORLOOSE = R++ -src[COMPARATORLOOSE] = '^' + src[GTLT] + '\\s*(' + LOOSEPLAIN + ')$|^$' -var COMPARATOR = R++ -src[COMPARATOR] = '^' + src[GTLT] + '\\s*(' + FULLPLAIN + ')$|^$' + if (possibleExtraneous.has(file)) { + reporter.verbose(reporter.lang('verboseFilePhantomExtraneous', file)); + possibleExtraneous.delete(file); + } + } -// An expression to strip any whitespace between the gtlt and the thing -// it modifies, so that `> 1.2.3` ==> `>1.2.3` -var COMPARATORTRIM = R++ -src[COMPARATORTRIM] = '(\\s*)' + src[GTLT] + - '\\s*(' + LOOSEPLAIN + '|' + src[XRANGEPLAIN] + ')' + for (var _iterator3 = possibleExtraneous, _isArray3 = Array.isArray(_iterator3), _i3 = 0, _iterator3 = _isArray3 ? _iterator3 : _iterator3[Symbol.iterator]();;) { + var _ref4; -// this one has to use the /g flag -re[COMPARATORTRIM] = new RegExp(src[COMPARATORTRIM], 'g') -var comparatorTrimReplace = '$1$2$3' + if (_isArray3) { + if (_i3 >= _iterator3.length) break; + _ref4 = _iterator3[_i3++]; + } else { + _i3 = _iterator3.next(); + if (_i3.done) break; + _ref4 = _i3.value; + } -// Something like `1.2.3 - 1.2.4` -// Note that these all use the loose form, because they'll be -// checked against either the strict or loose comparator form -// later. -var HYPHENRANGE = R++ -src[HYPHENRANGE] = '^\\s*(' + src[XRANGEPLAIN] + ')' + - '\\s+-\\s+' + - '(' + src[XRANGEPLAIN] + ')' + - '\\s*$' + const loc = _ref4; -var HYPHENRANGELOOSE = R++ -src[HYPHENRANGELOOSE] = '^\\s*(' + src[XRANGEPLAINLOOSE] + ')' + - '\\s+-\\s+' + - '(' + src[XRANGEPLAINLOOSE] + ')' + - '\\s*$' + if (files.has(loc.toLowerCase())) { + possibleExtraneous.delete(loc); + } + } -// Star ranges basically just allow anything at all. -var STAR = R++ -src[STAR] = '(<|>)?=?\\s*\\*' + return actions; + }); -// Compile to actual regexp objects. -// All are flag-free, unless they were created above with a flag. -for (var i = 0; i < R; i++) { - debug(i, src[i]) - if (!re[i]) { - re[i] = new RegExp(src[i]) - } -} + return function buildActionsForCopy(_x, _x2, _x3, _x4) { + return _ref.apply(this, arguments); + }; +})(); -exports.parse = parse -function parse (version, options) { - if (!options || typeof options !== 'object') { - options = { - loose: !!options, - includePrerelease: false - } - } +let buildActionsForHardlink = (() => { + var _ref9 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (queue, events, possibleExtraneous, reporter) { - if (version instanceof SemVer) { - return version - } + // + let build = (() => { + var _ref13 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (data) { + const src = data.src, + dest = data.dest; - if (typeof version !== 'string') { - return null - } + const onFresh = data.onFresh || noop; + const onDone = data.onDone || noop; + if (files.has(dest.toLowerCase())) { + // Fixes issue https://github.com/yarnpkg/yarn/issues/2734 + // When bulk hardlinking we have A -> B structure that we want to hardlink to A1 -> B1, + // package-linker passes that modules A1 and B1 need to be hardlinked, + // the recursive linking algorithm of A1 ends up scheduling files in B1 to be linked twice which will case + // an exception. + onDone(); + return; + } + files.add(dest.toLowerCase()); - if (version.length > MAX_LENGTH) { - return null - } + if (events.ignoreBasenames.indexOf((_path || _load_path()).default.basename(src)) >= 0) { + // ignored file + return; + } - var r = options.loose ? re[LOOSE] : re[FULL] - if (!r.test(version)) { - return null - } + const srcStat = yield lstat(src); + let srcFiles; - try { - return new SemVer(version, options) - } catch (er) { - return null - } -} + if (srcStat.isDirectory()) { + srcFiles = yield readdir(src); + } -exports.valid = valid -function valid (version, options) { - var v = parse(version, options) - return v ? v.version : null -} + const destExists = yield exists(dest); + if (destExists) { + const destStat = yield lstat(dest); -exports.clean = clean -function clean (version, options) { - var s = parse(version.trim().replace(/^[=v]+/, ''), options) - return s ? s.version : null -} + const bothSymlinks = srcStat.isSymbolicLink() && destStat.isSymbolicLink(); + const bothFolders = srcStat.isDirectory() && destStat.isDirectory(); + const bothFiles = srcStat.isFile() && destStat.isFile(); -exports.SemVer = SemVer + if (srcStat.mode !== destStat.mode) { + try { + yield access(dest, srcStat.mode); + } catch (err) { + // EINVAL access errors sometimes happen which shouldn't because node shouldn't be giving + // us modes that aren't valid. investigate this, it's generally safe to proceed. + reporter.verbose(err); + } + } -function SemVer (version, options) { - if (!options || typeof options !== 'object') { - options = { - loose: !!options, - includePrerelease: false - } - } - if (version instanceof SemVer) { - if (version.loose === options.loose) { - return version - } else { - version = version.version - } - } else if (typeof version !== 'string') { - throw new TypeError('Invalid Version: ' + version) - } + if (bothFiles && artifactFiles.has(dest)) { + // this file gets changed during build, likely by a custom install script. Don't bother checking it. + onDone(); + reporter.verbose(reporter.lang('verboseFileSkipArtifact', src)); + return; + } - if (version.length > MAX_LENGTH) { - throw new TypeError('version is longer than ' + MAX_LENGTH + ' characters') - } + // correct hardlink + if (bothFiles && srcStat.ino !== null && srcStat.ino === destStat.ino) { + onDone(); + reporter.verbose(reporter.lang('verboseFileSkip', src, dest, srcStat.ino)); + return; + } - if (!(this instanceof SemVer)) { - return new SemVer(version, options) - } + if (bothSymlinks) { + const srcReallink = yield readlink(src); + if (srcReallink === (yield readlink(dest))) { + // if both symlinks are the same then we can continue on + onDone(); + reporter.verbose(reporter.lang('verboseFileSkipSymlink', src, dest, srcReallink)); + return; + } + } - debug('SemVer', version, options) - this.options = options - this.loose = !!options.loose + if (bothFolders) { + // mark files that aren't in this folder as possibly extraneous + const destFiles = yield readdir(dest); + invariant(srcFiles, 'src files not initialised'); - var m = version.trim().match(options.loose ? re[LOOSE] : re[FULL]) + for (var _iterator10 = destFiles, _isArray10 = Array.isArray(_iterator10), _i10 = 0, _iterator10 = _isArray10 ? _iterator10 : _iterator10[Symbol.iterator]();;) { + var _ref14; - if (!m) { - throw new TypeError('Invalid Version: ' + version) - } + if (_isArray10) { + if (_i10 >= _iterator10.length) break; + _ref14 = _iterator10[_i10++]; + } else { + _i10 = _iterator10.next(); + if (_i10.done) break; + _ref14 = _i10.value; + } - this.raw = version + const file = _ref14; - // these are actually numbers - this.major = +m[1] - this.minor = +m[2] - this.patch = +m[3] + if (srcFiles.indexOf(file) < 0) { + const loc = (_path || _load_path()).default.join(dest, file); + possibleExtraneous.add(loc); - if (this.major > MAX_SAFE_INTEGER || this.major < 0) { - throw new TypeError('Invalid major version') - } + if ((yield lstat(loc)).isDirectory()) { + for (var _iterator11 = yield readdir(loc), _isArray11 = Array.isArray(_iterator11), _i11 = 0, _iterator11 = _isArray11 ? _iterator11 : _iterator11[Symbol.iterator]();;) { + var _ref15; - if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) { - throw new TypeError('Invalid minor version') - } + if (_isArray11) { + if (_i11 >= _iterator11.length) break; + _ref15 = _iterator11[_i11++]; + } else { + _i11 = _iterator11.next(); + if (_i11.done) break; + _ref15 = _i11.value; + } - if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) { - throw new TypeError('Invalid patch version') - } + const file = _ref15; - // numberify any prerelease numeric ids - if (!m[4]) { - this.prerelease = [] - } else { - this.prerelease = m[4].split('.').map(function (id) { - if (/^[0-9]+$/.test(id)) { - var num = +id - if (num >= 0 && num < MAX_SAFE_INTEGER) { - return num + possibleExtraneous.add((_path || _load_path()).default.join(loc, file)); + } + } + } + } + } } - } - return id - }) - } - this.build = m[5] ? m[5].split('.') : [] - this.format() -} + if (srcStat.isSymbolicLink()) { + onFresh(); + const linkname = yield readlink(src); + actions.symlink.push({ + dest, + linkname + }); + onDone(); + } else if (srcStat.isDirectory()) { + reporter.verbose(reporter.lang('verboseFileFolder', dest)); + yield mkdirp(dest); -SemVer.prototype.format = function () { - this.version = this.major + '.' + this.minor + '.' + this.patch - if (this.prerelease.length) { - this.version += '-' + this.prerelease.join('.') - } - return this.version -} + const destParts = dest.split((_path || _load_path()).default.sep); + while (destParts.length) { + files.add(destParts.join((_path || _load_path()).default.sep).toLowerCase()); + destParts.pop(); + } -SemVer.prototype.toString = function () { - return this.version -} + // push all files to queue + invariant(srcFiles, 'src files not initialised'); + let remaining = srcFiles.length; + if (!remaining) { + onDone(); + } + for (var _iterator12 = srcFiles, _isArray12 = Array.isArray(_iterator12), _i12 = 0, _iterator12 = _isArray12 ? _iterator12 : _iterator12[Symbol.iterator]();;) { + var _ref16; -SemVer.prototype.compare = function (other) { - debug('SemVer.compare', this.version, this.options, other) - if (!(other instanceof SemVer)) { - other = new SemVer(other, this.options) - } + if (_isArray12) { + if (_i12 >= _iterator12.length) break; + _ref16 = _iterator12[_i12++]; + } else { + _i12 = _iterator12.next(); + if (_i12.done) break; + _ref16 = _i12.value; + } - return this.compareMain(other) || this.comparePre(other) -} + const file = _ref16; -SemVer.prototype.compareMain = function (other) { - if (!(other instanceof SemVer)) { - other = new SemVer(other, this.options) - } + queue.push({ + onFresh, + src: (_path || _load_path()).default.join(src, file), + dest: (_path || _load_path()).default.join(dest, file), + onDone: function (_onDone2) { + function onDone() { + return _onDone2.apply(this, arguments); + } - return compareIdentifiers(this.major, other.major) || - compareIdentifiers(this.minor, other.minor) || - compareIdentifiers(this.patch, other.patch) -} + onDone.toString = function () { + return _onDone2.toString(); + }; -SemVer.prototype.comparePre = function (other) { - if (!(other instanceof SemVer)) { - other = new SemVer(other, this.options) - } + return onDone; + }(function () { + if (--remaining === 0) { + onDone(); + } + }) + }); + } + } else if (srcStat.isFile()) { + onFresh(); + actions.link.push({ + src, + dest, + removeDest: destExists + }); + onDone(); + } else { + throw new Error(`unsure how to copy this: ${src}`); + } + }); - // NOT having a prerelease is > having one - if (this.prerelease.length && !other.prerelease.length) { - return -1 - } else if (!this.prerelease.length && other.prerelease.length) { - return 1 - } else if (!this.prerelease.length && !other.prerelease.length) { - return 0 - } + return function build(_x10) { + return _ref13.apply(this, arguments); + }; + })(); - var i = 0 - do { - var a = this.prerelease[i] - var b = other.prerelease[i] - debug('prerelease compare', i, a, b) - if (a === undefined && b === undefined) { - return 0 - } else if (b === undefined) { - return 1 - } else if (a === undefined) { - return -1 - } else if (a === b) { - continue - } else { - return compareIdentifiers(a, b) - } - } while (++i) -} + const artifactFiles = new Set(events.artifactFiles || []); + const files = new Set(); -// preminor will bump the version up to the next minor release, and immediately -// down to pre-release. premajor and prepatch work the same way. -SemVer.prototype.inc = function (release, identifier) { - switch (release) { - case 'premajor': - this.prerelease.length = 0 - this.patch = 0 - this.minor = 0 - this.major++ - this.inc('pre', identifier) - break - case 'preminor': - this.prerelease.length = 0 - this.patch = 0 - this.minor++ - this.inc('pre', identifier) - break - case 'prepatch': - // If this is already a prerelease, it will bump to the next version - // drop any prereleases that might already exist, since they are not - // relevant at this point. - this.prerelease.length = 0 - this.inc('patch', identifier) - this.inc('pre', identifier) - break - // If the input is a non-prerelease version, this acts the same as - // prepatch. - case 'prerelease': - if (this.prerelease.length === 0) { - this.inc('patch', identifier) - } - this.inc('pre', identifier) - break + // initialise events + for (var _iterator7 = queue, _isArray7 = Array.isArray(_iterator7), _i7 = 0, _iterator7 = _isArray7 ? _iterator7 : _iterator7[Symbol.iterator]();;) { + var _ref10; - case 'major': - // If this is a pre-major version, bump up to the same major version. - // Otherwise increment major. - // 1.0.0-5 bumps to 1.0.0 - // 1.1.0 bumps to 2.0.0 - if (this.minor !== 0 || - this.patch !== 0 || - this.prerelease.length === 0) { - this.major++ - } - this.minor = 0 - this.patch = 0 - this.prerelease = [] - break - case 'minor': - // If this is a pre-minor version, bump up to the same minor version. - // Otherwise increment minor. - // 1.2.0-5 bumps to 1.2.0 - // 1.2.1 bumps to 1.3.0 - if (this.patch !== 0 || this.prerelease.length === 0) { - this.minor++ - } - this.patch = 0 - this.prerelease = [] - break - case 'patch': - // If this is not a pre-release version, it will increment the patch. - // If it is a pre-release it will bump up to the same patch version. - // 1.2.0-5 patches to 1.2.0 - // 1.2.0 patches to 1.2.1 - if (this.prerelease.length === 0) { - this.patch++ - } - this.prerelease = [] - break - // This probably shouldn't be used publicly. - // 1.0.0 "pre" would become 1.0.0-0 which is the wrong direction. - case 'pre': - if (this.prerelease.length === 0) { - this.prerelease = [0] + if (_isArray7) { + if (_i7 >= _iterator7.length) break; + _ref10 = _iterator7[_i7++]; } else { - var i = this.prerelease.length - while (--i >= 0) { - if (typeof this.prerelease[i] === 'number') { - this.prerelease[i]++ - i = -2 - } - } - if (i === -1) { - // didn't increment anything - this.prerelease.push(0) - } - } - if (identifier) { - // 1.2.0-beta.1 bumps to 1.2.0-beta.2, - // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0 - if (this.prerelease[0] === identifier) { - if (isNaN(this.prerelease[1])) { - this.prerelease = [identifier, 0] - } - } else { - this.prerelease = [identifier, 0] - } + _i7 = _iterator7.next(); + if (_i7.done) break; + _ref10 = _i7.value; } - break - default: - throw new Error('invalid increment argument: ' + release) - } - this.format() - this.raw = this.version - return this -} + const item = _ref10; -exports.inc = inc -function inc (version, release, loose, identifier) { - if (typeof (loose) === 'string') { - identifier = loose - loose = undefined - } + const onDone = item.onDone || noop; + item.onDone = function () { + events.onProgress(item.dest); + onDone(); + }; + } + events.onStart(queue.length); - try { - return new SemVer(version, loose).inc(release, identifier).version - } catch (er) { - return null - } -} + // start building actions + const actions = { + file: [], + symlink: [], + link: [] + }; -exports.diff = diff -function diff (version1, version2) { - if (eq(version1, version2)) { - return null - } else { - var v1 = parse(version1) - var v2 = parse(version2) - var prefix = '' - if (v1.prerelease.length || v2.prerelease.length) { - prefix = 'pre' - var defaultResult = 'prerelease' - } - for (var key in v1) { - if (key === 'major' || key === 'minor' || key === 'patch') { - if (v1[key] !== v2[key]) { - return prefix + key - } - } + // custom concurrency logic as we're always executing stacks of CONCURRENT_QUEUE_ITEMS queue items + // at a time due to the requirement to push items onto the queue + while (queue.length) { + const items = queue.splice(0, CONCURRENT_QUEUE_ITEMS); + yield Promise.all(items.map(build)); } - return defaultResult // may be undefined - } -} -exports.compareIdentifiers = compareIdentifiers + // simulate the existence of some files to prevent considering them extraneous + for (var _iterator8 = artifactFiles, _isArray8 = Array.isArray(_iterator8), _i8 = 0, _iterator8 = _isArray8 ? _iterator8 : _iterator8[Symbol.iterator]();;) { + var _ref11; -var numeric = /^[0-9]+$/ -function compareIdentifiers (a, b) { - var anum = numeric.test(a) - var bnum = numeric.test(b) + if (_isArray8) { + if (_i8 >= _iterator8.length) break; + _ref11 = _iterator8[_i8++]; + } else { + _i8 = _iterator8.next(); + if (_i8.done) break; + _ref11 = _i8.value; + } - if (anum && bnum) { - a = +a - b = +b - } + const file = _ref11; - return a === b ? 0 - : (anum && !bnum) ? -1 - : (bnum && !anum) ? 1 - : a < b ? -1 - : 1 -} + if (possibleExtraneous.has(file)) { + reporter.verbose(reporter.lang('verboseFilePhantomExtraneous', file)); + possibleExtraneous.delete(file); + } + } -exports.rcompareIdentifiers = rcompareIdentifiers -function rcompareIdentifiers (a, b) { - return compareIdentifiers(b, a) -} + for (var _iterator9 = possibleExtraneous, _isArray9 = Array.isArray(_iterator9), _i9 = 0, _iterator9 = _isArray9 ? _iterator9 : _iterator9[Symbol.iterator]();;) { + var _ref12; -exports.major = major -function major (a, loose) { - return new SemVer(a, loose).major -} + if (_isArray9) { + if (_i9 >= _iterator9.length) break; + _ref12 = _iterator9[_i9++]; + } else { + _i9 = _iterator9.next(); + if (_i9.done) break; + _ref12 = _i9.value; + } -exports.minor = minor -function minor (a, loose) { - return new SemVer(a, loose).minor -} + const loc = _ref12; -exports.patch = patch -function patch (a, loose) { - return new SemVer(a, loose).patch -} + if (files.has(loc.toLowerCase())) { + possibleExtraneous.delete(loc); + } + } -exports.compare = compare -function compare (a, b, loose) { - return new SemVer(a, loose).compare(new SemVer(b, loose)) -} + return actions; + }); -exports.compareLoose = compareLoose -function compareLoose (a, b) { - return compare(a, b, true) -} + return function buildActionsForHardlink(_x6, _x7, _x8, _x9) { + return _ref9.apply(this, arguments); + }; +})(); -exports.rcompare = rcompare -function rcompare (a, b, loose) { - return compare(b, a, loose) -} +let copyBulk = exports.copyBulk = (() => { + var _ref17 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (queue, reporter, _events) { + const events = { + onStart: _events && _events.onStart || noop, + onProgress: _events && _events.onProgress || noop, + possibleExtraneous: _events ? _events.possibleExtraneous : new Set(), + ignoreBasenames: _events && _events.ignoreBasenames || [], + artifactFiles: _events && _events.artifactFiles || [] + }; -exports.sort = sort -function sort (list, loose) { - return list.sort(function (a, b) { - return exports.compare(a, b, loose) - }) -} + const actions = yield buildActionsForCopy(queue, events, events.possibleExtraneous, reporter); + events.onStart(actions.file.length + actions.symlink.length + actions.link.length); -exports.rsort = rsort -function rsort (list, loose) { - return list.sort(function (a, b) { - return exports.rcompare(a, b, loose) - }) -} + const fileActions = actions.file; -exports.gt = gt -function gt (a, b, loose) { - return compare(a, b, loose) > 0 -} + const currentlyWriting = new Map(); -exports.lt = lt -function lt (a, b, loose) { - return compare(a, b, loose) < 0 -} + yield (_promise || _load_promise()).queue(fileActions, (() => { + var _ref18 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (data) { + let writePromise; + while (writePromise = currentlyWriting.get(data.dest)) { + yield writePromise; + } -exports.eq = eq -function eq (a, b, loose) { - return compare(a, b, loose) === 0 -} + reporter.verbose(reporter.lang('verboseFileCopy', data.src, data.dest)); + const copier = (0, (_fsNormalized || _load_fsNormalized()).copyFile)(data, function () { + return currentlyWriting.delete(data.dest); + }); + currentlyWriting.set(data.dest, copier); + events.onProgress(data.dest); + return copier; + }); -exports.neq = neq -function neq (a, b, loose) { - return compare(a, b, loose) !== 0 -} + return function (_x14) { + return _ref18.apply(this, arguments); + }; + })(), CONCURRENT_QUEUE_ITEMS); -exports.gte = gte -function gte (a, b, loose) { - return compare(a, b, loose) >= 0 -} + // we need to copy symlinks last as they could reference files we were copying + const symlinkActions = actions.symlink; + yield (_promise || _load_promise()).queue(symlinkActions, function (data) { + const linkname = (_path || _load_path()).default.resolve((_path || _load_path()).default.dirname(data.dest), data.linkname); + reporter.verbose(reporter.lang('verboseFileSymlink', data.dest, linkname)); + return symlink(linkname, data.dest); + }); + }); -exports.lte = lte -function lte (a, b, loose) { - return compare(a, b, loose) <= 0 -} + return function copyBulk(_x11, _x12, _x13) { + return _ref17.apply(this, arguments); + }; +})(); -exports.cmp = cmp -function cmp (a, op, b, loose) { - switch (op) { - case '===': - if (typeof a === 'object') - a = a.version - if (typeof b === 'object') - b = b.version - return a === b +let hardlinkBulk = exports.hardlinkBulk = (() => { + var _ref19 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (queue, reporter, _events) { + const events = { + onStart: _events && _events.onStart || noop, + onProgress: _events && _events.onProgress || noop, + possibleExtraneous: _events ? _events.possibleExtraneous : new Set(), + artifactFiles: _events && _events.artifactFiles || [], + ignoreBasenames: [] + }; - case '!==': - if (typeof a === 'object') - a = a.version - if (typeof b === 'object') - b = b.version - return a !== b + const actions = yield buildActionsForHardlink(queue, events, events.possibleExtraneous, reporter); + events.onStart(actions.file.length + actions.symlink.length + actions.link.length); - case '': - case '=': - case '==': - return eq(a, b, loose) + const fileActions = actions.link; - case '!=': - return neq(a, b, loose) + yield (_promise || _load_promise()).queue(fileActions, (() => { + var _ref20 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (data) { + reporter.verbose(reporter.lang('verboseFileLink', data.src, data.dest)); + if (data.removeDest) { + yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(data.dest); + } + yield link(data.src, data.dest); + }); - case '>': - return gt(a, b, loose) + return function (_x18) { + return _ref20.apply(this, arguments); + }; + })(), CONCURRENT_QUEUE_ITEMS); - case '>=': - return gte(a, b, loose) + // we need to copy symlinks last as they could reference files we were copying + const symlinkActions = actions.symlink; + yield (_promise || _load_promise()).queue(symlinkActions, function (data) { + const linkname = (_path || _load_path()).default.resolve((_path || _load_path()).default.dirname(data.dest), data.linkname); + reporter.verbose(reporter.lang('verboseFileSymlink', data.dest, linkname)); + return symlink(linkname, data.dest); + }); + }); - case '<': - return lt(a, b, loose) + return function hardlinkBulk(_x15, _x16, _x17) { + return _ref19.apply(this, arguments); + }; +})(); - case '<=': - return lte(a, b, loose) +let readFileAny = exports.readFileAny = (() => { + var _ref21 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (files) { + for (var _iterator13 = files, _isArray13 = Array.isArray(_iterator13), _i13 = 0, _iterator13 = _isArray13 ? _iterator13 : _iterator13[Symbol.iterator]();;) { + var _ref22; - default: - throw new TypeError('Invalid operator: ' + op) - } -} + if (_isArray13) { + if (_i13 >= _iterator13.length) break; + _ref22 = _iterator13[_i13++]; + } else { + _i13 = _iterator13.next(); + if (_i13.done) break; + _ref22 = _i13.value; + } -exports.Comparator = Comparator -function Comparator (comp, options) { - if (!options || typeof options !== 'object') { - options = { - loose: !!options, - includePrerelease: false - } - } + const file = _ref22; - if (comp instanceof Comparator) { - if (comp.loose === !!options.loose) { - return comp - } else { - comp = comp.value + if (yield exists(file)) { + return readFile(file); + } } - } + return null; + }); - if (!(this instanceof Comparator)) { - return new Comparator(comp, options) - } + return function readFileAny(_x19) { + return _ref21.apply(this, arguments); + }; +})(); - debug('comparator', comp, options) - this.options = options - this.loose = !!options.loose - this.parse(comp) +let readJson = exports.readJson = (() => { + var _ref23 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (loc) { + return (yield readJsonAndFile(loc)).object; + }); - if (this.semver === ANY) { - this.value = '' - } else { - this.value = this.operator + this.semver.version - } + return function readJson(_x20) { + return _ref23.apply(this, arguments); + }; +})(); - debug('comp', this) -} +let readJsonAndFile = exports.readJsonAndFile = (() => { + var _ref24 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (loc) { + const file = yield readFile(loc); + try { + return { + object: (0, (_map || _load_map()).default)(JSON.parse(stripBOM(file))), + content: file + }; + } catch (err) { + err.message = `${loc}: ${err.message}`; + throw err; + } + }); -var ANY = {} -Comparator.prototype.parse = function (comp) { - var r = this.options.loose ? re[COMPARATORLOOSE] : re[COMPARATOR] - var m = comp.match(r) + return function readJsonAndFile(_x21) { + return _ref24.apply(this, arguments); + }; +})(); - if (!m) { - throw new TypeError('Invalid comparator: ' + comp) - } +let find = exports.find = (() => { + var _ref25 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (filename, dir) { + const parts = dir.split((_path || _load_path()).default.sep); - this.operator = m[1] - if (this.operator === '=') { - this.operator = '' - } + while (parts.length) { + const loc = parts.concat(filename).join((_path || _load_path()).default.sep); - // if it literally is just '>' or '' then allow anything. - if (!m[2]) { - this.semver = ANY - } else { - this.semver = new SemVer(m[2], this.options.loose) - } -} + if (yield exists(loc)) { + return loc; + } else { + parts.pop(); + } + } -Comparator.prototype.toString = function () { - return this.value -} + return false; + }); -Comparator.prototype.test = function (version) { - debug('Comparator.test', version, this.options.loose) + return function find(_x22, _x23) { + return _ref25.apply(this, arguments); + }; +})(); - if (this.semver === ANY) { - return true - } +let symlink = exports.symlink = (() => { + var _ref26 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (src, dest) { + try { + const stats = yield lstat(dest); + if (stats.isSymbolicLink()) { + const resolved = yield realpath(dest); + if (resolved === src) { + return; + } + } + } catch (err) { + if (err.code !== 'ENOENT') { + throw err; + } + } + // We use rimraf for unlink which never throws an ENOENT on missing target + yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(dest); - if (typeof version === 'string') { - version = new SemVer(version, this.options) - } + if (process.platform === 'win32') { + // use directory junctions if possible on win32, this requires absolute paths + yield fsSymlink(src, dest, 'junction'); + } else { + // use relative paths otherwise which will be retained if the directory is moved + let relative; + try { + relative = (_path || _load_path()).default.relative((_fs || _load_fs()).default.realpathSync((_path || _load_path()).default.dirname(dest)), (_fs || _load_fs()).default.realpathSync(src)); + } catch (err) { + if (err.code !== 'ENOENT') { + throw err; + } + relative = (_path || _load_path()).default.relative((_path || _load_path()).default.dirname(dest), src); + } + // When path.relative returns an empty string for the current directory, we should instead use + // '.', which is a valid fs.symlink target. + yield fsSymlink(relative || '.', dest); + } + }); - return cmp(version, this.operator, this.semver, this.options) -} + return function symlink(_x24, _x25) { + return _ref26.apply(this, arguments); + }; +})(); -Comparator.prototype.intersects = function (comp, options) { - if (!(comp instanceof Comparator)) { - throw new TypeError('a Comparator is required') - } +let walk = exports.walk = (() => { + var _ref27 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (dir, relativeDir, ignoreBasenames = new Set()) { + let files = []; - if (!options || typeof options !== 'object') { - options = { - loose: !!options, - includePrerelease: false + let filenames = yield readdir(dir); + if (ignoreBasenames.size) { + filenames = filenames.filter(function (name) { + return !ignoreBasenames.has(name); + }); } - } - var rangeTmp + for (var _iterator14 = filenames, _isArray14 = Array.isArray(_iterator14), _i14 = 0, _iterator14 = _isArray14 ? _iterator14 : _iterator14[Symbol.iterator]();;) { + var _ref28; - if (this.operator === '') { - rangeTmp = new Range(comp.value, options) - return satisfies(this.value, rangeTmp, options) - } else if (comp.operator === '') { - rangeTmp = new Range(this.value, options) - return satisfies(comp.semver, rangeTmp, options) - } + if (_isArray14) { + if (_i14 >= _iterator14.length) break; + _ref28 = _iterator14[_i14++]; + } else { + _i14 = _iterator14.next(); + if (_i14.done) break; + _ref28 = _i14.value; + } - var sameDirectionIncreasing = - (this.operator === '>=' || this.operator === '>') && - (comp.operator === '>=' || comp.operator === '>') - var sameDirectionDecreasing = - (this.operator === '<=' || this.operator === '<') && - (comp.operator === '<=' || comp.operator === '<') - var sameSemVer = this.semver.version === comp.semver.version - var differentDirectionsInclusive = - (this.operator === '>=' || this.operator === '<=') && - (comp.operator === '>=' || comp.operator === '<=') - var oppositeDirectionsLessThan = - cmp(this.semver, '<', comp.semver, options) && - ((this.operator === '>=' || this.operator === '>') && - (comp.operator === '<=' || comp.operator === '<')) - var oppositeDirectionsGreaterThan = - cmp(this.semver, '>', comp.semver, options) && - ((this.operator === '<=' || this.operator === '<') && - (comp.operator === '>=' || comp.operator === '>')) + const name = _ref28; - return sameDirectionIncreasing || sameDirectionDecreasing || - (sameSemVer && differentDirectionsInclusive) || - oppositeDirectionsLessThan || oppositeDirectionsGreaterThan -} + const relative = relativeDir ? (_path || _load_path()).default.join(relativeDir, name) : name; + const loc = (_path || _load_path()).default.join(dir, name); + const stat = yield lstat(loc); -exports.Range = Range -function Range (range, options) { - if (!options || typeof options !== 'object') { - options = { - loose: !!options, - includePrerelease: false - } - } + files.push({ + relative, + basename: name, + absolute: loc, + mtime: +stat.mtime + }); - if (range instanceof Range) { - if (range.loose === !!options.loose && - range.includePrerelease === !!options.includePrerelease) { - return range - } else { - return new Range(range.raw, options) + if (stat.isDirectory()) { + files = files.concat((yield walk(loc, relative, ignoreBasenames))); + } } - } - - if (range instanceof Comparator) { - return new Range(range.value, options) - } - - if (!(this instanceof Range)) { - return new Range(range, options) - } - this.options = options - this.loose = !!options.loose - this.includePrerelease = !!options.includePrerelease + return files; + }); - // First, split based on boolean or || - this.raw = range - this.set = range.split(/\s*\|\|\s*/).map(function (range) { - return this.parseRange(range.trim()) - }, this).filter(function (c) { - // throw out any that are not relevant for whatever reason - return c.length - }) + return function walk(_x26, _x27) { + return _ref27.apply(this, arguments); + }; +})(); - if (!this.set.length) { - throw new TypeError('Invalid SemVer Range: ' + range) - } +let getFileSizeOnDisk = exports.getFileSizeOnDisk = (() => { + var _ref29 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (loc) { + const stat = yield lstat(loc); + const size = stat.size, + blockSize = stat.blksize; - this.format() -} -Range.prototype.format = function () { - this.range = this.set.map(function (comps) { - return comps.join(' ').trim() - }).join('||').trim() - return this.range -} + return Math.ceil(size / blockSize) * blockSize; + }); -Range.prototype.toString = function () { - return this.range -} + return function getFileSizeOnDisk(_x28) { + return _ref29.apply(this, arguments); + }; +})(); -Range.prototype.parseRange = function (range) { - var loose = this.options.loose - range = range.trim() - // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4` - var hr = loose ? re[HYPHENRANGELOOSE] : re[HYPHENRANGE] - range = range.replace(hr, hyphenReplace) - debug('hyphen replace', range) - // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5` - range = range.replace(re[COMPARATORTRIM], comparatorTrimReplace) - debug('comparator trim', range, re[COMPARATORTRIM]) +let getEolFromFile = (() => { + var _ref30 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (path) { + if (!(yield exists(path))) { + return undefined; + } - // `~ 1.2.3` => `~1.2.3` - range = range.replace(re[TILDETRIM], tildeTrimReplace) + const buffer = yield readFileBuffer(path); - // `^ 1.2.3` => `^1.2.3` - range = range.replace(re[CARETTRIM], caretTrimReplace) + for (let i = 0; i < buffer.length; ++i) { + if (buffer[i] === cr) { + return '\r\n'; + } + if (buffer[i] === lf) { + return '\n'; + } + } + return undefined; + }); - // normalize spaces - range = range.split(/\s+/).join(' ') + return function getEolFromFile(_x29) { + return _ref30.apply(this, arguments); + }; +})(); - // At this point, the range is completely trimmed and - // ready to be split into comparators. +let writeFilePreservingEol = exports.writeFilePreservingEol = (() => { + var _ref31 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (path, data) { + const eol = (yield getEolFromFile(path)) || (_os || _load_os()).default.EOL; + if (eol !== '\n') { + data = data.replace(/\n/g, eol); + } + yield writeFile(path, data); + }); - var compRe = loose ? re[COMPARATORLOOSE] : re[COMPARATOR] - var set = range.split(' ').map(function (comp) { - return parseComparator(comp, this.options) - }, this).join(' ').split(/\s+/) - if (this.options.loose) { - // in loose mode, throw out any that are not valid comparators - set = set.filter(function (comp) { - return !!comp.match(compRe) - }) - } - set = set.map(function (comp) { - return new Comparator(comp, this.options) - }, this) + return function writeFilePreservingEol(_x30, _x31) { + return _ref31.apply(this, arguments); + }; +})(); - return set -} +let hardlinksWork = exports.hardlinksWork = (() => { + var _ref32 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (dir) { + const filename = 'test-file' + Math.random(); + const file = (_path || _load_path()).default.join(dir, filename); + const fileLink = (_path || _load_path()).default.join(dir, filename + '-link'); + try { + yield writeFile(file, 'test'); + yield link(file, fileLink); + } catch (err) { + return false; + } finally { + yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(file); + yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(fileLink); + } + return true; + }); -Range.prototype.intersects = function (range, options) { - if (!(range instanceof Range)) { - throw new TypeError('a Range is required') - } + return function hardlinksWork(_x32) { + return _ref32.apply(this, arguments); + }; +})(); - return this.set.some(function (thisComparators) { - return thisComparators.every(function (thisComparator) { - return range.set.some(function (rangeComparators) { - return rangeComparators.every(function (rangeComparator) { - return thisComparator.intersects(rangeComparator, options) - }) - }) - }) - }) -} +// not a strict polyfill for Node's fs.mkdtemp -// Mostly just for testing and legacy API reasons -exports.toComparators = toComparators -function toComparators (range, options) { - return new Range(range, options).set.map(function (comp) { - return comp.map(function (c) { - return c.value - }).join(' ').trim().split(' ') - }) -} -// comprised of xranges, tildes, stars, and gtlt's at this point. -// already replaced the hyphen ranges -// turn into a set of JUST comparators. -function parseComparator (comp, options) { - debug('comp', comp, options) - comp = replaceCarets(comp, options) - debug('caret', comp) - comp = replaceTildes(comp, options) - debug('tildes', comp) - comp = replaceXRanges(comp, options) - debug('xrange', comp) - comp = replaceStars(comp, options) - debug('stars', comp) - return comp -} +let makeTempDir = exports.makeTempDir = (() => { + var _ref33 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (prefix) { + const dir = (_path || _load_path()).default.join((_os || _load_os()).default.tmpdir(), `yarn-${prefix || ''}-${Date.now()}-${Math.random()}`); + yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(dir); + yield mkdirp(dir); + return dir; + }); -function isX (id) { - return !id || id.toLowerCase() === 'x' || id === '*' -} + return function makeTempDir(_x33) { + return _ref33.apply(this, arguments); + }; +})(); -// ~, ~> --> * (any, kinda silly) -// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0 -// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0 -// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0 -// ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0 -// ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0 -function replaceTildes (comp, options) { - return comp.trim().split(/\s+/).map(function (comp) { - return replaceTilde(comp, options) - }).join(' ') -} +let readFirstAvailableStream = exports.readFirstAvailableStream = (() => { + var _ref34 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (paths) { + for (var _iterator15 = paths, _isArray15 = Array.isArray(_iterator15), _i15 = 0, _iterator15 = _isArray15 ? _iterator15 : _iterator15[Symbol.iterator]();;) { + var _ref35; -function replaceTilde (comp, options) { - var r = options.loose ? re[TILDELOOSE] : re[TILDE] - return comp.replace(r, function (_, M, m, p, pr) { - debug('tilde', comp, _, M, m, p, pr) - var ret + if (_isArray15) { + if (_i15 >= _iterator15.length) break; + _ref35 = _iterator15[_i15++]; + } else { + _i15 = _iterator15.next(); + if (_i15.done) break; + _ref35 = _i15.value; + } - if (isX(M)) { - ret = '' - } else if (isX(m)) { - ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' - } else if (isX(p)) { - // ~1.2 == >=1.2.0 <1.3.0 - ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' - } else if (pr) { - debug('replaceTilde pr', pr) - ret = '>=' + M + '.' + m + '.' + p + '-' + pr + - ' <' + M + '.' + (+m + 1) + '.0' - } else { - // ~1.2.3 == >=1.2.3 <1.3.0 - ret = '>=' + M + '.' + m + '.' + p + - ' <' + M + '.' + (+m + 1) + '.0' + const path = _ref35; + + try { + const fd = yield open(path, 'r'); + return (_fs || _load_fs()).default.createReadStream(path, { fd }); + } catch (err) { + // Try the next one + } } + return null; + }); - debug('tilde return', ret) - return ret - }) -} + return function readFirstAvailableStream(_x34) { + return _ref34.apply(this, arguments); + }; +})(); -// ^ --> * (any, kinda silly) -// ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0 -// ^2.0, ^2.0.x --> >=2.0.0 <3.0.0 -// ^1.2, ^1.2.x --> >=1.2.0 <2.0.0 -// ^1.2.3 --> >=1.2.3 <2.0.0 -// ^1.2.0 --> >=1.2.0 <2.0.0 -function replaceCarets (comp, options) { - return comp.trim().split(/\s+/).map(function (comp) { - return replaceCaret(comp, options) - }).join(' ') -} +let getFirstSuitableFolder = exports.getFirstSuitableFolder = (() => { + var _ref36 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (paths, mode = constants.W_OK | constants.X_OK) { + const result = { + skipped: [], + folder: null + }; -function replaceCaret (comp, options) { - debug('caret', comp, options) - var r = options.loose ? re[CARETLOOSE] : re[CARET] - return comp.replace(r, function (_, M, m, p, pr) { - debug('caret', comp, _, M, m, p, pr) - var ret + for (var _iterator16 = paths, _isArray16 = Array.isArray(_iterator16), _i16 = 0, _iterator16 = _isArray16 ? _iterator16 : _iterator16[Symbol.iterator]();;) { + var _ref37; - if (isX(M)) { - ret = '' - } else if (isX(m)) { - ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' - } else if (isX(p)) { - if (M === '0') { - ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' - } else { - ret = '>=' + M + '.' + m + '.0 <' + (+M + 1) + '.0.0' - } - } else if (pr) { - debug('replaceCaret pr', pr) - if (M === '0') { - if (m === '0') { - ret = '>=' + M + '.' + m + '.' + p + '-' + pr + - ' <' + M + '.' + m + '.' + (+p + 1) - } else { - ret = '>=' + M + '.' + m + '.' + p + '-' + pr + - ' <' + M + '.' + (+m + 1) + '.0' - } + if (_isArray16) { + if (_i16 >= _iterator16.length) break; + _ref37 = _iterator16[_i16++]; } else { - ret = '>=' + M + '.' + m + '.' + p + '-' + pr + - ' <' + (+M + 1) + '.0.0' + _i16 = _iterator16.next(); + if (_i16.done) break; + _ref37 = _i16.value; } - } else { - debug('no pr') - if (M === '0') { - if (m === '0') { - ret = '>=' + M + '.' + m + '.' + p + - ' <' + M + '.' + m + '.' + (+p + 1) - } else { - ret = '>=' + M + '.' + m + '.' + p + - ' <' + M + '.' + (+m + 1) + '.0' - } - } else { - ret = '>=' + M + '.' + m + '.' + p + - ' <' + (+M + 1) + '.0.0' + + const folder = _ref37; + + try { + yield mkdirp(folder); + yield access(folder, mode); + + result.folder = folder; + + return result; + } catch (error) { + result.skipped.push({ + error, + folder + }); } } + return result; + }); - debug('caret return', ret) - return ret - }) + return function getFirstSuitableFolder(_x35) { + return _ref36.apply(this, arguments); + }; +})(); + +exports.copy = copy; +exports.readFile = readFile; +exports.readFileRaw = readFileRaw; +exports.normalizeOS = normalizeOS; + +var _fs; + +function _load_fs() { + return _fs = _interopRequireDefault(__webpack_require__(3)); } -function replaceXRanges (comp, options) { - debug('replaceXRanges', comp, options) - return comp.split(/\s+/).map(function (comp) { - return replaceXRange(comp, options) - }).join(' ') +var _glob; + +function _load_glob() { + return _glob = _interopRequireDefault(__webpack_require__(75)); } -function replaceXRange (comp, options) { - comp = comp.trim() - var r = options.loose ? re[XRANGELOOSE] : re[XRANGE] - return comp.replace(r, function (ret, gtlt, M, m, p, pr) { - debug('xRange', comp, ret, gtlt, M, m, p, pr) - var xM = isX(M) - var xm = xM || isX(m) - var xp = xm || isX(p) - var anyX = xp +var _os; - if (gtlt === '=' && anyX) { - gtlt = '' - } +function _load_os() { + return _os = _interopRequireDefault(__webpack_require__(36)); +} - if (xM) { - if (gtlt === '>' || gtlt === '<') { - // nothing is allowed - ret = '<0.0.0' - } else { - // nothing is forbidden - ret = '*' - } - } else if (gtlt && anyX) { - // we know patch is an x, because we have any x at all. - // replace X with 0 - if (xm) { - m = 0 - } - p = 0 +var _path; - if (gtlt === '>') { - // >1 => >=2.0.0 - // >1.2 => >=1.3.0 - // >1.2.3 => >= 1.2.4 - gtlt = '>=' - if (xm) { - M = +M + 1 - m = 0 - p = 0 - } else { - m = +m + 1 - p = 0 - } - } else if (gtlt === '<=') { - // <=0.7.x is actually <0.8.0, since any 0.7.x should - // pass. Similarly, <=7.x is actually <8.0.0, etc. - gtlt = '<' - if (xm) { - M = +M + 1 - } else { - m = +m + 1 - } - } +function _load_path() { + return _path = _interopRequireDefault(__webpack_require__(0)); +} - ret = gtlt + M + '.' + m + '.' + p - } else if (xm) { - ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' - } else if (xp) { - ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' - } +var _blockingQueue; - debug('xRange return', ret) +function _load_blockingQueue() { + return _blockingQueue = _interopRequireDefault(__webpack_require__(84)); +} - return ret - }) +var _promise; + +function _load_promise() { + return _promise = _interopRequireWildcard(__webpack_require__(40)); } -// Because * is AND-ed with everything else in the comparator, -// and '' means "any version", just remove the *s entirely. -function replaceStars (comp, options) { - debug('replaceStars', comp, options) - // Looseness is ignored here. star is always as loose as it gets! - return comp.trim().replace(re[STAR], '') +var _promise2; + +function _load_promise2() { + return _promise2 = __webpack_require__(40); } -// This function is passed to string.replace(re[HYPHENRANGE]) -// M, m, patch, prerelease, build -// 1.2 - 3.4.5 => >=1.2.0 <=3.4.5 -// 1.2.3 - 3.4 => >=1.2.0 <3.5.0 Any 3.4.x will do -// 1.2 - 3.4 => >=1.2.0 <3.5.0 -function hyphenReplace ($0, - from, fM, fm, fp, fpr, fb, - to, tM, tm, tp, tpr, tb) { - if (isX(fM)) { - from = '' - } else if (isX(fm)) { - from = '>=' + fM + '.0.0' - } else if (isX(fp)) { - from = '>=' + fM + '.' + fm + '.0' - } else { - from = '>=' + from - } +var _map; - if (isX(tM)) { - to = '' - } else if (isX(tm)) { - to = '<' + (+tM + 1) + '.0.0' - } else if (isX(tp)) { - to = '<' + tM + '.' + (+tm + 1) + '.0' - } else if (tpr) { - to = '<=' + tM + '.' + tm + '.' + tp + '-' + tpr - } else { - to = '<=' + to - } +function _load_map() { + return _map = _interopRequireDefault(__webpack_require__(20)); +} - return (from + ' ' + to).trim() +var _fsNormalized; + +function _load_fsNormalized() { + return _fsNormalized = __webpack_require__(164); } -// if ANY of the sets match ALL of its comparators, then pass -Range.prototype.test = function (version) { - if (!version) { - return false - } +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } - if (typeof version === 'string') { - version = new SemVer(version, this.options) - } +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - for (var i = 0; i < this.set.length; i++) { - if (testSet(this.set[i], version, this.options)) { - return true - } - } - return false -} +const constants = exports.constants = typeof (_fs || _load_fs()).default.constants !== 'undefined' ? (_fs || _load_fs()).default.constants : { + R_OK: (_fs || _load_fs()).default.R_OK, + W_OK: (_fs || _load_fs()).default.W_OK, + X_OK: (_fs || _load_fs()).default.X_OK +}; -function testSet (set, version, options) { - for (var i = 0; i < set.length; i++) { - if (!set[i].test(version)) { - return false - } - } +const lockQueue = exports.lockQueue = new (_blockingQueue || _load_blockingQueue()).default('fs lock'); - if (version.prerelease.length && !options.includePrerelease) { - // Find the set of versions that are allowed to have prereleases - // For example, ^1.2.3-pr.1 desugars to >=1.2.3-pr.1 <2.0.0 - // That should allow `1.2.3-pr.2` to pass. - // However, `1.2.4-alpha.notready` should NOT be allowed, - // even though it's within the range set by the comparators. - for (i = 0; i < set.length; i++) { - debug(set[i].semver) - if (set[i].semver === ANY) { - continue - } +const readFileBuffer = exports.readFileBuffer = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.readFile); +const open = exports.open = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.open); +const writeFile = exports.writeFile = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.writeFile); +const readlink = exports.readlink = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.readlink); +const realpath = exports.realpath = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.realpath); +const readdir = exports.readdir = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.readdir); +const rename = exports.rename = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.rename); +const access = exports.access = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.access); +const stat = exports.stat = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.stat); +const mkdirp = exports.mkdirp = (0, (_promise2 || _load_promise2()).promisify)(__webpack_require__(116)); +const exists = exports.exists = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.exists, true); +const lstat = exports.lstat = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.lstat); +const chmod = exports.chmod = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.chmod); +const link = exports.link = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.link); +const glob = exports.glob = (0, (_promise2 || _load_promise2()).promisify)((_glob || _load_glob()).default); +exports.unlink = (_fsNormalized || _load_fsNormalized()).unlink; - if (set[i].semver.prerelease.length > 0) { - var allowed = set[i].semver - if (allowed.major === version.major && - allowed.minor === version.minor && - allowed.patch === version.patch) { - return true - } - } - } +// fs.copyFile uses the native file copying instructions on the system, performing much better +// than any JS-based solution and consumes fewer resources. Repeated testing to fine tune the +// concurrency level revealed 128 as the sweet spot on a quad-core, 16 CPU Intel system with SSD. - // Version has a -pre, but it's not one of the ones we like. - return false - } +const CONCURRENT_QUEUE_ITEMS = (_fs || _load_fs()).default.copyFile ? 128 : 4; - return true -} +const fsSymlink = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.symlink); +const invariant = __webpack_require__(7); +const stripBOM = __webpack_require__(122); -exports.satisfies = satisfies -function satisfies (version, range, options) { - try { - range = new Range(range, options) - } catch (er) { - return false - } - return range.test(version) +const noop = () => {}; + +function copy(src, dest, reporter) { + return copyBulk([{ src, dest }], reporter); } -exports.maxSatisfying = maxSatisfying -function maxSatisfying (versions, range, options) { - var max = null - var maxSV = null - try { - var rangeObj = new Range(range, options) - } catch (er) { - return null - } - versions.forEach(function (v) { - if (rangeObj.test(v)) { - // satisfies(v, range, options) - if (!max || maxSV.compare(v) === -1) { - // compare(max, v, true) - max = v - maxSV = new SemVer(max, options) +function _readFile(loc, encoding) { + return new Promise((resolve, reject) => { + (_fs || _load_fs()).default.readFile(loc, encoding, function (err, content) { + if (err) { + reject(err); + } else { + resolve(content); } - } - }) - return max + }); + }); } -exports.minSatisfying = minSatisfying -function minSatisfying (versions, range, options) { - var min = null - var minSV = null - try { - var rangeObj = new Range(range, options) - } catch (er) { - return null - } - versions.forEach(function (v) { - if (rangeObj.test(v)) { - // satisfies(v, range, options) - if (!min || minSV.compare(v) === 1) { - // compare(min, v, true) - min = v - minSV = new SemVer(min, options) - } - } - }) - return min +function readFile(loc) { + return _readFile(loc, 'utf8').then(normalizeOS); } -exports.minVersion = minVersion -function minVersion (range, loose) { - range = new Range(range, loose) +function readFileRaw(loc) { + return _readFile(loc, 'binary'); +} - var minver = new SemVer('0.0.0') - if (range.test(minver)) { - return minver - } +function normalizeOS(body) { + return body.replace(/\r\n/g, '\n'); +} - minver = new SemVer('0.0.0-0') - if (range.test(minver)) { - return minver - } +const cr = '\r'.charCodeAt(0); +const lf = '\n'.charCodeAt(0); - minver = null - for (var i = 0; i < range.set.length; ++i) { - var comparators = range.set[i] +/***/ }), +/* 6 */ +/***/ (function(module, exports, __webpack_require__) { - comparators.forEach(function (comparator) { - // Clone to avoid manipulating the comparator's semver object. - var compver = new SemVer(comparator.semver.version) - switch (comparator.operator) { - case '>': - if (compver.prerelease.length === 0) { - compver.patch++ - } else { - compver.prerelease.push(0) - } - compver.raw = compver.format() - /* fallthrough */ - case '': - case '>=': - if (!minver || gt(minver, compver)) { - minver = compver - } - break - case '<': - case '<=': - /* Ignore maximum versions */ - break - /* istanbul ignore next */ - default: - throw new Error('Unexpected operation: ' + comparator.operator) - } - }) - } +"use strict"; - if (minver && range.test(minver)) { - return minver - } - return null -} +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.getPathKey = getPathKey; +const os = __webpack_require__(36); +const path = __webpack_require__(0); +const userHome = __webpack_require__(45).default; -exports.validRange = validRange -function validRange (range, options) { - try { - // Return '*' instead of '' so that truthiness works. - // This will throw if it's invalid anyway - return new Range(range, options).range || '*' - } catch (er) { - return null - } -} +var _require = __webpack_require__(171); -// Determine if version is less than all the versions possible in the range -exports.ltr = ltr -function ltr (version, range, options) { - return outside(version, range, '<', options) -} +const getCacheDir = _require.getCacheDir, + getConfigDir = _require.getConfigDir, + getDataDir = _require.getDataDir; -// Determine if version is greater than all the versions possible in the range. -exports.gtr = gtr -function gtr (version, range, options) { - return outside(version, range, '>', options) -} +const isWebpackBundle = __webpack_require__(227); -exports.outside = outside -function outside (version, range, hilo, options) { - version = new SemVer(version, options) - range = new Range(range, options) +const DEPENDENCY_TYPES = exports.DEPENDENCY_TYPES = ['devDependencies', 'dependencies', 'optionalDependencies', 'peerDependencies']; +const RESOLUTIONS = exports.RESOLUTIONS = 'resolutions'; +const MANIFEST_FIELDS = exports.MANIFEST_FIELDS = [RESOLUTIONS, ...DEPENDENCY_TYPES]; - var gtfn, ltefn, ltfn, comp, ecomp - switch (hilo) { - case '>': - gtfn = gt - ltefn = lte - ltfn = lt - comp = '>' - ecomp = '>=' - break - case '<': - gtfn = lt - ltefn = gte - ltfn = gt - comp = '<' - ecomp = '<=' - break - default: - throw new TypeError('Must provide a hilo val of "<" or ">"') - } +const SUPPORTED_NODE_VERSIONS = exports.SUPPORTED_NODE_VERSIONS = '^4.8.0 || ^5.7.0 || ^6.2.2 || >=8.0.0'; + +const YARN_REGISTRY = exports.YARN_REGISTRY = 'https://registry.yarnpkg.com'; + +const YARN_DOCS = exports.YARN_DOCS = 'https://yarnpkg.com/en/docs/cli/'; +const YARN_INSTALLER_SH = exports.YARN_INSTALLER_SH = 'https://yarnpkg.com/install.sh'; +const YARN_INSTALLER_MSI = exports.YARN_INSTALLER_MSI = 'https://yarnpkg.com/latest.msi'; - // If it satisifes the range it is not outside - if (satisfies(version, range, options)) { - return false - } +const SELF_UPDATE_VERSION_URL = exports.SELF_UPDATE_VERSION_URL = 'https://yarnpkg.com/latest-version'; - // From now on, variable terms are as if we're in "gtr" mode. - // but note that everything is flipped for the "ltr" function. +// cache version, bump whenever we make backwards incompatible changes +const CACHE_VERSION = exports.CACHE_VERSION = 2; - for (var i = 0; i < range.set.length; ++i) { - var comparators = range.set[i] +// lockfile version, bump whenever we make backwards incompatible changes +const LOCKFILE_VERSION = exports.LOCKFILE_VERSION = 1; - var high = null - var low = null +// max amount of network requests to perform concurrently +const NETWORK_CONCURRENCY = exports.NETWORK_CONCURRENCY = 8; - comparators.forEach(function (comparator) { - if (comparator.semver === ANY) { - comparator = new Comparator('>=0.0.0') - } - high = high || comparator - low = low || comparator - if (gtfn(comparator.semver, high.semver, options)) { - high = comparator - } else if (ltfn(comparator.semver, low.semver, options)) { - low = comparator - } - }) +// HTTP timeout used when downloading packages +const NETWORK_TIMEOUT = exports.NETWORK_TIMEOUT = 30 * 1000; // in milliseconds - // If the edge version comparator has a operator then our version - // isn't outside it - if (high.operator === comp || high.operator === ecomp) { - return false - } +// max amount of child processes to execute concurrently +const CHILD_CONCURRENCY = exports.CHILD_CONCURRENCY = 5; - // If the lowest version comparator has an operator and our version - // is less than it then it isn't higher than the range - if ((!low.operator || low.operator === comp) && - ltefn(version, low.semver)) { - return false - } else if (low.operator === ecomp && ltfn(version, low.semver)) { - return false - } +const REQUIRED_PACKAGE_KEYS = exports.REQUIRED_PACKAGE_KEYS = ['name', 'version', '_uid']; + +function getPreferredCacheDirectories() { + const preferredCacheDirectories = [getCacheDir()]; + + if (process.getuid) { + // $FlowFixMe: process.getuid exists, dammit + preferredCacheDirectories.push(path.join(os.tmpdir(), `.yarn-cache-${process.getuid()}`)); } - return true -} -exports.prerelease = prerelease -function prerelease (version, options) { - var parsed = parse(version, options) - return (parsed && parsed.prerelease.length) ? parsed.prerelease : null -} + preferredCacheDirectories.push(path.join(os.tmpdir(), `.yarn-cache`)); -exports.intersects = intersects -function intersects (r1, r2, options) { - r1 = new Range(r1, options) - r2 = new Range(r2, options) - return r1.intersects(r2) + return preferredCacheDirectories; } -exports.coerce = coerce -function coerce (version) { - if (version instanceof SemVer) { - return version - } +const PREFERRED_MODULE_CACHE_DIRECTORIES = exports.PREFERRED_MODULE_CACHE_DIRECTORIES = getPreferredCacheDirectories(); +const CONFIG_DIRECTORY = exports.CONFIG_DIRECTORY = getConfigDir(); +const DATA_DIRECTORY = exports.DATA_DIRECTORY = getDataDir(); +const LINK_REGISTRY_DIRECTORY = exports.LINK_REGISTRY_DIRECTORY = path.join(DATA_DIRECTORY, 'link'); +const GLOBAL_MODULE_DIRECTORY = exports.GLOBAL_MODULE_DIRECTORY = path.join(DATA_DIRECTORY, 'global'); - if (typeof version !== 'string') { - return null +const NODE_BIN_PATH = exports.NODE_BIN_PATH = process.execPath; +const YARN_BIN_PATH = exports.YARN_BIN_PATH = getYarnBinPath(); + +// Webpack needs to be configured with node.__dirname/__filename = false +function getYarnBinPath() { + if (isWebpackBundle) { + return __filename; + } else { + return path.join(__dirname, '..', 'bin', 'yarn.js'); } +} - var match = version.match(re[COERCE]) +const NODE_MODULES_FOLDER = exports.NODE_MODULES_FOLDER = 'node_modules'; +const NODE_PACKAGE_JSON = exports.NODE_PACKAGE_JSON = 'package.json'; - if (match == null) { - return null +const POSIX_GLOBAL_PREFIX = exports.POSIX_GLOBAL_PREFIX = `${process.env.DESTDIR || ''}/usr/local`; +const FALLBACK_GLOBAL_PREFIX = exports.FALLBACK_GLOBAL_PREFIX = path.join(userHome, '.yarn'); + +const META_FOLDER = exports.META_FOLDER = '.yarn-meta'; +const INTEGRITY_FILENAME = exports.INTEGRITY_FILENAME = '.yarn-integrity'; +const LOCKFILE_FILENAME = exports.LOCKFILE_FILENAME = 'yarn.lock'; +const METADATA_FILENAME = exports.METADATA_FILENAME = '.yarn-metadata.json'; +const TARBALL_FILENAME = exports.TARBALL_FILENAME = '.yarn-tarball.tgz'; +const CLEAN_FILENAME = exports.CLEAN_FILENAME = '.yarnclean'; + +const NPM_LOCK_FILENAME = exports.NPM_LOCK_FILENAME = 'package-lock.json'; +const NPM_SHRINKWRAP_FILENAME = exports.NPM_SHRINKWRAP_FILENAME = 'npm-shrinkwrap.json'; + +const DEFAULT_INDENT = exports.DEFAULT_INDENT = ' '; +const SINGLE_INSTANCE_PORT = exports.SINGLE_INSTANCE_PORT = 31997; +const SINGLE_INSTANCE_FILENAME = exports.SINGLE_INSTANCE_FILENAME = '.yarn-single-instance'; + +const ENV_PATH_KEY = exports.ENV_PATH_KEY = getPathKey(process.platform, process.env); + +function getPathKey(platform, env) { + let pathKey = 'PATH'; + + // windows calls its path "Path" usually, but this is not guaranteed. + if (platform === 'win32') { + pathKey = 'Path'; + + for (const key in env) { + if (key.toLowerCase() === 'path') { + pathKey = key; + } + } } - return parse(match[1] + - '.' + (match[2] || '0') + - '.' + (match[3] || '0')) + return pathKey; } +const VERSION_COLOR_SCHEME = exports.VERSION_COLOR_SCHEME = { + major: 'red', + premajor: 'red', + minor: 'yellow', + preminor: 'yellow', + patch: 'green', + prepatch: 'green', + prerelease: 'red', + unchanged: 'white', + unknown: 'red' +}; /***/ }), -/* 406 */ +/* 7 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; +/** + * Copyright (c) 2013-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ -// detect either spaces or tabs but not both to properly handle tabs -// for indentation and spaces for alignment -const INDENT_RE = /^(?:( )+|\t+)/; - -function getMostUsed(indents) { - let result = 0; - let maxUsed = 0; - let maxWeight = 0; - - for (const entry of indents) { - // TODO: use destructuring when targeting Node.js 6 - const key = entry[0]; - const val = entry[1]; - - const u = val[0]; - const w = val[1]; - - if (u > maxUsed || (u === maxUsed && w > maxWeight)) { - maxUsed = u; - maxWeight = w; - result = Number(key); - } - } - return result; -} +/** + * Use invariant() to assert state which your program assumes to be true. + * + * Provide sprintf-style format (only %s is supported) and arguments + * to provide information about what broke and what you were + * expecting. + * + * The invariant message will be stripped in production, but the invariant + * will remain to ensure logic does not differ in production. + */ -module.exports = str => { - if (typeof str !== 'string') { - throw new TypeError('Expected a string'); - } +var NODE_ENV = "none"; - // used to see if tabs or spaces are the most used - let tabs = 0; - let spaces = 0; +var invariant = function(condition, format, a, b, c, d, e, f) { + if (NODE_ENV !== 'production') { + if (format === undefined) { + throw new Error('invariant requires an error message argument'); + } + } - // remember the size of previous line's indentation - let prev = 0; + if (!condition) { + var error; + if (format === undefined) { + error = new Error( + 'Minified exception occurred; use the non-minified dev environment ' + + 'for the full error message and additional helpful warnings.' + ); + } else { + var args = [a, b, c, d, e, f]; + var argIndex = 0; + error = new Error( + format.replace(/%s/g, function() { return args[argIndex++]; }) + ); + error.name = 'Invariant Violation'; + } - // remember how many indents/unindents as occurred for a given size - // and how much lines follow a given indentation - // - // indents = { - // 3: [1, 0], - // 4: [1, 5], - // 5: [1, 0], - // 12: [1, 0], - // } - const indents = new Map(); + error.framesToPop = 1; // we don't care about invariant's own frame + throw error; + } +}; - // pointer to the array of last used indent - let current; +module.exports = invariant; - // whether the last action was an indent (opposed to an unindent) - let isIndent; - for (const line of str.split(/\n/g)) { - if (!line) { - // ignore empty lines - continue; - } +/***/ }), +/* 8 */, +/* 9 */ +/***/ (function(module, exports) { - let indent; - const matches = line.match(INDENT_RE); +module.exports = __webpack_require__(133); - if (matches) { - indent = matches[0].length; +/***/ }), +/* 10 */, +/* 11 */ +/***/ (function(module, exports) { - if (matches[1]) { - spaces++; - } else { - tabs++; - } - } else { - indent = 0; - } +// https://github.com/zloirock/core-js/issues/86#issuecomment-115759028 +var global = module.exports = typeof window != 'undefined' && window.Math == Math + ? window : typeof self != 'undefined' && self.Math == Math ? self + // eslint-disable-next-line no-new-func + : Function('return this')(); +if (typeof __g == 'number') __g = global; // eslint-disable-line no-undef - const diff = indent - prev; - prev = indent; - if (diff) { - // an indent or unindent has been detected +/***/ }), +/* 12 */ +/***/ (function(module, exports, __webpack_require__) { - isIndent = diff > 0; +"use strict"; - current = indents.get(isIndent ? diff : -diff); - if (current) { - current[0]++; - } else { - current = [1, 0]; - indents.set(diff, current); - } - } else if (current) { - // if the last action was an indent, increment the weight - current[1] += Number(isIndent); - } - } +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.sortAlpha = sortAlpha; +exports.entries = entries; +exports.removePrefix = removePrefix; +exports.removeSuffix = removeSuffix; +exports.addSuffix = addSuffix; +exports.hyphenate = hyphenate; +exports.camelCase = camelCase; +exports.compareSortedArrays = compareSortedArrays; +exports.sleep = sleep; +const _camelCase = __webpack_require__(176); - const amount = getMostUsed(indents); +function sortAlpha(a, b) { + // sort alphabetically in a deterministic way + const shortLen = Math.min(a.length, b.length); + for (let i = 0; i < shortLen; i++) { + const aChar = a.charCodeAt(i); + const bChar = b.charCodeAt(i); + if (aChar !== bChar) { + return aChar - bChar; + } + } + return a.length - b.length; +} - let type; - let indent; - if (!amount) { - type = null; - indent = ''; - } else if (spaces >= tabs) { - type = 'space'; - indent = ' '.repeat(amount); - } else { - type = 'tab'; - indent = '\t'.repeat(amount); - } +function entries(obj) { + const entries = []; + if (obj) { + for (const key in obj) { + entries.push([key, obj[key]]); + } + } + return entries; +} - return { - amount, - type, - indent - }; -}; +function removePrefix(pattern, prefix) { + if (pattern.startsWith(prefix)) { + pattern = pattern.slice(prefix.length); + } + return pattern; +} -/***/ }), -/* 407 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +function removeSuffix(pattern, suffix) { + if (pattern.endsWith(suffix)) { + return pattern.slice(0, -suffix.length); + } -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "installInDir", function() { return installInDir; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "runScriptInPackage", function() { return runScriptInPackage; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "runScriptInPackageStreaming", function() { return runScriptInPackageStreaming; }); -/* harmony import */ var _child_process__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(221); -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ + return pattern; +} -const YARN_EXEC = process.env.npm_execpath || 'yarn'; -/** - * Install all dependencies in the given directory - */ +function addSuffix(pattern, suffix) { + if (!pattern.endsWith(suffix)) { + return pattern + suffix; + } -async function installInDir(directory, extraArgs = []) { - const options = ['install', '--non-interactive', ...extraArgs]; // We pass the mutex flag to ensure only one instance of yarn runs at any - // given time (e.g. to avoid conflicts). + return pattern; +} - await Object(_child_process__WEBPACK_IMPORTED_MODULE_0__["spawn"])(YARN_EXEC, options, { - cwd: directory +function hyphenate(str) { + return str.replace(/[A-Z]/g, match => { + return '-' + match.charAt(0).toLowerCase(); }); } -/** - * Run script in the given directory - */ -async function runScriptInPackage(script, args, pkg) { - const execOpts = { - cwd: pkg.path - }; - await Object(_child_process__WEBPACK_IMPORTED_MODULE_0__["spawn"])(YARN_EXEC, ['run', script, ...args], execOpts); +function camelCase(str) { + if (/[A-Z]/.test(str)) { + return null; + } else { + return _camelCase(str); + } } -/** - * Run script in the given directory - */ -function runScriptInPackageStreaming({ - script, - args, - pkg, - debug -}) { - const execOpts = { - cwd: pkg.path - }; - return Object(_child_process__WEBPACK_IMPORTED_MODULE_0__["spawnStreaming"])(YARN_EXEC, ['run', script, ...args], execOpts, { - prefix: pkg.name, - debug +function compareSortedArrays(array1, array2) { + if (array1.length !== array2.length) { + return false; + } + for (let i = 0, len = array1.length; i < len; i++) { + if (array1[i] !== array2[i]) { + return false; + } + } + return true; +} + +function sleep(ms) { + return new Promise(resolve => { + setTimeout(resolve, ms); }); } /***/ }), -/* 408 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* 13 */ +/***/ (function(module, exports, __webpack_require__) { + +var store = __webpack_require__(107)('wks'); +var uid = __webpack_require__(111); +var Symbol = __webpack_require__(11).Symbol; +var USE_SYMBOL = typeof Symbol == 'function'; + +var $exports = module.exports = function (name) { + return store[name] || (store[name] = + USE_SYMBOL && Symbol[name] || (USE_SYMBOL ? Symbol : uid)('Symbol.' + name)); +}; + +$exports.store = store; + + +/***/ }), +/* 14 */ +/***/ (function(module, exports, __webpack_require__) { "use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "readYarnLock", function() { return readYarnLock; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "resolveDepsForProject", function() { return resolveDepsForProject; }); -/* harmony import */ var _yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(409); -/* harmony import */ var _yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var _utils_fs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(231); -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ -// @ts-expect-error published types are worthless -async function readYarnLock(kbn) { - try { - const contents = await Object(_utils_fs__WEBPACK_IMPORTED_MODULE_1__["readFile"])(kbn.getAbsolute('yarn.lock'), 'utf8'); - const yarnLock = Object(_yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__["parse"])(contents); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.stringify = exports.parse = undefined; - if (yarnLock.type === 'success') { - return yarnLock.object; - } +var _asyncToGenerator2; - throw new Error('unable to read yarn.lock file, please run `yarn kbn bootstrap`'); - } catch (error) { - if (error.code !== 'ENOENT') { - throw error; - } +function _load_asyncToGenerator() { + return _asyncToGenerator2 = _interopRequireDefault(__webpack_require__(1)); +} + +var _parse; + +function _load_parse() { + return _parse = __webpack_require__(81); +} + +Object.defineProperty(exports, 'parse', { + enumerable: true, + get: function get() { + return _interopRequireDefault(_parse || _load_parse()).default; + } +}); + +var _stringify; + +function _load_stringify() { + return _stringify = __webpack_require__(150); +} + +Object.defineProperty(exports, 'stringify', { + enumerable: true, + get: function get() { + return _interopRequireDefault(_stringify || _load_stringify()).default; } +}); +exports.implodeEntry = implodeEntry; +exports.explodeEntry = explodeEntry; + +var _misc; - return {}; +function _load_misc() { + return _misc = __webpack_require__(12); } -/** - * Get a list of the absolute dependencies of this project, as resolved - * in the yarn.lock file, does not include other projects in the workspace - * or their dependencies - */ -function resolveDepsForProject({ - project: rootProject, - yarnLock, - kbn, - log, - productionDepsOnly, - includeDependentProject -}) { - /** map of [name@range, { name, version }] */ - const resolved = new Map(); - const seenProjects = new Set(); - const projectQueue = [rootProject]; - const depQueue = []; +var _normalizePattern; - while (projectQueue.length) { - const project = projectQueue.shift(); +function _load_normalizePattern() { + return _normalizePattern = __webpack_require__(29); +} - if (seenProjects.has(project)) { - continue; - } +var _parse2; - seenProjects.add(project); - const projectDeps = Object.entries(productionDepsOnly ? project.productionDependencies : project.allDependencies); +function _load_parse2() { + return _parse2 = _interopRequireDefault(__webpack_require__(81)); +} - for (const [name, versionRange] of projectDeps) { - depQueue.push([name, versionRange]); - } +var _constants; - while (depQueue.length) { - const [name, versionRange] = depQueue.shift(); - const req = `${name}@${versionRange}`; +function _load_constants() { + return _constants = __webpack_require__(6); +} - if (resolved.has(req)) { - continue; - } +var _fs; - if (includeDependentProject && kbn.hasProject(name)) { - projectQueue.push(kbn.getProject(name)); - } +function _load_fs() { + return _fs = _interopRequireWildcard(__webpack_require__(5)); +} - if (!kbn.hasProject(name)) { - const pkg = yarnLock[req]; +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } - if (!pkg) { - log.warning('yarn.lock file is out of date, please run `yarn kbn bootstrap` to re-enable caching'); - return; - } +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - resolved.set(req, { - name, - version: pkg.version - }); - const allDepsEntries = [...Object.entries(pkg.dependencies || {}), ...Object.entries(pkg.optionalDependencies || {})]; +const invariant = __webpack_require__(7); - for (const [childName, childVersionRange] of allDepsEntries) { - depQueue.push([childName, childVersionRange]); - } - } - } - } +const path = __webpack_require__(0); +const ssri = __webpack_require__(55); - return resolved; +function getName(pattern) { + return (0, (_normalizePattern || _load_normalizePattern()).normalizePattern)(pattern).name; } -/***/ }), -/* 409 */ -/***/ (function(module, exports, __webpack_require__) { +function blankObjectUndefined(obj) { + return obj && Object.keys(obj).length ? obj : undefined; +} -module.exports = -/******/ (function(modules) { // webpackBootstrap -/******/ // The module cache -/******/ var installedModules = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ -/******/ // Check if module is in cache -/******/ if(installedModules[moduleId]) { -/******/ return installedModules[moduleId].exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = installedModules[moduleId] = { -/******/ i: moduleId, -/******/ l: false, -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); -/******/ -/******/ // Flag the module as loaded -/******/ module.l = true; -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/******/ -/******/ // expose the modules object (__webpack_modules__) -/******/ __webpack_require__.m = modules; -/******/ -/******/ // expose the module cache -/******/ __webpack_require__.c = installedModules; -/******/ -/******/ // identity function for calling harmony imports with the correct context -/******/ __webpack_require__.i = function(value) { return value; }; -/******/ -/******/ // define getter function for harmony exports -/******/ __webpack_require__.d = function(exports, name, getter) { -/******/ if(!__webpack_require__.o(exports, name)) { -/******/ Object.defineProperty(exports, name, { -/******/ configurable: false, -/******/ enumerable: true, -/******/ get: getter -/******/ }); -/******/ } -/******/ }; -/******/ -/******/ // getDefaultExport function for compatibility with non-harmony modules -/******/ __webpack_require__.n = function(module) { -/******/ var getter = module && module.__esModule ? -/******/ function getDefault() { return module['default']; } : -/******/ function getModuleExports() { return module; }; -/******/ __webpack_require__.d(getter, 'a', getter); -/******/ return getter; -/******/ }; -/******/ -/******/ // Object.prototype.hasOwnProperty.call -/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; -/******/ -/******/ // __webpack_public_path__ -/******/ __webpack_require__.p = ""; -/******/ -/******/ // Load entry module and return exports -/******/ return __webpack_require__(__webpack_require__.s = 14); -/******/ }) -/************************************************************************/ -/******/ ([ -/* 0 */ -/***/ (function(module, exports) { +function keyForRemote(remote) { + return remote.resolved || (remote.reference && remote.hash ? `${remote.reference}#${remote.hash}` : null); +} -module.exports = __webpack_require__(4); +function serializeIntegrity(integrity) { + // We need this because `Integrity.toString()` does not use sorting to ensure a stable string output + // See https://git.io/vx2Hy + return integrity.toString().split(' ').sort().join(' '); +} -/***/ }), -/* 1 */ -/***/ (function(module, exports, __webpack_require__) { +function implodeEntry(pattern, obj) { + const inferredName = getName(pattern); + const integrity = obj.integrity ? serializeIntegrity(obj.integrity) : ''; + const imploded = { + name: inferredName === obj.name ? undefined : obj.name, + version: obj.version, + uid: obj.uid === obj.version ? undefined : obj.uid, + resolved: obj.resolved, + registry: obj.registry === 'npm' ? undefined : obj.registry, + dependencies: blankObjectUndefined(obj.dependencies), + optionalDependencies: blankObjectUndefined(obj.optionalDependencies), + permissions: blankObjectUndefined(obj.permissions), + prebuiltVariants: blankObjectUndefined(obj.prebuiltVariants) + }; + if (integrity) { + imploded.integrity = integrity; + } + return imploded; +} -"use strict"; +function explodeEntry(pattern, obj) { + obj.optionalDependencies = obj.optionalDependencies || {}; + obj.dependencies = obj.dependencies || {}; + obj.uid = obj.uid || obj.version; + obj.permissions = obj.permissions || {}; + obj.registry = obj.registry || 'npm'; + obj.name = obj.name || getName(pattern); + const integrity = obj.integrity; + if (integrity && integrity.isIntegrity) { + obj.integrity = ssri.parse(integrity); + } + return obj; +} +class Lockfile { + constructor({ cache, source, parseResultType } = {}) { + this.source = source || ''; + this.cache = cache; + this.parseResultType = parseResultType; + } -exports.__esModule = true; + // source string if the `cache` was parsed -var _promise = __webpack_require__(173); -var _promise2 = _interopRequireDefault(_promise); + // if true, we're parsing an old yarn file and need to update integrity fields + hasEntriesExistWithoutIntegrity() { + if (!this.cache) { + return false; + } -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + for (const key in this.cache) { + // $FlowFixMe - `this.cache` is clearly defined at this point + if (!/^.*@(file:|http)/.test(key) && this.cache[key] && !this.cache[key].integrity) { + return true; + } + } -exports.default = function (fn) { - return function () { - var gen = fn.apply(this, arguments); - return new _promise2.default(function (resolve, reject) { - function step(key, arg) { - try { - var info = gen[key](arg); - var value = info.value; - } catch (error) { - reject(error); - return; - } + return false; + } - if (info.done) { - resolve(value); - } else { - return _promise2.default.resolve(value).then(function (value) { - step("next", value); - }, function (err) { - step("throw", err); - }); - } - } + static fromDirectory(dir, reporter) { + return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { + // read the manifest in this directory + const lockfileLoc = path.join(dir, (_constants || _load_constants()).LOCKFILE_FILENAME); - return step("next"); - }); - }; -}; + let lockfile; + let rawLockfile = ''; + let parseResult; -/***/ }), -/* 2 */ -/***/ (function(module, exports) { + if (yield (_fs || _load_fs()).exists(lockfileLoc)) { + rawLockfile = yield (_fs || _load_fs()).readFile(lockfileLoc); + parseResult = (0, (_parse2 || _load_parse2()).default)(rawLockfile, lockfileLoc); -module.exports = __webpack_require__(113); + if (reporter) { + if (parseResult.type === 'merge') { + reporter.info(reporter.lang('lockfileMerged')); + } else if (parseResult.type === 'conflict') { + reporter.warn(reporter.lang('lockfileConflict')); + } + } -/***/ }), -/* 3 */ -/***/ (function(module, exports) { + lockfile = parseResult.object; + } else if (reporter) { + reporter.info(reporter.lang('noLockfileFound')); + } -module.exports = __webpack_require__(132); + return new Lockfile({ cache: lockfile, source: rawLockfile, parseResultType: parseResult && parseResult.type }); + })(); + } -/***/ }), -/* 4 */ -/***/ (function(module, exports, __webpack_require__) { + getLocked(pattern) { + const cache = this.cache; + if (!cache) { + return undefined; + } -"use strict"; + const shrunk = pattern in cache && cache[pattern]; + if (typeof shrunk === 'string') { + return this.getLocked(shrunk); + } else if (shrunk) { + explodeEntry(pattern, shrunk); + return shrunk; + } -Object.defineProperty(exports, "__esModule", { - value: true -}); -class MessageError extends Error { - constructor(msg, code) { - super(msg); - this.code = code; + return undefined; } -} - -exports.MessageError = MessageError; -class ProcessSpawnError extends MessageError { - constructor(msg, code, process) { - super(msg, code); - this.process = process; + removePattern(pattern) { + const cache = this.cache; + if (!cache) { + return; + } + delete cache[pattern]; } -} + getLockfile(patterns) { + const lockfile = {}; + const seen = new Map(); -exports.ProcessSpawnError = ProcessSpawnError; -class SecurityError extends MessageError {} + // order by name so that lockfile manifest is assigned to the first dependency with this manifest + // the others that have the same remoteKey will just refer to the first + // ordering allows for consistency in lockfile when it is serialized + const sortedPatternsKeys = Object.keys(patterns).sort((_misc || _load_misc()).sortAlpha); -exports.SecurityError = SecurityError; -class ProcessTermError extends MessageError {} + for (var _iterator = sortedPatternsKeys, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { + var _ref; -exports.ProcessTermError = ProcessTermError; -class ResponseError extends Error { - constructor(msg, responseCode) { - super(msg); - this.responseCode = responseCode; - } + if (_isArray) { + if (_i >= _iterator.length) break; + _ref = _iterator[_i++]; + } else { + _i = _iterator.next(); + if (_i.done) break; + _ref = _i.value; + } -} -exports.ResponseError = ResponseError; + const pattern = _ref; -/***/ }), -/* 5 */ -/***/ (function(module, exports, __webpack_require__) { + const pkg = patterns[pattern]; + const remote = pkg._remote, + ref = pkg._reference; -"use strict"; + invariant(ref, 'Package is missing a reference'); + invariant(remote, 'Package is missing a remote'); + const remoteKey = keyForRemote(remote); + const seenPattern = remoteKey && seen.get(remoteKey); + if (seenPattern) { + // no point in duplicating it + lockfile[pattern] = seenPattern; -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.getFirstSuitableFolder = exports.readFirstAvailableStream = exports.makeTempDir = exports.hardlinksWork = exports.writeFilePreservingEol = exports.getFileSizeOnDisk = exports.walk = exports.symlink = exports.find = exports.readJsonAndFile = exports.readJson = exports.readFileAny = exports.hardlinkBulk = exports.copyBulk = exports.unlink = exports.glob = exports.link = exports.chmod = exports.lstat = exports.exists = exports.mkdirp = exports.stat = exports.access = exports.rename = exports.readdir = exports.realpath = exports.readlink = exports.writeFile = exports.open = exports.readFileBuffer = exports.lockQueue = exports.constants = undefined; + // if we're relying on our name being inferred and two of the patterns have + // different inferred names then we need to set it + if (!seenPattern.name && getName(pattern) !== pkg.name) { + seenPattern.name = pkg.name; + } + continue; + } + const obj = implodeEntry(pattern, { + name: pkg.name, + version: pkg.version, + uid: pkg._uid, + resolved: remote.resolved, + integrity: remote.integrity, + registry: remote.registry, + dependencies: pkg.dependencies, + peerDependencies: pkg.peerDependencies, + optionalDependencies: pkg.optionalDependencies, + permissions: ref.permissions, + prebuiltVariants: pkg.prebuiltVariants + }); -var _asyncToGenerator2; + lockfile[pattern] = obj; -function _load_asyncToGenerator() { - return _asyncToGenerator2 = _interopRequireDefault(__webpack_require__(1)); -} + if (remoteKey) { + seen.set(remoteKey, obj); + } + } -let buildActionsForCopy = (() => { - var _ref = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (queue, events, possibleExtraneous, reporter) { + return lockfile; + } +} +exports.default = Lockfile; - // - let build = (() => { - var _ref5 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (data) { - const src = data.src, - dest = data.dest, - type = data.type; +/***/ }), +/* 15 */, +/* 16 */, +/* 17 */ +/***/ (function(module, exports) { - const onFresh = data.onFresh || noop; - const onDone = data.onDone || noop; +module.exports = __webpack_require__(173); - // TODO https://github.com/yarnpkg/yarn/issues/3751 - // related to bundled dependencies handling - if (files.has(dest.toLowerCase())) { - reporter.verbose(`The case-insensitive file ${dest} shouldn't be copied twice in one bulk copy`); - } else { - files.add(dest.toLowerCase()); - } +/***/ }), +/* 18 */, +/* 19 */, +/* 20 */ +/***/ (function(module, exports, __webpack_require__) { - if (type === 'symlink') { - yield mkdirp((_path || _load_path()).default.dirname(dest)); - onFresh(); - actions.symlink.push({ - dest, - linkname: src - }); - onDone(); - return; - } +"use strict"; - if (events.ignoreBasenames.indexOf((_path || _load_path()).default.basename(src)) >= 0) { - // ignored file - return; - } - const srcStat = yield lstat(src); - let srcFiles; +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = nullify; +function nullify(obj = {}) { + if (Array.isArray(obj)) { + for (var _iterator = obj, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { + var _ref; - if (srcStat.isDirectory()) { - srcFiles = yield readdir(src); - } + if (_isArray) { + if (_i >= _iterator.length) break; + _ref = _iterator[_i++]; + } else { + _i = _iterator.next(); + if (_i.done) break; + _ref = _i.value; + } - let destStat; - try { - // try accessing the destination - destStat = yield lstat(dest); - } catch (e) { - // proceed if destination doesn't exist, otherwise error - if (e.code !== 'ENOENT') { - throw e; - } - } + const item = _ref; - // if destination exists - if (destStat) { - const bothSymlinks = srcStat.isSymbolicLink() && destStat.isSymbolicLink(); - const bothFolders = srcStat.isDirectory() && destStat.isDirectory(); - const bothFiles = srcStat.isFile() && destStat.isFile(); + nullify(item); + } + } else if (obj !== null && typeof obj === 'object' || typeof obj === 'function') { + Object.setPrototypeOf(obj, null); - // EINVAL access errors sometimes happen which shouldn't because node shouldn't be giving - // us modes that aren't valid. investigate this, it's generally safe to proceed. + // for..in can only be applied to 'object', not 'function' + if (typeof obj === 'object') { + for (const key in obj) { + nullify(obj[key]); + } + } + } - /* if (srcStat.mode !== destStat.mode) { - try { - await access(dest, srcStat.mode); - } catch (err) {} - } */ + return obj; +} - if (bothFiles && artifactFiles.has(dest)) { - // this file gets changed during build, likely by a custom install script. Don't bother checking it. - onDone(); - reporter.verbose(reporter.lang('verboseFileSkipArtifact', src)); - return; - } +/***/ }), +/* 21 */, +/* 22 */ +/***/ (function(module, exports) { - if (bothFiles && srcStat.size === destStat.size && (0, (_fsNormalized || _load_fsNormalized()).fileDatesEqual)(srcStat.mtime, destStat.mtime)) { - // we can safely assume this is the same file - onDone(); - reporter.verbose(reporter.lang('verboseFileSkip', src, dest, srcStat.size, +srcStat.mtime)); - return; - } +module.exports = __webpack_require__(162); - if (bothSymlinks) { - const srcReallink = yield readlink(src); - if (srcReallink === (yield readlink(dest))) { - // if both symlinks are the same then we can continue on - onDone(); - reporter.verbose(reporter.lang('verboseFileSkipSymlink', src, dest, srcReallink)); - return; - } - } +/***/ }), +/* 23 */ +/***/ (function(module, exports) { - if (bothFolders) { - // mark files that aren't in this folder as possibly extraneous - const destFiles = yield readdir(dest); - invariant(srcFiles, 'src files not initialised'); +var core = module.exports = { version: '2.5.7' }; +if (typeof __e == 'number') __e = core; // eslint-disable-line no-undef - for (var _iterator4 = destFiles, _isArray4 = Array.isArray(_iterator4), _i4 = 0, _iterator4 = _isArray4 ? _iterator4 : _iterator4[Symbol.iterator]();;) { - var _ref6; - if (_isArray4) { - if (_i4 >= _iterator4.length) break; - _ref6 = _iterator4[_i4++]; - } else { - _i4 = _iterator4.next(); - if (_i4.done) break; - _ref6 = _i4.value; - } +/***/ }), +/* 24 */, +/* 25 */, +/* 26 */, +/* 27 */ +/***/ (function(module, exports, __webpack_require__) { - const file = _ref6; +var isObject = __webpack_require__(34); +module.exports = function (it) { + if (!isObject(it)) throw TypeError(it + ' is not an object!'); + return it; +}; - if (srcFiles.indexOf(file) < 0) { - const loc = (_path || _load_path()).default.join(dest, file); - possibleExtraneous.add(loc); - if ((yield lstat(loc)).isDirectory()) { - for (var _iterator5 = yield readdir(loc), _isArray5 = Array.isArray(_iterator5), _i5 = 0, _iterator5 = _isArray5 ? _iterator5 : _iterator5[Symbol.iterator]();;) { - var _ref7; +/***/ }), +/* 28 */, +/* 29 */ +/***/ (function(module, exports, __webpack_require__) { - if (_isArray5) { - if (_i5 >= _iterator5.length) break; - _ref7 = _iterator5[_i5++]; - } else { - _i5 = _iterator5.next(); - if (_i5.done) break; - _ref7 = _i5.value; - } +"use strict"; - const file = _ref7; - possibleExtraneous.add((_path || _load_path()).default.join(loc, file)); - } - } - } - } - } - } +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.normalizePattern = normalizePattern; - if (destStat && destStat.isSymbolicLink()) { - yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(dest); - destStat = null; - } +/** + * Explode and normalize a pattern into its name and range. + */ - if (srcStat.isSymbolicLink()) { - onFresh(); - const linkname = yield readlink(src); - actions.symlink.push({ - dest, - linkname - }); - onDone(); - } else if (srcStat.isDirectory()) { - if (!destStat) { - reporter.verbose(reporter.lang('verboseFileFolder', dest)); - yield mkdirp(dest); - } +function normalizePattern(pattern) { + let hasVersion = false; + let range = 'latest'; + let name = pattern; - const destParts = dest.split((_path || _load_path()).default.sep); - while (destParts.length) { - files.add(destParts.join((_path || _load_path()).default.sep).toLowerCase()); - destParts.pop(); - } + // if we're a scope then remove the @ and add it back later + let isScoped = false; + if (name[0] === '@') { + isScoped = true; + name = name.slice(1); + } - // push all files to queue - invariant(srcFiles, 'src files not initialised'); - let remaining = srcFiles.length; - if (!remaining) { - onDone(); - } - for (var _iterator6 = srcFiles, _isArray6 = Array.isArray(_iterator6), _i6 = 0, _iterator6 = _isArray6 ? _iterator6 : _iterator6[Symbol.iterator]();;) { - var _ref8; + // take first part as the name + const parts = name.split('@'); + if (parts.length > 1) { + name = parts.shift(); + range = parts.join('@'); - if (_isArray6) { - if (_i6 >= _iterator6.length) break; - _ref8 = _iterator6[_i6++]; - } else { - _i6 = _iterator6.next(); - if (_i6.done) break; - _ref8 = _i6.value; - } + if (range) { + hasVersion = true; + } else { + range = '*'; + } + } - const file = _ref8; + // add back @ scope suffix + if (isScoped) { + name = `@${name}`; + } - queue.push({ - dest: (_path || _load_path()).default.join(dest, file), - onFresh, - onDone: function (_onDone) { - function onDone() { - return _onDone.apply(this, arguments); - } + return { name, range, hasVersion }; +} - onDone.toString = function () { - return _onDone.toString(); - }; +/***/ }), +/* 30 */, +/* 31 */ +/***/ (function(module, exports, __webpack_require__) { - return onDone; - }(function () { - if (--remaining === 0) { - onDone(); - } - }), - src: (_path || _load_path()).default.join(src, file) - }); - } - } else if (srcStat.isFile()) { - onFresh(); - actions.file.push({ - src, - dest, - atime: srcStat.atime, - mtime: srcStat.mtime, - mode: srcStat.mode - }); - onDone(); - } else { - throw new Error(`unsure how to copy this: ${src}`); - } - }); +var dP = __webpack_require__(50); +var createDesc = __webpack_require__(106); +module.exports = __webpack_require__(33) ? function (object, key, value) { + return dP.f(object, key, createDesc(1, value)); +} : function (object, key, value) { + object[key] = value; + return object; +}; - return function build(_x5) { - return _ref5.apply(this, arguments); - }; - })(); - const artifactFiles = new Set(events.artifactFiles || []); - const files = new Set(); +/***/ }), +/* 32 */ +/***/ (function(module, exports, __webpack_require__) { - // initialise events - for (var _iterator = queue, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { - var _ref2; +/* eslint-disable node/no-deprecated-api */ +var buffer = __webpack_require__(63) +var Buffer = buffer.Buffer - if (_isArray) { - if (_i >= _iterator.length) break; - _ref2 = _iterator[_i++]; - } else { - _i = _iterator.next(); - if (_i.done) break; - _ref2 = _i.value; - } +// alternative to using Object.keys for old browsers +function copyProps (src, dst) { + for (var key in src) { + dst[key] = src[key] + } +} +if (Buffer.from && Buffer.alloc && Buffer.allocUnsafe && Buffer.allocUnsafeSlow) { + module.exports = buffer +} else { + // Copy properties from require('buffer') + copyProps(buffer, exports) + exports.Buffer = SafeBuffer +} - const item = _ref2; +function SafeBuffer (arg, encodingOrOffset, length) { + return Buffer(arg, encodingOrOffset, length) +} - const onDone = item.onDone; - item.onDone = function () { - events.onProgress(item.dest); - if (onDone) { - onDone(); - } - }; - } - events.onStart(queue.length); +// Copy static methods from Buffer +copyProps(Buffer, SafeBuffer) - // start building actions - const actions = { - file: [], - symlink: [], - link: [] - }; +SafeBuffer.from = function (arg, encodingOrOffset, length) { + if (typeof arg === 'number') { + throw new TypeError('Argument must not be a number') + } + return Buffer(arg, encodingOrOffset, length) +} - // custom concurrency logic as we're always executing stacks of CONCURRENT_QUEUE_ITEMS queue items - // at a time due to the requirement to push items onto the queue - while (queue.length) { - const items = queue.splice(0, CONCURRENT_QUEUE_ITEMS); - yield Promise.all(items.map(build)); +SafeBuffer.alloc = function (size, fill, encoding) { + if (typeof size !== 'number') { + throw new TypeError('Argument must be a number') + } + var buf = Buffer(size) + if (fill !== undefined) { + if (typeof encoding === 'string') { + buf.fill(fill, encoding) + } else { + buf.fill(fill) } + } else { + buf.fill(0) + } + return buf +} - // simulate the existence of some files to prevent considering them extraneous - for (var _iterator2 = artifactFiles, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) { - var _ref3; - - if (_isArray2) { - if (_i2 >= _iterator2.length) break; - _ref3 = _iterator2[_i2++]; - } else { - _i2 = _iterator2.next(); - if (_i2.done) break; - _ref3 = _i2.value; - } +SafeBuffer.allocUnsafe = function (size) { + if (typeof size !== 'number') { + throw new TypeError('Argument must be a number') + } + return Buffer(size) +} - const file = _ref3; +SafeBuffer.allocUnsafeSlow = function (size) { + if (typeof size !== 'number') { + throw new TypeError('Argument must be a number') + } + return buffer.SlowBuffer(size) +} - if (possibleExtraneous.has(file)) { - reporter.verbose(reporter.lang('verboseFilePhantomExtraneous', file)); - possibleExtraneous.delete(file); - } - } - for (var _iterator3 = possibleExtraneous, _isArray3 = Array.isArray(_iterator3), _i3 = 0, _iterator3 = _isArray3 ? _iterator3 : _iterator3[Symbol.iterator]();;) { - var _ref4; +/***/ }), +/* 33 */ +/***/ (function(module, exports, __webpack_require__) { - if (_isArray3) { - if (_i3 >= _iterator3.length) break; - _ref4 = _iterator3[_i3++]; - } else { - _i3 = _iterator3.next(); - if (_i3.done) break; - _ref4 = _i3.value; - } +// Thank's IE8 for his funny defineProperty +module.exports = !__webpack_require__(85)(function () { + return Object.defineProperty({}, 'a', { get: function () { return 7; } }).a != 7; +}); - const loc = _ref4; - if (files.has(loc.toLowerCase())) { - possibleExtraneous.delete(loc); - } - } +/***/ }), +/* 34 */ +/***/ (function(module, exports) { - return actions; - }); +module.exports = function (it) { + return typeof it === 'object' ? it !== null : typeof it === 'function'; +}; - return function buildActionsForCopy(_x, _x2, _x3, _x4) { - return _ref.apply(this, arguments); - }; -})(); -let buildActionsForHardlink = (() => { - var _ref9 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (queue, events, possibleExtraneous, reporter) { +/***/ }), +/* 35 */ +/***/ (function(module, exports) { - // - let build = (() => { - var _ref13 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (data) { - const src = data.src, - dest = data.dest; +module.exports = {}; - const onFresh = data.onFresh || noop; - const onDone = data.onDone || noop; - if (files.has(dest.toLowerCase())) { - // Fixes issue https://github.com/yarnpkg/yarn/issues/2734 - // When bulk hardlinking we have A -> B structure that we want to hardlink to A1 -> B1, - // package-linker passes that modules A1 and B1 need to be hardlinked, - // the recursive linking algorithm of A1 ends up scheduling files in B1 to be linked twice which will case - // an exception. - onDone(); - return; - } - files.add(dest.toLowerCase()); - if (events.ignoreBasenames.indexOf((_path || _load_path()).default.basename(src)) >= 0) { - // ignored file - return; - } +/***/ }), +/* 36 */ +/***/ (function(module, exports) { - const srcStat = yield lstat(src); - let srcFiles; +module.exports = __webpack_require__(122); - if (srcStat.isDirectory()) { - srcFiles = yield readdir(src); - } +/***/ }), +/* 37 */, +/* 38 */, +/* 39 */, +/* 40 */ +/***/ (function(module, exports, __webpack_require__) { - const destExists = yield exists(dest); - if (destExists) { - const destStat = yield lstat(dest); +"use strict"; - const bothSymlinks = srcStat.isSymbolicLink() && destStat.isSymbolicLink(); - const bothFolders = srcStat.isDirectory() && destStat.isDirectory(); - const bothFiles = srcStat.isFile() && destStat.isFile(); - if (srcStat.mode !== destStat.mode) { - try { - yield access(dest, srcStat.mode); - } catch (err) { - // EINVAL access errors sometimes happen which shouldn't because node shouldn't be giving - // us modes that aren't valid. investigate this, it's generally safe to proceed. - reporter.verbose(err); - } - } +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.wait = wait; +exports.promisify = promisify; +exports.queue = queue; +function wait(delay) { + return new Promise(resolve => { + setTimeout(resolve, delay); + }); +} - if (bothFiles && artifactFiles.has(dest)) { - // this file gets changed during build, likely by a custom install script. Don't bother checking it. - onDone(); - reporter.verbose(reporter.lang('verboseFileSkipArtifact', src)); - return; - } +function promisify(fn, firstData) { + return function (...args) { + return new Promise(function (resolve, reject) { + args.push(function (err, ...result) { + let res = result; - // correct hardlink - if (bothFiles && srcStat.ino !== null && srcStat.ino === destStat.ino) { - onDone(); - reporter.verbose(reporter.lang('verboseFileSkip', src, dest, srcStat.ino)); - return; - } + if (result.length <= 1) { + res = result[0]; + } - if (bothSymlinks) { - const srcReallink = yield readlink(src); - if (srcReallink === (yield readlink(dest))) { - // if both symlinks are the same then we can continue on - onDone(); - reporter.verbose(reporter.lang('verboseFileSkipSymlink', src, dest, srcReallink)); - return; - } - } + if (firstData) { + res = err; + err = null; + } - if (bothFolders) { - // mark files that aren't in this folder as possibly extraneous - const destFiles = yield readdir(dest); - invariant(srcFiles, 'src files not initialised'); + if (err) { + reject(err); + } else { + resolve(res); + } + }); - for (var _iterator10 = destFiles, _isArray10 = Array.isArray(_iterator10), _i10 = 0, _iterator10 = _isArray10 ? _iterator10 : _iterator10[Symbol.iterator]();;) { - var _ref14; + fn.apply(null, args); + }); + }; +} - if (_isArray10) { - if (_i10 >= _iterator10.length) break; - _ref14 = _iterator10[_i10++]; - } else { - _i10 = _iterator10.next(); - if (_i10.done) break; - _ref14 = _i10.value; - } +function queue(arr, promiseProducer, concurrency = Infinity) { + concurrency = Math.min(concurrency, arr.length); - const file = _ref14; + // clone + arr = arr.slice(); - if (srcFiles.indexOf(file) < 0) { - const loc = (_path || _load_path()).default.join(dest, file); - possibleExtraneous.add(loc); + const results = []; + let total = arr.length; + if (!total) { + return Promise.resolve(results); + } - if ((yield lstat(loc)).isDirectory()) { - for (var _iterator11 = yield readdir(loc), _isArray11 = Array.isArray(_iterator11), _i11 = 0, _iterator11 = _isArray11 ? _iterator11 : _iterator11[Symbol.iterator]();;) { - var _ref15; + return new Promise((resolve, reject) => { + for (let i = 0; i < concurrency; i++) { + next(); + } - if (_isArray11) { - if (_i11 >= _iterator11.length) break; - _ref15 = _iterator11[_i11++]; - } else { - _i11 = _iterator11.next(); - if (_i11.done) break; - _ref15 = _i11.value; - } + function next() { + const item = arr.shift(); + const promise = promiseProducer(item); - const file = _ref15; + promise.then(function (result) { + results.push(result); - possibleExtraneous.add((_path || _load_path()).default.join(loc, file)); - } - } - } - } + total--; + if (total === 0) { + resolve(results); + } else { + if (arr.length) { + next(); } } + }, reject); + } + }); +} - if (srcStat.isSymbolicLink()) { - onFresh(); - const linkname = yield readlink(src); - actions.symlink.push({ - dest, - linkname - }); - onDone(); - } else if (srcStat.isDirectory()) { - reporter.verbose(reporter.lang('verboseFileFolder', dest)); - yield mkdirp(dest); +/***/ }), +/* 41 */ +/***/ (function(module, exports, __webpack_require__) { - const destParts = dest.split((_path || _load_path()).default.sep); - while (destParts.length) { - files.add(destParts.join((_path || _load_path()).default.sep).toLowerCase()); - destParts.pop(); - } +var global = __webpack_require__(11); +var core = __webpack_require__(23); +var ctx = __webpack_require__(48); +var hide = __webpack_require__(31); +var has = __webpack_require__(49); +var PROTOTYPE = 'prototype'; - // push all files to queue - invariant(srcFiles, 'src files not initialised'); - let remaining = srcFiles.length; - if (!remaining) { - onDone(); - } - for (var _iterator12 = srcFiles, _isArray12 = Array.isArray(_iterator12), _i12 = 0, _iterator12 = _isArray12 ? _iterator12 : _iterator12[Symbol.iterator]();;) { - var _ref16; +var $export = function (type, name, source) { + var IS_FORCED = type & $export.F; + var IS_GLOBAL = type & $export.G; + var IS_STATIC = type & $export.S; + var IS_PROTO = type & $export.P; + var IS_BIND = type & $export.B; + var IS_WRAP = type & $export.W; + var exports = IS_GLOBAL ? core : core[name] || (core[name] = {}); + var expProto = exports[PROTOTYPE]; + var target = IS_GLOBAL ? global : IS_STATIC ? global[name] : (global[name] || {})[PROTOTYPE]; + var key, own, out; + if (IS_GLOBAL) source = name; + for (key in source) { + // contains in native + own = !IS_FORCED && target && target[key] !== undefined; + if (own && has(exports, key)) continue; + // export native or passed + out = own ? target[key] : source[key]; + // prevent global pollution for namespaces + exports[key] = IS_GLOBAL && typeof target[key] != 'function' ? source[key] + // bind timers to global for call from export context + : IS_BIND && own ? ctx(out, global) + // wrap global constructors for prevent change them in library + : IS_WRAP && target[key] == out ? (function (C) { + var F = function (a, b, c) { + if (this instanceof C) { + switch (arguments.length) { + case 0: return new C(); + case 1: return new C(a); + case 2: return new C(a, b); + } return new C(a, b, c); + } return C.apply(this, arguments); + }; + F[PROTOTYPE] = C[PROTOTYPE]; + return F; + // make static versions for prototype methods + })(out) : IS_PROTO && typeof out == 'function' ? ctx(Function.call, out) : out; + // export proto methods to core.%CONSTRUCTOR%.methods.%NAME% + if (IS_PROTO) { + (exports.virtual || (exports.virtual = {}))[key] = out; + // export proto methods to core.%CONSTRUCTOR%.prototype.%NAME% + if (type & $export.R && expProto && !expProto[key]) hide(expProto, key, out); + } + } +}; +// type bitmap +$export.F = 1; // forced +$export.G = 2; // global +$export.S = 4; // static +$export.P = 8; // proto +$export.B = 16; // bind +$export.W = 32; // wrap +$export.U = 64; // safe +$export.R = 128; // real proto method for `library` +module.exports = $export; - if (_isArray12) { - if (_i12 >= _iterator12.length) break; - _ref16 = _iterator12[_i12++]; - } else { - _i12 = _iterator12.next(); - if (_i12.done) break; - _ref16 = _i12.value; - } - const file = _ref16; +/***/ }), +/* 42 */ +/***/ (function(module, exports, __webpack_require__) { - queue.push({ - onFresh, - src: (_path || _load_path()).default.join(src, file), - dest: (_path || _load_path()).default.join(dest, file), - onDone: function (_onDone2) { - function onDone() { - return _onDone2.apply(this, arguments); - } +try { + var util = __webpack_require__(2); + if (typeof util.inherits !== 'function') throw ''; + module.exports = util.inherits; +} catch (e) { + module.exports = __webpack_require__(224); +} - onDone.toString = function () { - return _onDone2.toString(); - }; - return onDone; - }(function () { - if (--remaining === 0) { - onDone(); - } - }) - }); - } - } else if (srcStat.isFile()) { - onFresh(); - actions.link.push({ - src, - dest, - removeDest: destExists - }); - onDone(); - } else { - throw new Error(`unsure how to copy this: ${src}`); - } - }); +/***/ }), +/* 43 */, +/* 44 */, +/* 45 */ +/***/ (function(module, exports, __webpack_require__) { - return function build(_x10) { - return _ref13.apply(this, arguments); - }; - })(); +"use strict"; - const artifactFiles = new Set(events.artifactFiles || []); - const files = new Set(); - // initialise events - for (var _iterator7 = queue, _isArray7 = Array.isArray(_iterator7), _i7 = 0, _iterator7 = _isArray7 ? _iterator7 : _iterator7[Symbol.iterator]();;) { - var _ref10; +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.home = undefined; - if (_isArray7) { - if (_i7 >= _iterator7.length) break; - _ref10 = _iterator7[_i7++]; - } else { - _i7 = _iterator7.next(); - if (_i7.done) break; - _ref10 = _i7.value; - } +var _rootUser; - const item = _ref10; +function _load_rootUser() { + return _rootUser = _interopRequireDefault(__webpack_require__(169)); +} - const onDone = item.onDone || noop; - item.onDone = function () { - events.onProgress(item.dest); - onDone(); - }; - } - events.onStart(queue.length); +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - // start building actions - const actions = { - file: [], - symlink: [], - link: [] - }; +const path = __webpack_require__(0); - // custom concurrency logic as we're always executing stacks of CONCURRENT_QUEUE_ITEMS queue items - // at a time due to the requirement to push items onto the queue - while (queue.length) { - const items = queue.splice(0, CONCURRENT_QUEUE_ITEMS); - yield Promise.all(items.map(build)); - } +const home = exports.home = __webpack_require__(36).homedir(); - // simulate the existence of some files to prevent considering them extraneous - for (var _iterator8 = artifactFiles, _isArray8 = Array.isArray(_iterator8), _i8 = 0, _iterator8 = _isArray8 ? _iterator8 : _iterator8[Symbol.iterator]();;) { - var _ref11; +const userHomeDir = (_rootUser || _load_rootUser()).default ? path.resolve('/usr/local/share') : home; - if (_isArray8) { - if (_i8 >= _iterator8.length) break; - _ref11 = _iterator8[_i8++]; - } else { - _i8 = _iterator8.next(); - if (_i8.done) break; - _ref11 = _i8.value; - } +exports.default = userHomeDir; - const file = _ref11; +/***/ }), +/* 46 */ +/***/ (function(module, exports) { - if (possibleExtraneous.has(file)) { - reporter.verbose(reporter.lang('verboseFilePhantomExtraneous', file)); - possibleExtraneous.delete(file); - } - } +module.exports = function (it) { + if (typeof it != 'function') throw TypeError(it + ' is not a function!'); + return it; +}; - for (var _iterator9 = possibleExtraneous, _isArray9 = Array.isArray(_iterator9), _i9 = 0, _iterator9 = _isArray9 ? _iterator9 : _iterator9[Symbol.iterator]();;) { - var _ref12; - if (_isArray9) { - if (_i9 >= _iterator9.length) break; - _ref12 = _iterator9[_i9++]; - } else { - _i9 = _iterator9.next(); - if (_i9.done) break; - _ref12 = _i9.value; - } +/***/ }), +/* 47 */ +/***/ (function(module, exports) { - const loc = _ref12; +var toString = {}.toString; - if (files.has(loc.toLowerCase())) { - possibleExtraneous.delete(loc); - } - } +module.exports = function (it) { + return toString.call(it).slice(8, -1); +}; - return actions; - }); - return function buildActionsForHardlink(_x6, _x7, _x8, _x9) { - return _ref9.apply(this, arguments); - }; -})(); +/***/ }), +/* 48 */ +/***/ (function(module, exports, __webpack_require__) { -let copyBulk = exports.copyBulk = (() => { - var _ref17 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (queue, reporter, _events) { - const events = { - onStart: _events && _events.onStart || noop, - onProgress: _events && _events.onProgress || noop, - possibleExtraneous: _events ? _events.possibleExtraneous : new Set(), - ignoreBasenames: _events && _events.ignoreBasenames || [], - artifactFiles: _events && _events.artifactFiles || [] +// optional / simple context binding +var aFunction = __webpack_require__(46); +module.exports = function (fn, that, length) { + aFunction(fn); + if (that === undefined) return fn; + switch (length) { + case 1: return function (a) { + return fn.call(that, a); + }; + case 2: return function (a, b) { + return fn.call(that, a, b); + }; + case 3: return function (a, b, c) { + return fn.call(that, a, b, c); }; + } + return function (/* ...args */) { + return fn.apply(that, arguments); + }; +}; - const actions = yield buildActionsForCopy(queue, events, events.possibleExtraneous, reporter); - events.onStart(actions.file.length + actions.symlink.length + actions.link.length); - const fileActions = actions.file; +/***/ }), +/* 49 */ +/***/ (function(module, exports) { - const currentlyWriting = new Map(); +var hasOwnProperty = {}.hasOwnProperty; +module.exports = function (it, key) { + return hasOwnProperty.call(it, key); +}; - yield (_promise || _load_promise()).queue(fileActions, (() => { - var _ref18 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (data) { - let writePromise; - while (writePromise = currentlyWriting.get(data.dest)) { - yield writePromise; - } - reporter.verbose(reporter.lang('verboseFileCopy', data.src, data.dest)); - const copier = (0, (_fsNormalized || _load_fsNormalized()).copyFile)(data, function () { - return currentlyWriting.delete(data.dest); - }); - currentlyWriting.set(data.dest, copier); - events.onProgress(data.dest); - return copier; - }); +/***/ }), +/* 50 */ +/***/ (function(module, exports, __webpack_require__) { - return function (_x14) { - return _ref18.apply(this, arguments); - }; - })(), CONCURRENT_QUEUE_ITEMS); +var anObject = __webpack_require__(27); +var IE8_DOM_DEFINE = __webpack_require__(184); +var toPrimitive = __webpack_require__(201); +var dP = Object.defineProperty; - // we need to copy symlinks last as they could reference files we were copying - const symlinkActions = actions.symlink; - yield (_promise || _load_promise()).queue(symlinkActions, function (data) { - const linkname = (_path || _load_path()).default.resolve((_path || _load_path()).default.dirname(data.dest), data.linkname); - reporter.verbose(reporter.lang('verboseFileSymlink', data.dest, linkname)); - return symlink(linkname, data.dest); - }); - }); +exports.f = __webpack_require__(33) ? Object.defineProperty : function defineProperty(O, P, Attributes) { + anObject(O); + P = toPrimitive(P, true); + anObject(Attributes); + if (IE8_DOM_DEFINE) try { + return dP(O, P, Attributes); + } catch (e) { /* empty */ } + if ('get' in Attributes || 'set' in Attributes) throw TypeError('Accessors not supported!'); + if ('value' in Attributes) O[P] = Attributes.value; + return O; +}; - return function copyBulk(_x11, _x12, _x13) { - return _ref17.apply(this, arguments); - }; -})(); -let hardlinkBulk = exports.hardlinkBulk = (() => { - var _ref19 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (queue, reporter, _events) { - const events = { - onStart: _events && _events.onStart || noop, - onProgress: _events && _events.onProgress || noop, - possibleExtraneous: _events ? _events.possibleExtraneous : new Set(), - artifactFiles: _events && _events.artifactFiles || [], - ignoreBasenames: [] - }; +/***/ }), +/* 51 */, +/* 52 */, +/* 53 */, +/* 54 */ +/***/ (function(module, exports) { - const actions = yield buildActionsForHardlink(queue, events, events.possibleExtraneous, reporter); - events.onStart(actions.file.length + actions.symlink.length + actions.link.length); +module.exports = __webpack_require__(164); - const fileActions = actions.link; +/***/ }), +/* 55 */ +/***/ (function(module, exports, __webpack_require__) { - yield (_promise || _load_promise()).queue(fileActions, (() => { - var _ref20 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (data) { - reporter.verbose(reporter.lang('verboseFileLink', data.src, data.dest)); - if (data.removeDest) { - yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(data.dest); - } - yield link(data.src, data.dest); - }); +"use strict"; - return function (_x18) { - return _ref20.apply(this, arguments); - }; - })(), CONCURRENT_QUEUE_ITEMS); - // we need to copy symlinks last as they could reference files we were copying - const symlinkActions = actions.symlink; - yield (_promise || _load_promise()).queue(symlinkActions, function (data) { - const linkname = (_path || _load_path()).default.resolve((_path || _load_path()).default.dirname(data.dest), data.linkname); - reporter.verbose(reporter.lang('verboseFileSymlink', data.dest, linkname)); - return symlink(linkname, data.dest); - }); - }); +const Buffer = __webpack_require__(32).Buffer - return function hardlinkBulk(_x15, _x16, _x17) { - return _ref19.apply(this, arguments); - }; -})(); +const crypto = __webpack_require__(9) +const Transform = __webpack_require__(17).Transform -let readFileAny = exports.readFileAny = (() => { - var _ref21 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (files) { - for (var _iterator13 = files, _isArray13 = Array.isArray(_iterator13), _i13 = 0, _iterator13 = _isArray13 ? _iterator13 : _iterator13[Symbol.iterator]();;) { - var _ref22; +const SPEC_ALGORITHMS = ['sha256', 'sha384', 'sha512'] - if (_isArray13) { - if (_i13 >= _iterator13.length) break; - _ref22 = _iterator13[_i13++]; - } else { - _i13 = _iterator13.next(); - if (_i13.done) break; - _ref22 = _i13.value; - } +const BASE64_REGEX = /^[a-z0-9+/]+(?:=?=?)$/i +const SRI_REGEX = /^([^-]+)-([^?]+)([?\S*]*)$/ +const STRICT_SRI_REGEX = /^([^-]+)-([A-Za-z0-9+/=]{44,88})(\?[\x21-\x7E]*)*$/ +const VCHAR_REGEX = /^[\x21-\x7E]+$/ - const file = _ref22; +class Hash { + get isHash () { return true } + constructor (hash, opts) { + const strict = !!(opts && opts.strict) + this.source = hash.trim() + // 3.1. Integrity metadata (called "Hash" by ssri) + // https://w3c.github.io/webappsec-subresource-integrity/#integrity-metadata-description + const match = this.source.match( + strict + ? STRICT_SRI_REGEX + : SRI_REGEX + ) + if (!match) { return } + if (strict && !SPEC_ALGORITHMS.some(a => a === match[1])) { return } + this.algorithm = match[1] + this.digest = match[2] - if (yield exists(file)) { - return readFile(file); + const rawOpts = match[3] + this.options = rawOpts ? rawOpts.slice(1).split('?') : [] + } + hexDigest () { + return this.digest && Buffer.from(this.digest, 'base64').toString('hex') + } + toJSON () { + return this.toString() + } + toString (opts) { + if (opts && opts.strict) { + // Strict mode enforces the standard as close to the foot of the + // letter as it can. + if (!( + // The spec has very restricted productions for algorithms. + // https://www.w3.org/TR/CSP2/#source-list-syntax + SPEC_ALGORITHMS.some(x => x === this.algorithm) && + // Usually, if someone insists on using a "different" base64, we + // leave it as-is, since there's multiple standards, and the + // specified is not a URL-safe variant. + // https://www.w3.org/TR/CSP2/#base64_value + this.digest.match(BASE64_REGEX) && + // Option syntax is strictly visual chars. + // https://w3c.github.io/webappsec-subresource-integrity/#grammardef-option-expression + // https://tools.ietf.org/html/rfc5234#appendix-B.1 + (this.options || []).every(opt => opt.match(VCHAR_REGEX)) + )) { + return '' } } - return null; - }); - - return function readFileAny(_x19) { - return _ref21.apply(this, arguments); - }; -})(); + const options = this.options && this.options.length + ? `?${this.options.join('?')}` + : '' + return `${this.algorithm}-${this.digest}${options}` + } +} -let readJson = exports.readJson = (() => { - var _ref23 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (loc) { - return (yield readJsonAndFile(loc)).object; - }); +class Integrity { + get isIntegrity () { return true } + toJSON () { + return this.toString() + } + toString (opts) { + opts = opts || {} + let sep = opts.sep || ' ' + if (opts.strict) { + // Entries must be separated by whitespace, according to spec. + sep = sep.replace(/\S+/g, ' ') + } + return Object.keys(this).map(k => { + return this[k].map(hash => { + return Hash.prototype.toString.call(hash, opts) + }).filter(x => x.length).join(sep) + }).filter(x => x.length).join(sep) + } + concat (integrity, opts) { + const other = typeof integrity === 'string' + ? integrity + : stringify(integrity, opts) + return parse(`${this.toString(opts)} ${other}`, opts) + } + hexDigest () { + return parse(this, {single: true}).hexDigest() + } + match (integrity, opts) { + const other = parse(integrity, opts) + const algo = other.pickAlgorithm(opts) + return ( + this[algo] && + other[algo] && + this[algo].find(hash => + other[algo].find(otherhash => + hash.digest === otherhash.digest + ) + ) + ) || false + } + pickAlgorithm (opts) { + const pickAlgorithm = (opts && opts.pickAlgorithm) || getPrioritizedHash + const keys = Object.keys(this) + if (!keys.length) { + throw new Error(`No algorithms available for ${ + JSON.stringify(this.toString()) + }`) + } + return keys.reduce((acc, algo) => { + return pickAlgorithm(acc, algo) || acc + }) + } +} - return function readJson(_x20) { - return _ref23.apply(this, arguments); - }; -})(); +module.exports.parse = parse +function parse (sri, opts) { + opts = opts || {} + if (typeof sri === 'string') { + return _parse(sri, opts) + } else if (sri.algorithm && sri.digest) { + const fullSri = new Integrity() + fullSri[sri.algorithm] = [sri] + return _parse(stringify(fullSri, opts), opts) + } else { + return _parse(stringify(sri, opts), opts) + } +} -let readJsonAndFile = exports.readJsonAndFile = (() => { - var _ref24 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (loc) { - const file = yield readFile(loc); - try { - return { - object: (0, (_map || _load_map()).default)(JSON.parse(stripBOM(file))), - content: file - }; - } catch (err) { - err.message = `${loc}: ${err.message}`; - throw err; +function _parse (integrity, opts) { + // 3.4.3. Parse metadata + // https://w3c.github.io/webappsec-subresource-integrity/#parse-metadata + if (opts.single) { + return new Hash(integrity, opts) + } + return integrity.trim().split(/\s+/).reduce((acc, string) => { + const hash = new Hash(string, opts) + if (hash.algorithm && hash.digest) { + const algo = hash.algorithm + if (!acc[algo]) { acc[algo] = [] } + acc[algo].push(hash) } - }); - - return function readJsonAndFile(_x21) { - return _ref24.apply(this, arguments); - }; -})(); + return acc + }, new Integrity()) +} -let find = exports.find = (() => { - var _ref25 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (filename, dir) { - const parts = dir.split((_path || _load_path()).default.sep); +module.exports.stringify = stringify +function stringify (obj, opts) { + if (obj.algorithm && obj.digest) { + return Hash.prototype.toString.call(obj, opts) + } else if (typeof obj === 'string') { + return stringify(parse(obj, opts), opts) + } else { + return Integrity.prototype.toString.call(obj, opts) + } +} - while (parts.length) { - const loc = parts.concat(filename).join((_path || _load_path()).default.sep); +module.exports.fromHex = fromHex +function fromHex (hexDigest, algorithm, opts) { + const optString = (opts && opts.options && opts.options.length) + ? `?${opts.options.join('?')}` + : '' + return parse( + `${algorithm}-${ + Buffer.from(hexDigest, 'hex').toString('base64') + }${optString}`, opts + ) +} - if (yield exists(loc)) { - return loc; - } else { - parts.pop(); - } +module.exports.fromData = fromData +function fromData (data, opts) { + opts = opts || {} + const algorithms = opts.algorithms || ['sha512'] + const optString = opts.options && opts.options.length + ? `?${opts.options.join('?')}` + : '' + return algorithms.reduce((acc, algo) => { + const digest = crypto.createHash(algo).update(data).digest('base64') + const hash = new Hash( + `${algo}-${digest}${optString}`, + opts + ) + if (hash.algorithm && hash.digest) { + const algo = hash.algorithm + if (!acc[algo]) { acc[algo] = [] } + acc[algo].push(hash) } + return acc + }, new Integrity()) +} - return false; - }); - - return function find(_x22, _x23) { - return _ref25.apply(this, arguments); - }; -})(); +module.exports.fromStream = fromStream +function fromStream (stream, opts) { + opts = opts || {} + const P = opts.Promise || Promise + const istream = integrityStream(opts) + return new P((resolve, reject) => { + stream.pipe(istream) + stream.on('error', reject) + istream.on('error', reject) + let sri + istream.on('integrity', s => { sri = s }) + istream.on('end', () => resolve(sri)) + istream.on('data', () => {}) + }) +} -let symlink = exports.symlink = (() => { - var _ref26 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (src, dest) { - try { - const stats = yield lstat(dest); - if (stats.isSymbolicLink()) { - const resolved = yield realpath(dest); - if (resolved === src) { - return; +module.exports.checkData = checkData +function checkData (data, sri, opts) { + opts = opts || {} + sri = parse(sri, opts) + if (!Object.keys(sri).length) { + if (opts.error) { + throw Object.assign( + new Error('No valid integrity hashes to check against'), { + code: 'EINTEGRITY' } - } - } catch (err) { - if (err.code !== 'ENOENT') { - throw err; - } + ) + } else { + return false } - // We use rimraf for unlink which never throws an ENOENT on missing target - yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(dest); + } + const algorithm = sri.pickAlgorithm(opts) + const digest = crypto.createHash(algorithm).update(data).digest('base64') + const newSri = parse({algorithm, digest}) + const match = newSri.match(sri, opts) + if (match || !opts.error) { + return match + } else if (typeof opts.size === 'number' && (data.length !== opts.size)) { + const err = new Error(`data size mismatch when checking ${sri}.\n Wanted: ${opts.size}\n Found: ${data.length}`) + err.code = 'EBADSIZE' + err.found = data.length + err.expected = opts.size + err.sri = sri + throw err + } else { + const err = new Error(`Integrity checksum failed when using ${algorithm}: Wanted ${sri}, but got ${newSri}. (${data.length} bytes)`) + err.code = 'EINTEGRITY' + err.found = newSri + err.expected = sri + err.algorithm = algorithm + err.sri = sri + throw err + } +} - if (process.platform === 'win32') { - // use directory junctions if possible on win32, this requires absolute paths - yield fsSymlink(src, dest, 'junction'); +module.exports.checkStream = checkStream +function checkStream (stream, sri, opts) { + opts = opts || {} + const P = opts.Promise || Promise + const checker = integrityStream(Object.assign({}, opts, { + integrity: sri + })) + return new P((resolve, reject) => { + stream.pipe(checker) + stream.on('error', reject) + checker.on('error', reject) + let sri + checker.on('verified', s => { sri = s }) + checker.on('end', () => resolve(sri)) + checker.on('data', () => {}) + }) +} + +module.exports.integrityStream = integrityStream +function integrityStream (opts) { + opts = opts || {} + // For verification + const sri = opts.integrity && parse(opts.integrity, opts) + const goodSri = sri && Object.keys(sri).length + const algorithm = goodSri && sri.pickAlgorithm(opts) + const digests = goodSri && sri[algorithm] + // Calculating stream + const algorithms = Array.from( + new Set( + (opts.algorithms || ['sha512']) + .concat(algorithm ? [algorithm] : []) + ) + ) + const hashes = algorithms.map(crypto.createHash) + let streamSize = 0 + const stream = new Transform({ + transform (chunk, enc, cb) { + streamSize += chunk.length + hashes.forEach(h => h.update(chunk, enc)) + cb(null, chunk, enc) + } + }).on('end', () => { + const optString = (opts.options && opts.options.length) + ? `?${opts.options.join('?')}` + : '' + const newSri = parse(hashes.map((h, i) => { + return `${algorithms[i]}-${h.digest('base64')}${optString}` + }).join(' '), opts) + // Integrity verification mode + const match = goodSri && newSri.match(sri, opts) + if (typeof opts.size === 'number' && streamSize !== opts.size) { + const err = new Error(`stream size mismatch when checking ${sri}.\n Wanted: ${opts.size}\n Found: ${streamSize}`) + err.code = 'EBADSIZE' + err.found = streamSize + err.expected = opts.size + err.sri = sri + stream.emit('error', err) + } else if (opts.integrity && !match) { + const err = new Error(`${sri} integrity checksum failed when using ${algorithm}: wanted ${digests} but got ${newSri}. (${streamSize} bytes)`) + err.code = 'EINTEGRITY' + err.found = newSri + err.expected = digests + err.algorithm = algorithm + err.sri = sri + stream.emit('error', err) } else { - // use relative paths otherwise which will be retained if the directory is moved - let relative; - try { - relative = (_path || _load_path()).default.relative((_fs || _load_fs()).default.realpathSync((_path || _load_path()).default.dirname(dest)), (_fs || _load_fs()).default.realpathSync(src)); - } catch (err) { - if (err.code !== 'ENOENT') { - throw err; - } - relative = (_path || _load_path()).default.relative((_path || _load_path()).default.dirname(dest), src); - } - // When path.relative returns an empty string for the current directory, we should instead use - // '.', which is a valid fs.symlink target. - yield fsSymlink(relative || '.', dest); + stream.emit('size', streamSize) + stream.emit('integrity', newSri) + match && stream.emit('verified', match) } - }); + }) + return stream +} - return function symlink(_x24, _x25) { - return _ref26.apply(this, arguments); - }; -})(); +module.exports.create = createIntegrity +function createIntegrity (opts) { + opts = opts || {} + const algorithms = opts.algorithms || ['sha512'] + const optString = opts.options && opts.options.length + ? `?${opts.options.join('?')}` + : '' -let walk = exports.walk = (() => { - var _ref27 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (dir, relativeDir, ignoreBasenames = new Set()) { - let files = []; + const hashes = algorithms.map(crypto.createHash) - let filenames = yield readdir(dir); - if (ignoreBasenames.size) { - filenames = filenames.filter(function (name) { - return !ignoreBasenames.has(name); - }); + return { + update: function (chunk, enc) { + hashes.forEach(h => h.update(chunk, enc)) + return this + }, + digest: function (enc) { + const integrity = algorithms.reduce((acc, algo) => { + const digest = hashes.shift().digest('base64') + const hash = new Hash( + `${algo}-${digest}${optString}`, + opts + ) + if (hash.algorithm && hash.digest) { + const algo = hash.algorithm + if (!acc[algo]) { acc[algo] = [] } + acc[algo].push(hash) + } + return acc + }, new Integrity()) + + return integrity } + } +} - for (var _iterator14 = filenames, _isArray14 = Array.isArray(_iterator14), _i14 = 0, _iterator14 = _isArray14 ? _iterator14 : _iterator14[Symbol.iterator]();;) { - var _ref28; +const NODE_HASHES = new Set(crypto.getHashes()) - if (_isArray14) { - if (_i14 >= _iterator14.length) break; - _ref28 = _iterator14[_i14++]; - } else { - _i14 = _iterator14.next(); - if (_i14.done) break; - _ref28 = _i14.value; - } +// This is a Best Effort™ at a reasonable priority for hash algos +const DEFAULT_PRIORITY = [ + 'md5', 'whirlpool', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512', + // TODO - it's unclear _which_ of these Node will actually use as its name + // for the algorithm, so we guesswork it based on the OpenSSL names. + 'sha3', + 'sha3-256', 'sha3-384', 'sha3-512', + 'sha3_256', 'sha3_384', 'sha3_512' +].filter(algo => NODE_HASHES.has(algo)) - const name = _ref28; +function getPrioritizedHash (algo1, algo2) { + return DEFAULT_PRIORITY.indexOf(algo1.toLowerCase()) >= DEFAULT_PRIORITY.indexOf(algo2.toLowerCase()) + ? algo1 + : algo2 +} - const relative = relativeDir ? (_path || _load_path()).default.join(relativeDir, name) : name; - const loc = (_path || _load_path()).default.join(dir, name); - const stat = yield lstat(loc); - files.push({ - relative, - basename: name, - absolute: loc, - mtime: +stat.mtime - }); +/***/ }), +/* 56 */, +/* 57 */, +/* 58 */, +/* 59 */, +/* 60 */ +/***/ (function(module, exports, __webpack_require__) { - if (stat.isDirectory()) { - files = files.concat((yield walk(loc, relative, ignoreBasenames))); - } - } +module.exports = minimatch +minimatch.Minimatch = Minimatch - return files; - }); +var path = { sep: '/' } +try { + path = __webpack_require__(0) +} catch (er) {} - return function walk(_x26, _x27) { - return _ref27.apply(this, arguments); - }; -})(); +var GLOBSTAR = minimatch.GLOBSTAR = Minimatch.GLOBSTAR = {} +var expand = __webpack_require__(175) -let getFileSizeOnDisk = exports.getFileSizeOnDisk = (() => { - var _ref29 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (loc) { - const stat = yield lstat(loc); - const size = stat.size, - blockSize = stat.blksize; +var plTypes = { + '!': { open: '(?:(?!(?:', close: '))[^/]*?)'}, + '?': { open: '(?:', close: ')?' }, + '+': { open: '(?:', close: ')+' }, + '*': { open: '(?:', close: ')*' }, + '@': { open: '(?:', close: ')' } +} +// any single thing other than / +// don't need to escape / when using new RegExp() +var qmark = '[^/]' - return Math.ceil(size / blockSize) * blockSize; - }); +// * => any number of characters +var star = qmark + '*?' - return function getFileSizeOnDisk(_x28) { - return _ref29.apply(this, arguments); - }; -})(); +// ** when dots are allowed. Anything goes, except .. and . +// not (^ or / followed by one or two dots followed by $ or /), +// followed by anything, any number of times. +var twoStarDot = '(?:(?!(?:\\\/|^)(?:\\.{1,2})($|\\\/)).)*?' -let getEolFromFile = (() => { - var _ref30 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (path) { - if (!(yield exists(path))) { - return undefined; - } +// not a ^ or / followed by a dot, +// followed by anything, any number of times. +var twoStarNoDot = '(?:(?!(?:\\\/|^)\\.).)*?' - const buffer = yield readFileBuffer(path); +// characters that need to be escaped in RegExp. +var reSpecials = charSet('().*{}+?[]^$\\!') - for (let i = 0; i < buffer.length; ++i) { - if (buffer[i] === cr) { - return '\r\n'; - } - if (buffer[i] === lf) { - return '\n'; - } - } - return undefined; - }); +// "abc" -> { a:true, b:true, c:true } +function charSet (s) { + return s.split('').reduce(function (set, c) { + set[c] = true + return set + }, {}) +} - return function getEolFromFile(_x29) { - return _ref30.apply(this, arguments); - }; -})(); +// normalizes slashes. +var slashSplit = /\/+/ -let writeFilePreservingEol = exports.writeFilePreservingEol = (() => { - var _ref31 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (path, data) { - const eol = (yield getEolFromFile(path)) || (_os || _load_os()).default.EOL; - if (eol !== '\n') { - data = data.replace(/\n/g, eol); - } - yield writeFile(path, data); - }); +minimatch.filter = filter +function filter (pattern, options) { + options = options || {} + return function (p, i, list) { + return minimatch(p, pattern, options) + } +} - return function writeFilePreservingEol(_x30, _x31) { - return _ref31.apply(this, arguments); - }; -})(); +function ext (a, b) { + a = a || {} + b = b || {} + var t = {} + Object.keys(b).forEach(function (k) { + t[k] = b[k] + }) + Object.keys(a).forEach(function (k) { + t[k] = a[k] + }) + return t +} -let hardlinksWork = exports.hardlinksWork = (() => { - var _ref32 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (dir) { - const filename = 'test-file' + Math.random(); - const file = (_path || _load_path()).default.join(dir, filename); - const fileLink = (_path || _load_path()).default.join(dir, filename + '-link'); - try { - yield writeFile(file, 'test'); - yield link(file, fileLink); - } catch (err) { - return false; - } finally { - yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(file); - yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(fileLink); - } - return true; - }); +minimatch.defaults = function (def) { + if (!def || !Object.keys(def).length) return minimatch - return function hardlinksWork(_x32) { - return _ref32.apply(this, arguments); - }; -})(); + var orig = minimatch -// not a strict polyfill for Node's fs.mkdtemp + var m = function minimatch (p, pattern, options) { + return orig.minimatch(p, pattern, ext(def, options)) + } + m.Minimatch = function Minimatch (pattern, options) { + return new orig.Minimatch(pattern, ext(def, options)) + } -let makeTempDir = exports.makeTempDir = (() => { - var _ref33 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (prefix) { - const dir = (_path || _load_path()).default.join((_os || _load_os()).default.tmpdir(), `yarn-${prefix || ''}-${Date.now()}-${Math.random()}`); - yield (0, (_fsNormalized || _load_fsNormalized()).unlink)(dir); - yield mkdirp(dir); - return dir; - }); + return m +} - return function makeTempDir(_x33) { - return _ref33.apply(this, arguments); - }; -})(); +Minimatch.defaults = function (def) { + if (!def || !Object.keys(def).length) return Minimatch + return minimatch.defaults(def).Minimatch +} -let readFirstAvailableStream = exports.readFirstAvailableStream = (() => { - var _ref34 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (paths) { - for (var _iterator15 = paths, _isArray15 = Array.isArray(_iterator15), _i15 = 0, _iterator15 = _isArray15 ? _iterator15 : _iterator15[Symbol.iterator]();;) { - var _ref35; +function minimatch (p, pattern, options) { + if (typeof pattern !== 'string') { + throw new TypeError('glob pattern string required') + } - if (_isArray15) { - if (_i15 >= _iterator15.length) break; - _ref35 = _iterator15[_i15++]; - } else { - _i15 = _iterator15.next(); - if (_i15.done) break; - _ref35 = _i15.value; - } + if (!options) options = {} - const path = _ref35; + // shortcut: comments match nothing. + if (!options.nocomment && pattern.charAt(0) === '#') { + return false + } - try { - const fd = yield open(path, 'r'); - return (_fs || _load_fs()).default.createReadStream(path, { fd }); - } catch (err) { - // Try the next one - } - } - return null; - }); + // "" only matches "" + if (pattern.trim() === '') return p === '' - return function readFirstAvailableStream(_x34) { - return _ref34.apply(this, arguments); - }; -})(); + return new Minimatch(pattern, options).match(p) +} -let getFirstSuitableFolder = exports.getFirstSuitableFolder = (() => { - var _ref36 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (paths, mode = constants.W_OK | constants.X_OK) { - const result = { - skipped: [], - folder: null - }; +function Minimatch (pattern, options) { + if (!(this instanceof Minimatch)) { + return new Minimatch(pattern, options) + } - for (var _iterator16 = paths, _isArray16 = Array.isArray(_iterator16), _i16 = 0, _iterator16 = _isArray16 ? _iterator16 : _iterator16[Symbol.iterator]();;) { - var _ref37; + if (typeof pattern !== 'string') { + throw new TypeError('glob pattern string required') + } - if (_isArray16) { - if (_i16 >= _iterator16.length) break; - _ref37 = _iterator16[_i16++]; - } else { - _i16 = _iterator16.next(); - if (_i16.done) break; - _ref37 = _i16.value; - } + if (!options) options = {} + pattern = pattern.trim() - const folder = _ref37; + // windows support: need to use /, not \ + if (path.sep !== '/') { + pattern = pattern.split(path.sep).join('/') + } - try { - yield mkdirp(folder); - yield access(folder, mode); + this.options = options + this.set = [] + this.pattern = pattern + this.regexp = null + this.negate = false + this.comment = false + this.empty = false - result.folder = folder; + // make the set of regexps etc. + this.make() +} - return result; - } catch (error) { - result.skipped.push({ - error, - folder - }); - } - } - return result; - }); +Minimatch.prototype.debug = function () {} - return function getFirstSuitableFolder(_x35) { - return _ref36.apply(this, arguments); - }; -})(); +Minimatch.prototype.make = make +function make () { + // don't do it more than once. + if (this._made) return -exports.copy = copy; -exports.readFile = readFile; -exports.readFileRaw = readFileRaw; -exports.normalizeOS = normalizeOS; + var pattern = this.pattern + var options = this.options -var _fs; + // empty patterns and comments match nothing. + if (!options.nocomment && pattern.charAt(0) === '#') { + this.comment = true + return + } + if (!pattern) { + this.empty = true + return + } -function _load_fs() { - return _fs = _interopRequireDefault(__webpack_require__(3)); -} + // step 1: figure out negation, etc. + this.parseNegate() -var _glob; + // step 2: expand braces + var set = this.globSet = this.braceExpand() -function _load_glob() { - return _glob = _interopRequireDefault(__webpack_require__(75)); -} + if (options.debug) this.debug = console.error -var _os; + this.debug(this.pattern, set) -function _load_os() { - return _os = _interopRequireDefault(__webpack_require__(36)); -} + // step 3: now we have a set, so turn each one into a series of path-portion + // matching patterns. + // These will be regexps, except in the case of "**", which is + // set to the GLOBSTAR object for globstar behavior, + // and will not contain any / characters + set = this.globParts = set.map(function (s) { + return s.split(slashSplit) + }) -var _path; + this.debug(this.pattern, set) -function _load_path() { - return _path = _interopRequireDefault(__webpack_require__(0)); -} + // glob --> regexps + set = set.map(function (s, si, set) { + return s.map(this.parse, this) + }, this) -var _blockingQueue; + this.debug(this.pattern, set) -function _load_blockingQueue() { - return _blockingQueue = _interopRequireDefault(__webpack_require__(84)); -} + // filter out everything that didn't compile properly. + set = set.filter(function (s) { + return s.indexOf(false) === -1 + }) -var _promise; + this.debug(this.pattern, set) -function _load_promise() { - return _promise = _interopRequireWildcard(__webpack_require__(40)); + this.set = set } -var _promise2; +Minimatch.prototype.parseNegate = parseNegate +function parseNegate () { + var pattern = this.pattern + var negate = false + var options = this.options + var negateOffset = 0 -function _load_promise2() { - return _promise2 = __webpack_require__(40); -} + if (options.nonegate) return -var _map; + for (var i = 0, l = pattern.length + ; i < l && pattern.charAt(i) === '!' + ; i++) { + negate = !negate + negateOffset++ + } -function _load_map() { - return _map = _interopRequireDefault(__webpack_require__(20)); + if (negateOffset) this.pattern = pattern.substr(negateOffset) + this.negate = negate } -var _fsNormalized; - -function _load_fsNormalized() { - return _fsNormalized = __webpack_require__(164); +// Brace expansion: +// a{b,c}d -> abd acd +// a{b,}c -> abc ac +// a{0..3}d -> a0d a1d a2d a3d +// a{b,c{d,e}f}g -> abg acdfg acefg +// a{b,c}d{e,f}g -> abdeg acdeg abdeg abdfg +// +// Invalid sets are not expanded. +// a{2..}b -> a{2..}b +// a{b}c -> a{b}c +minimatch.braceExpand = function (pattern, options) { + return braceExpand(pattern, options) } -function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } +Minimatch.prototype.braceExpand = braceExpand -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +function braceExpand (pattern, options) { + if (!options) { + if (this instanceof Minimatch) { + options = this.options + } else { + options = {} + } + } -const constants = exports.constants = typeof (_fs || _load_fs()).default.constants !== 'undefined' ? (_fs || _load_fs()).default.constants : { - R_OK: (_fs || _load_fs()).default.R_OK, - W_OK: (_fs || _load_fs()).default.W_OK, - X_OK: (_fs || _load_fs()).default.X_OK -}; + pattern = typeof pattern === 'undefined' + ? this.pattern : pattern -const lockQueue = exports.lockQueue = new (_blockingQueue || _load_blockingQueue()).default('fs lock'); + if (typeof pattern === 'undefined') { + throw new TypeError('undefined pattern') + } -const readFileBuffer = exports.readFileBuffer = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.readFile); -const open = exports.open = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.open); -const writeFile = exports.writeFile = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.writeFile); -const readlink = exports.readlink = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.readlink); -const realpath = exports.realpath = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.realpath); -const readdir = exports.readdir = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.readdir); -const rename = exports.rename = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.rename); -const access = exports.access = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.access); -const stat = exports.stat = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.stat); -const mkdirp = exports.mkdirp = (0, (_promise2 || _load_promise2()).promisify)(__webpack_require__(116)); -const exists = exports.exists = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.exists, true); -const lstat = exports.lstat = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.lstat); -const chmod = exports.chmod = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.chmod); -const link = exports.link = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.link); -const glob = exports.glob = (0, (_promise2 || _load_promise2()).promisify)((_glob || _load_glob()).default); -exports.unlink = (_fsNormalized || _load_fsNormalized()).unlink; + if (options.nobrace || + !pattern.match(/\{.*\}/)) { + // shortcut. no need to expand. + return [pattern] + } -// fs.copyFile uses the native file copying instructions on the system, performing much better -// than any JS-based solution and consumes fewer resources. Repeated testing to fine tune the -// concurrency level revealed 128 as the sweet spot on a quad-core, 16 CPU Intel system with SSD. + return expand(pattern) +} -const CONCURRENT_QUEUE_ITEMS = (_fs || _load_fs()).default.copyFile ? 128 : 4; +// parse a component of the expanded set. +// At this point, no pattern may contain "/" in it +// so we're going to return a 2d array, where each entry is the full +// pattern, split on '/', and then turned into a regular expression. +// A regexp is made at the end which joins each array with an +// escaped /, and another full one which joins each regexp with |. +// +// Following the lead of Bash 4.1, note that "**" only has special meaning +// when it is the *only* thing in a path portion. Otherwise, any series +// of * is equivalent to a single *. Globstar behavior is enabled by +// default, and can be disabled by setting options.noglobstar. +Minimatch.prototype.parse = parse +var SUBPARSE = {} +function parse (pattern, isSub) { + if (pattern.length > 1024 * 64) { + throw new TypeError('pattern is too long') + } -const fsSymlink = (0, (_promise2 || _load_promise2()).promisify)((_fs || _load_fs()).default.symlink); -const invariant = __webpack_require__(7); -const stripBOM = __webpack_require__(122); + var options = this.options -const noop = () => {}; + // shortcuts + if (!options.noglobstar && pattern === '**') return GLOBSTAR + if (pattern === '') return '' -function copy(src, dest, reporter) { - return copyBulk([{ src, dest }], reporter); -} + var re = '' + var hasMagic = !!options.nocase + var escaping = false + // ? => one single character + var patternListStack = [] + var negativeLists = [] + var stateChar + var inClass = false + var reClassStart = -1 + var classStart = -1 + // . and .. never match anything that doesn't start with ., + // even when options.dot is set. + var patternStart = pattern.charAt(0) === '.' ? '' // anything + // not (start or / followed by . or .. followed by / or end) + : options.dot ? '(?!(?:^|\\\/)\\.{1,2}(?:$|\\\/))' + : '(?!\\.)' + var self = this -function _readFile(loc, encoding) { - return new Promise((resolve, reject) => { - (_fs || _load_fs()).default.readFile(loc, encoding, function (err, content) { - if (err) { - reject(err); - } else { - resolve(content); + function clearStateChar () { + if (stateChar) { + // we had some state-tracking character + // that wasn't consumed by this pass. + switch (stateChar) { + case '*': + re += star + hasMagic = true + break + case '?': + re += qmark + hasMagic = true + break + default: + re += '\\' + stateChar + break } - }); - }); -} + self.debug('clearStateChar %j %j', stateChar, re) + stateChar = false + } + } -function readFile(loc) { - return _readFile(loc, 'utf8').then(normalizeOS); -} + for (var i = 0, len = pattern.length, c + ; (i < len) && (c = pattern.charAt(i)) + ; i++) { + this.debug('%s\t%s %s %j', pattern, i, re, c) -function readFileRaw(loc) { - return _readFile(loc, 'binary'); -} + // skip over any that are escaped. + if (escaping && reSpecials[c]) { + re += '\\' + c + escaping = false + continue + } -function normalizeOS(body) { - return body.replace(/\r\n/g, '\n'); -} + switch (c) { + case '/': + // completely not allowed, even escaped. + // Should already be path-split by now. + return false -const cr = '\r'.charCodeAt(0); -const lf = '\n'.charCodeAt(0); + case '\\': + clearStateChar() + escaping = true + continue -/***/ }), -/* 6 */ -/***/ (function(module, exports, __webpack_require__) { + // the various stateChar values + // for the "extglob" stuff. + case '?': + case '*': + case '+': + case '@': + case '!': + this.debug('%s\t%s %s %j <-- stateChar', pattern, i, re, c) -"use strict"; + // all of those are literals inside a class, except that + // the glob [!a] means [^a] in regexp + if (inClass) { + this.debug(' in class') + if (c === '!' && i === classStart + 1) c = '^' + re += c + continue + } + + // if we already have a stateChar, then it means + // that there was something like ** or +? in there. + // Handle the stateChar, then proceed with this one. + self.debug('call clearStateChar %j', stateChar) + clearStateChar() + stateChar = c + // if extglob is disabled, then +(asdf|foo) isn't a thing. + // just clear the statechar *now*, rather than even diving into + // the patternList stuff. + if (options.noext) clearStateChar() + continue + case '(': + if (inClass) { + re += '(' + continue + } -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.getPathKey = getPathKey; -const os = __webpack_require__(36); -const path = __webpack_require__(0); -const userHome = __webpack_require__(45).default; + if (!stateChar) { + re += '\\(' + continue + } -var _require = __webpack_require__(171); + patternListStack.push({ + type: stateChar, + start: i - 1, + reStart: re.length, + open: plTypes[stateChar].open, + close: plTypes[stateChar].close + }) + // negation is (?:(?!js)[^/]*) + re += stateChar === '!' ? '(?:(?!(?:' : '(?:' + this.debug('plType %j %j', stateChar, re) + stateChar = false + continue -const getCacheDir = _require.getCacheDir, - getConfigDir = _require.getConfigDir, - getDataDir = _require.getDataDir; + case ')': + if (inClass || !patternListStack.length) { + re += '\\)' + continue + } -const isWebpackBundle = __webpack_require__(227); + clearStateChar() + hasMagic = true + var pl = patternListStack.pop() + // negation is (?:(?!js)[^/]*) + // The others are (?:) + re += pl.close + if (pl.type === '!') { + negativeLists.push(pl) + } + pl.reEnd = re.length + continue -const DEPENDENCY_TYPES = exports.DEPENDENCY_TYPES = ['devDependencies', 'dependencies', 'optionalDependencies', 'peerDependencies']; -const RESOLUTIONS = exports.RESOLUTIONS = 'resolutions'; -const MANIFEST_FIELDS = exports.MANIFEST_FIELDS = [RESOLUTIONS, ...DEPENDENCY_TYPES]; + case '|': + if (inClass || !patternListStack.length || escaping) { + re += '\\|' + escaping = false + continue + } -const SUPPORTED_NODE_VERSIONS = exports.SUPPORTED_NODE_VERSIONS = '^4.8.0 || ^5.7.0 || ^6.2.2 || >=8.0.0'; + clearStateChar() + re += '|' + continue -const YARN_REGISTRY = exports.YARN_REGISTRY = 'https://registry.yarnpkg.com'; + // these are mostly the same in regexp and glob + case '[': + // swallow any state-tracking char before the [ + clearStateChar() -const YARN_DOCS = exports.YARN_DOCS = 'https://yarnpkg.com/en/docs/cli/'; -const YARN_INSTALLER_SH = exports.YARN_INSTALLER_SH = 'https://yarnpkg.com/install.sh'; -const YARN_INSTALLER_MSI = exports.YARN_INSTALLER_MSI = 'https://yarnpkg.com/latest.msi'; + if (inClass) { + re += '\\' + c + continue + } -const SELF_UPDATE_VERSION_URL = exports.SELF_UPDATE_VERSION_URL = 'https://yarnpkg.com/latest-version'; + inClass = true + classStart = i + reClassStart = re.length + re += c + continue -// cache version, bump whenever we make backwards incompatible changes -const CACHE_VERSION = exports.CACHE_VERSION = 2; + case ']': + // a right bracket shall lose its special + // meaning and represent itself in + // a bracket expression if it occurs + // first in the list. -- POSIX.2 2.8.3.2 + if (i === classStart + 1 || !inClass) { + re += '\\' + c + escaping = false + continue + } -// lockfile version, bump whenever we make backwards incompatible changes -const LOCKFILE_VERSION = exports.LOCKFILE_VERSION = 1; + // handle the case where we left a class open. + // "[z-a]" is valid, equivalent to "\[z-a\]" + if (inClass) { + // split where the last [ was, make sure we don't have + // an invalid re. if so, re-walk the contents of the + // would-be class to re-translate any characters that + // were passed through as-is + // TODO: It would probably be faster to determine this + // without a try/catch and a new RegExp, but it's tricky + // to do safely. For now, this is safe and works. + var cs = pattern.substring(classStart + 1, i) + try { + RegExp('[' + cs + ']') + } catch (er) { + // not a valid class! + var sp = this.parse(cs, SUBPARSE) + re = re.substr(0, reClassStart) + '\\[' + sp[0] + '\\]' + hasMagic = hasMagic || sp[1] + inClass = false + continue + } + } -// max amount of network requests to perform concurrently -const NETWORK_CONCURRENCY = exports.NETWORK_CONCURRENCY = 8; + // finish up the class. + hasMagic = true + inClass = false + re += c + continue -// HTTP timeout used when downloading packages -const NETWORK_TIMEOUT = exports.NETWORK_TIMEOUT = 30 * 1000; // in milliseconds + default: + // swallow any state char that wasn't consumed + clearStateChar() -// max amount of child processes to execute concurrently -const CHILD_CONCURRENCY = exports.CHILD_CONCURRENCY = 5; + if (escaping) { + // no need + escaping = false + } else if (reSpecials[c] + && !(c === '^' && inClass)) { + re += '\\' + } -const REQUIRED_PACKAGE_KEYS = exports.REQUIRED_PACKAGE_KEYS = ['name', 'version', '_uid']; + re += c -function getPreferredCacheDirectories() { - const preferredCacheDirectories = [getCacheDir()]; + } // switch + } // for - if (process.getuid) { - // $FlowFixMe: process.getuid exists, dammit - preferredCacheDirectories.push(path.join(os.tmpdir(), `.yarn-cache-${process.getuid()}`)); + // handle the case where we left a class open. + // "[abc" is valid, equivalent to "\[abc" + if (inClass) { + // split where the last [ was, and escape it + // this is a huge pita. We now have to re-walk + // the contents of the would-be class to re-translate + // any characters that were passed through as-is + cs = pattern.substr(classStart + 1) + sp = this.parse(cs, SUBPARSE) + re = re.substr(0, reClassStart) + '\\[' + sp[0] + hasMagic = hasMagic || sp[1] } - preferredCacheDirectories.push(path.join(os.tmpdir(), `.yarn-cache`)); - - return preferredCacheDirectories; -} + // handle the case where we had a +( thing at the *end* + // of the pattern. + // each pattern list stack adds 3 chars, and we need to go through + // and escape any | chars that were passed through as-is for the regexp. + // Go through and escape them, taking care not to double-escape any + // | chars that were already escaped. + for (pl = patternListStack.pop(); pl; pl = patternListStack.pop()) { + var tail = re.slice(pl.reStart + pl.open.length) + this.debug('setting tail', re, pl) + // maybe some even number of \, then maybe 1 \, followed by a | + tail = tail.replace(/((?:\\{2}){0,64})(\\?)\|/g, function (_, $1, $2) { + if (!$2) { + // the | isn't already escaped, so escape it. + $2 = '\\' + } -const PREFERRED_MODULE_CACHE_DIRECTORIES = exports.PREFERRED_MODULE_CACHE_DIRECTORIES = getPreferredCacheDirectories(); -const CONFIG_DIRECTORY = exports.CONFIG_DIRECTORY = getConfigDir(); -const DATA_DIRECTORY = exports.DATA_DIRECTORY = getDataDir(); -const LINK_REGISTRY_DIRECTORY = exports.LINK_REGISTRY_DIRECTORY = path.join(DATA_DIRECTORY, 'link'); -const GLOBAL_MODULE_DIRECTORY = exports.GLOBAL_MODULE_DIRECTORY = path.join(DATA_DIRECTORY, 'global'); + // need to escape all those slashes *again*, without escaping the + // one that we need for escaping the | character. As it works out, + // escaping an even number of slashes can be done by simply repeating + // it exactly after itself. That's why this trick works. + // + // I am sorry that you have to see this. + return $1 + $1 + $2 + '|' + }) -const NODE_BIN_PATH = exports.NODE_BIN_PATH = process.execPath; -const YARN_BIN_PATH = exports.YARN_BIN_PATH = getYarnBinPath(); + this.debug('tail=%j\n %s', tail, tail, pl, re) + var t = pl.type === '*' ? star + : pl.type === '?' ? qmark + : '\\' + pl.type -// Webpack needs to be configured with node.__dirname/__filename = false -function getYarnBinPath() { - if (isWebpackBundle) { - return __filename; - } else { - return path.join(__dirname, '..', 'bin', 'yarn.js'); + hasMagic = true + re = re.slice(0, pl.reStart) + t + '\\(' + tail } -} - -const NODE_MODULES_FOLDER = exports.NODE_MODULES_FOLDER = 'node_modules'; -const NODE_PACKAGE_JSON = exports.NODE_PACKAGE_JSON = 'package.json'; - -const POSIX_GLOBAL_PREFIX = exports.POSIX_GLOBAL_PREFIX = `${process.env.DESTDIR || ''}/usr/local`; -const FALLBACK_GLOBAL_PREFIX = exports.FALLBACK_GLOBAL_PREFIX = path.join(userHome, '.yarn'); -const META_FOLDER = exports.META_FOLDER = '.yarn-meta'; -const INTEGRITY_FILENAME = exports.INTEGRITY_FILENAME = '.yarn-integrity'; -const LOCKFILE_FILENAME = exports.LOCKFILE_FILENAME = 'yarn.lock'; -const METADATA_FILENAME = exports.METADATA_FILENAME = '.yarn-metadata.json'; -const TARBALL_FILENAME = exports.TARBALL_FILENAME = '.yarn-tarball.tgz'; -const CLEAN_FILENAME = exports.CLEAN_FILENAME = '.yarnclean'; + // handle trailing things that only matter at the very end. + clearStateChar() + if (escaping) { + // trailing \\ + re += '\\\\' + } -const NPM_LOCK_FILENAME = exports.NPM_LOCK_FILENAME = 'package-lock.json'; -const NPM_SHRINKWRAP_FILENAME = exports.NPM_SHRINKWRAP_FILENAME = 'npm-shrinkwrap.json'; + // only need to apply the nodot start if the re starts with + // something that could conceivably capture a dot + var addPatternStart = false + switch (re.charAt(0)) { + case '.': + case '[': + case '(': addPatternStart = true + } -const DEFAULT_INDENT = exports.DEFAULT_INDENT = ' '; -const SINGLE_INSTANCE_PORT = exports.SINGLE_INSTANCE_PORT = 31997; -const SINGLE_INSTANCE_FILENAME = exports.SINGLE_INSTANCE_FILENAME = '.yarn-single-instance'; + // Hack to work around lack of negative lookbehind in JS + // A pattern like: *.!(x).!(y|z) needs to ensure that a name + // like 'a.xyz.yz' doesn't match. So, the first negative + // lookahead, has to look ALL the way ahead, to the end of + // the pattern. + for (var n = negativeLists.length - 1; n > -1; n--) { + var nl = negativeLists[n] -const ENV_PATH_KEY = exports.ENV_PATH_KEY = getPathKey(process.platform, process.env); + var nlBefore = re.slice(0, nl.reStart) + var nlFirst = re.slice(nl.reStart, nl.reEnd - 8) + var nlLast = re.slice(nl.reEnd - 8, nl.reEnd) + var nlAfter = re.slice(nl.reEnd) -function getPathKey(platform, env) { - let pathKey = 'PATH'; + nlLast += nlAfter - // windows calls its path "Path" usually, but this is not guaranteed. - if (platform === 'win32') { - pathKey = 'Path'; + // Handle nested stuff like *(*.js|!(*.json)), where open parens + // mean that we should *not* include the ) in the bit that is considered + // "after" the negated section. + var openParensBefore = nlBefore.split('(').length - 1 + var cleanAfter = nlAfter + for (i = 0; i < openParensBefore; i++) { + cleanAfter = cleanAfter.replace(/\)[+*?]?/, '') + } + nlAfter = cleanAfter - for (const key in env) { - if (key.toLowerCase() === 'path') { - pathKey = key; - } + var dollar = '' + if (nlAfter === '' && isSub !== SUBPARSE) { + dollar = '$' } + var newRe = nlBefore + nlFirst + nlAfter + dollar + nlLast + re = newRe } - return pathKey; -} - -const VERSION_COLOR_SCHEME = exports.VERSION_COLOR_SCHEME = { - major: 'red', - premajor: 'red', - minor: 'yellow', - preminor: 'yellow', - patch: 'green', - prepatch: 'green', - prerelease: 'red', - unchanged: 'white', - unknown: 'red' -}; - -/***/ }), -/* 7 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/** - * Copyright (c) 2013-present, Facebook, Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - - - -/** - * Use invariant() to assert state which your program assumes to be true. - * - * Provide sprintf-style format (only %s is supported) and arguments - * to provide information about what broke and what you were - * expecting. - * - * The invariant message will be stripped in production, but the invariant - * will remain to ensure logic does not differ in production. - */ - -var NODE_ENV = "none"; + // if the re is not "" at this point, then we need to make sure + // it doesn't match against an empty path part. + // Otherwise a/* will match a/, which it should not. + if (re !== '' && hasMagic) { + re = '(?=.)' + re + } -var invariant = function(condition, format, a, b, c, d, e, f) { - if (NODE_ENV !== 'production') { - if (format === undefined) { - throw new Error('invariant requires an error message argument'); - } + if (addPatternStart) { + re = patternStart + re } - if (!condition) { - var error; - if (format === undefined) { - error = new Error( - 'Minified exception occurred; use the non-minified dev environment ' + - 'for the full error message and additional helpful warnings.' - ); - } else { - var args = [a, b, c, d, e, f]; - var argIndex = 0; - error = new Error( - format.replace(/%s/g, function() { return args[argIndex++]; }) - ); - error.name = 'Invariant Violation'; - } + // parsing just a piece of a larger pattern. + if (isSub === SUBPARSE) { + return [re, hasMagic] + } - error.framesToPop = 1; // we don't care about invariant's own frame - throw error; + // skip the regexp for non-magical patterns + // unescape anything in it, though, so that it'll be + // an exact match against a file etc. + if (!hasMagic) { + return globUnescape(pattern) } -}; -module.exports = invariant; + var flags = options.nocase ? 'i' : '' + try { + var regExp = new RegExp('^' + re + '$', flags) + } catch (er) { + // If it was an invalid regular expression, then it can't match + // anything. This trick looks for a character after the end of + // the string, which is of course impossible, except in multi-line + // mode, but it's not a /m regex. + return new RegExp('$.') + } + regExp._glob = pattern + regExp._src = re -/***/ }), -/* 8 */, -/* 9 */ -/***/ (function(module, exports) { + return regExp +} -module.exports = __webpack_require__(133); +minimatch.makeRe = function (pattern, options) { + return new Minimatch(pattern, options || {}).makeRe() +} -/***/ }), -/* 10 */, -/* 11 */ -/***/ (function(module, exports) { +Minimatch.prototype.makeRe = makeRe +function makeRe () { + if (this.regexp || this.regexp === false) return this.regexp -// https://github.com/zloirock/core-js/issues/86#issuecomment-115759028 -var global = module.exports = typeof window != 'undefined' && window.Math == Math - ? window : typeof self != 'undefined' && self.Math == Math ? self - // eslint-disable-next-line no-new-func - : Function('return this')(); -if (typeof __g == 'number') __g = global; // eslint-disable-line no-undef + // at this point, this.set is a 2d array of partial + // pattern strings, or "**". + // + // It's better to use .match(). This function shouldn't + // be used, really, but it's pretty convenient sometimes, + // when you just want to work with a regex. + var set = this.set + if (!set.length) { + this.regexp = false + return this.regexp + } + var options = this.options -/***/ }), -/* 12 */ -/***/ (function(module, exports, __webpack_require__) { + var twoStar = options.noglobstar ? star + : options.dot ? twoStarDot + : twoStarNoDot + var flags = options.nocase ? 'i' : '' -"use strict"; + var re = set.map(function (pattern) { + return pattern.map(function (p) { + return (p === GLOBSTAR) ? twoStar + : (typeof p === 'string') ? regExpEscape(p) + : p._src + }).join('\\\/') + }).join('|') + // must match entire pattern + // ending in a * or ** will make it less strict. + re = '^(?:' + re + ')$' -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.sortAlpha = sortAlpha; -exports.entries = entries; -exports.removePrefix = removePrefix; -exports.removeSuffix = removeSuffix; -exports.addSuffix = addSuffix; -exports.hyphenate = hyphenate; -exports.camelCase = camelCase; -exports.compareSortedArrays = compareSortedArrays; -exports.sleep = sleep; -const _camelCase = __webpack_require__(176); + // can match anything, as long as it's not this. + if (this.negate) re = '^(?!' + re + ').*$' -function sortAlpha(a, b) { - // sort alphabetically in a deterministic way - const shortLen = Math.min(a.length, b.length); - for (let i = 0; i < shortLen; i++) { - const aChar = a.charCodeAt(i); - const bChar = b.charCodeAt(i); - if (aChar !== bChar) { - return aChar - bChar; - } + try { + this.regexp = new RegExp(re, flags) + } catch (ex) { + this.regexp = false } - return a.length - b.length; + return this.regexp } -function entries(obj) { - const entries = []; - if (obj) { - for (const key in obj) { - entries.push([key, obj[key]]); - } +minimatch.match = function (list, pattern, options) { + options = options || {} + var mm = new Minimatch(pattern, options) + list = list.filter(function (f) { + return mm.match(f) + }) + if (mm.options.nonull && !list.length) { + list.push(pattern) } - return entries; + return list } -function removePrefix(pattern, prefix) { - if (pattern.startsWith(prefix)) { - pattern = pattern.slice(prefix.length); - } - - return pattern; -} +Minimatch.prototype.match = match +function match (f, partial) { + this.debug('match', f, this.pattern) + // short-circuit in the case of busted things. + // comments, etc. + if (this.comment) return false + if (this.empty) return f === '' -function removeSuffix(pattern, suffix) { - if (pattern.endsWith(suffix)) { - return pattern.slice(0, -suffix.length); - } + if (f === '/' && partial) return true - return pattern; -} + var options = this.options -function addSuffix(pattern, suffix) { - if (!pattern.endsWith(suffix)) { - return pattern + suffix; + // windows: need to use /, not \ + if (path.sep !== '/') { + f = f.split(path.sep).join('/') } - return pattern; -} + // treat the test path as a set of pathparts. + f = f.split(slashSplit) + this.debug(this.pattern, 'split', f) -function hyphenate(str) { - return str.replace(/[A-Z]/g, match => { - return '-' + match.charAt(0).toLowerCase(); - }); -} + // just ONE of the pattern sets in this.set needs to match + // in order for it to be valid. If negating, then just one + // match means that we have failed. + // Either way, return on the first hit. -function camelCase(str) { - if (/[A-Z]/.test(str)) { - return null; - } else { - return _camelCase(str); - } -} + var set = this.set + this.debug(this.pattern, 'set', set) -function compareSortedArrays(array1, array2) { - if (array1.length !== array2.length) { - return false; + // Find the basename of the path by looking for the last non-empty segment + var filename + var i + for (i = f.length - 1; i >= 0; i--) { + filename = f[i] + if (filename) break } - for (let i = 0, len = array1.length; i < len; i++) { - if (array1[i] !== array2[i]) { - return false; + + for (i = 0; i < set.length; i++) { + var pattern = set[i] + var file = f + if (options.matchBase && pattern.length === 1) { + file = [filename] + } + var hit = this.matchOne(file, pattern, partial) + if (hit) { + if (options.flipNegate) return true + return !this.negate } } - return true; -} -function sleep(ms) { - return new Promise(resolve => { - setTimeout(resolve, ms); - }); + // didn't get any hits. this is success if it's a negative + // pattern, failure otherwise. + if (options.flipNegate) return false + return this.negate } -/***/ }), -/* 13 */ -/***/ (function(module, exports, __webpack_require__) { - -var store = __webpack_require__(107)('wks'); -var uid = __webpack_require__(111); -var Symbol = __webpack_require__(11).Symbol; -var USE_SYMBOL = typeof Symbol == 'function'; - -var $exports = module.exports = function (name) { - return store[name] || (store[name] = - USE_SYMBOL && Symbol[name] || (USE_SYMBOL ? Symbol : uid)('Symbol.' + name)); -}; +// set partial to true to test if, for example, +// "/a/b" matches the start of "/*/b/*/d" +// Partial means, if you run out of file before you run +// out of pattern, then that's fine, as long as all +// the parts match. +Minimatch.prototype.matchOne = function (file, pattern, partial) { + var options = this.options -$exports.store = store; + this.debug('matchOne', + { 'this': this, file: file, pattern: pattern }) + this.debug('matchOne', file.length, pattern.length) -/***/ }), -/* 14 */ -/***/ (function(module, exports, __webpack_require__) { + for (var fi = 0, + pi = 0, + fl = file.length, + pl = pattern.length + ; (fi < fl) && (pi < pl) + ; fi++, pi++) { + this.debug('matchOne loop') + var p = pattern[pi] + var f = file[fi] -"use strict"; + this.debug(pattern, p, f) + // should be impossible. + // some invalid regexp stuff in the set. + if (p === false) return false -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.stringify = exports.parse = undefined; + if (p === GLOBSTAR) { + this.debug('GLOBSTAR', [pattern, p, f]) -var _asyncToGenerator2; + // "**" + // a/**/b/**/c would match the following: + // a/b/x/y/z/c + // a/x/y/z/b/c + // a/b/x/b/x/c + // a/b/c + // To do this, take the rest of the pattern after + // the **, and see if it would match the file remainder. + // If so, return success. + // If not, the ** "swallows" a segment, and try again. + // This is recursively awful. + // + // a/**/b/**/c matching a/b/x/y/z/c + // - a matches a + // - doublestar + // - matchOne(b/x/y/z/c, b/**/c) + // - b matches b + // - doublestar + // - matchOne(x/y/z/c, c) -> no + // - matchOne(y/z/c, c) -> no + // - matchOne(z/c, c) -> no + // - matchOne(c, c) yes, hit + var fr = fi + var pr = pi + 1 + if (pr === pl) { + this.debug('** at the end') + // a ** at the end will just swallow the rest. + // We have found a match. + // however, it will not swallow /.x, unless + // options.dot is set. + // . and .. are *never* matched by **, for explosively + // exponential reasons. + for (; fi < fl; fi++) { + if (file[fi] === '.' || file[fi] === '..' || + (!options.dot && file[fi].charAt(0) === '.')) return false + } + return true + } -function _load_asyncToGenerator() { - return _asyncToGenerator2 = _interopRequireDefault(__webpack_require__(1)); -} + // ok, let's see if we can swallow whatever we can. + while (fr < fl) { + var swallowee = file[fr] -var _parse; + this.debug('\nglobstar while', file, fr, pattern, pr, swallowee) -function _load_parse() { - return _parse = __webpack_require__(81); -} + // XXX remove this slice. Just pass the start index. + if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) { + this.debug('globstar found match!', fr, fl, swallowee) + // found a match. + return true + } else { + // can't swallow "." or ".." ever. + // can only swallow ".foo" when explicitly asked. + if (swallowee === '.' || swallowee === '..' || + (!options.dot && swallowee.charAt(0) === '.')) { + this.debug('dot detected!', file, fr, pattern, pr) + break + } -Object.defineProperty(exports, 'parse', { - enumerable: true, - get: function get() { - return _interopRequireDefault(_parse || _load_parse()).default; - } -}); + // ** swallows a segment, and continue. + this.debug('globstar swallow a segment, and continue') + fr++ + } + } -var _stringify; + // no match was found. + // However, in partial mode, we can't say this is necessarily over. + // If there's more *pattern* left, then + if (partial) { + // ran out of file + this.debug('\n>>> no match, partial?', file, fr, pattern, pr) + if (fr === fl) return true + } + return false + } -function _load_stringify() { - return _stringify = __webpack_require__(150); -} + // something other than ** + // non-magic patterns just have to match exactly + // patterns with magic have been turned into regexps. + var hit + if (typeof p === 'string') { + if (options.nocase) { + hit = f.toLowerCase() === p.toLowerCase() + } else { + hit = f === p + } + this.debug('string match', p, f, hit) + } else { + hit = f.match(p) + this.debug('pattern match', p, f, hit) + } -Object.defineProperty(exports, 'stringify', { - enumerable: true, - get: function get() { - return _interopRequireDefault(_stringify || _load_stringify()).default; + if (!hit) return false } -}); -exports.implodeEntry = implodeEntry; -exports.explodeEntry = explodeEntry; - -var _misc; - -function _load_misc() { - return _misc = __webpack_require__(12); -} - -var _normalizePattern; -function _load_normalizePattern() { - return _normalizePattern = __webpack_require__(29); -} + // Note: ending in / means that we'll get a final "" + // at the end of the pattern. This can only match a + // corresponding "" at the end of the file. + // If the file ends in /, then it can only match a + // a pattern that ends in /, unless the pattern just + // doesn't have any more for it. But, a/b/ should *not* + // match "a/b/*", even though "" matches against the + // [^/]*? pattern, except in partial mode, where it might + // simply not be reached yet. + // However, a/b/ should still satisfy a/* -var _parse2; + // now either we fell off the end of the pattern, or we're done. + if (fi === fl && pi === pl) { + // ran out of pattern and filename at the same time. + // an exact hit! + return true + } else if (fi === fl) { + // ran out of file, but still had pattern left. + // this is ok if we're doing the match as part of + // a glob fs traversal. + return partial + } else if (pi === pl) { + // ran out of pattern, still have file left. + // this is only acceptable if we're on the very last + // empty segment of a file with a trailing slash. + // a/* should match a/b/ + var emptyFileEnd = (fi === fl - 1) && (file[fi] === '') + return emptyFileEnd + } -function _load_parse2() { - return _parse2 = _interopRequireDefault(__webpack_require__(81)); + // should be unreachable. + throw new Error('wtf?') } -var _constants; - -function _load_constants() { - return _constants = __webpack_require__(6); +// replace stuff like \* with * +function globUnescape (s) { + return s.replace(/\\(.)/g, '$1') } -var _fs; - -function _load_fs() { - return _fs = _interopRequireWildcard(__webpack_require__(5)); +function regExpEscape (s) { + return s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&') } -function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -const invariant = __webpack_require__(7); - -const path = __webpack_require__(0); -const ssri = __webpack_require__(55); -function getName(pattern) { - return (0, (_normalizePattern || _load_normalizePattern()).normalizePattern)(pattern).name; -} +/***/ }), +/* 61 */ +/***/ (function(module, exports, __webpack_require__) { -function blankObjectUndefined(obj) { - return obj && Object.keys(obj).length ? obj : undefined; -} +var wrappy = __webpack_require__(123) +module.exports = wrappy(once) +module.exports.strict = wrappy(onceStrict) -function keyForRemote(remote) { - return remote.resolved || (remote.reference && remote.hash ? `${remote.reference}#${remote.hash}` : null); -} +once.proto = once(function () { + Object.defineProperty(Function.prototype, 'once', { + value: function () { + return once(this) + }, + configurable: true + }) -function serializeIntegrity(integrity) { - // We need this because `Integrity.toString()` does not use sorting to ensure a stable string output - // See https://git.io/vx2Hy - return integrity.toString().split(' ').sort().join(' '); -} + Object.defineProperty(Function.prototype, 'onceStrict', { + value: function () { + return onceStrict(this) + }, + configurable: true + }) +}) -function implodeEntry(pattern, obj) { - const inferredName = getName(pattern); - const integrity = obj.integrity ? serializeIntegrity(obj.integrity) : ''; - const imploded = { - name: inferredName === obj.name ? undefined : obj.name, - version: obj.version, - uid: obj.uid === obj.version ? undefined : obj.uid, - resolved: obj.resolved, - registry: obj.registry === 'npm' ? undefined : obj.registry, - dependencies: blankObjectUndefined(obj.dependencies), - optionalDependencies: blankObjectUndefined(obj.optionalDependencies), - permissions: blankObjectUndefined(obj.permissions), - prebuiltVariants: blankObjectUndefined(obj.prebuiltVariants) - }; - if (integrity) { - imploded.integrity = integrity; +function once (fn) { + var f = function () { + if (f.called) return f.value + f.called = true + return f.value = fn.apply(this, arguments) } - return imploded; + f.called = false + return f } -function explodeEntry(pattern, obj) { - obj.optionalDependencies = obj.optionalDependencies || {}; - obj.dependencies = obj.dependencies || {}; - obj.uid = obj.uid || obj.version; - obj.permissions = obj.permissions || {}; - obj.registry = obj.registry || 'npm'; - obj.name = obj.name || getName(pattern); - const integrity = obj.integrity; - if (integrity && integrity.isIntegrity) { - obj.integrity = ssri.parse(integrity); +function onceStrict (fn) { + var f = function () { + if (f.called) + throw new Error(f.onceError) + f.called = true + return f.value = fn.apply(this, arguments) } - return obj; + var name = fn.name || 'Function wrapped with `once`' + f.onceError = name + " shouldn't be called more than once" + f.called = false + return f } -class Lockfile { - constructor({ cache, source, parseResultType } = {}) { - this.source = source || ''; - this.cache = cache; - this.parseResultType = parseResultType; - } - - // source string if the `cache` was parsed +/***/ }), +/* 62 */, +/* 63 */ +/***/ (function(module, exports) { - // if true, we're parsing an old yarn file and need to update integrity fields - hasEntriesExistWithoutIntegrity() { - if (!this.cache) { - return false; - } +module.exports = __webpack_require__(416); - for (const key in this.cache) { - // $FlowFixMe - `this.cache` is clearly defined at this point - if (!/^.*@(file:|http)/.test(key) && this.cache[key] && !this.cache[key].integrity) { - return true; - } - } +/***/ }), +/* 64 */, +/* 65 */, +/* 66 */, +/* 67 */ +/***/ (function(module, exports) { - return false; - } +// 7.2.1 RequireObjectCoercible(argument) +module.exports = function (it) { + if (it == undefined) throw TypeError("Can't call method on " + it); + return it; +}; - static fromDirectory(dir, reporter) { - return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () { - // read the manifest in this directory - const lockfileLoc = path.join(dir, (_constants || _load_constants()).LOCKFILE_FILENAME); - let lockfile; - let rawLockfile = ''; - let parseResult; +/***/ }), +/* 68 */ +/***/ (function(module, exports, __webpack_require__) { - if (yield (_fs || _load_fs()).exists(lockfileLoc)) { - rawLockfile = yield (_fs || _load_fs()).readFile(lockfileLoc); - parseResult = (0, (_parse2 || _load_parse2()).default)(rawLockfile, lockfileLoc); +var isObject = __webpack_require__(34); +var document = __webpack_require__(11).document; +// typeof document.createElement is 'object' in old IE +var is = isObject(document) && isObject(document.createElement); +module.exports = function (it) { + return is ? document.createElement(it) : {}; +}; - if (reporter) { - if (parseResult.type === 'merge') { - reporter.info(reporter.lang('lockfileMerged')); - } else if (parseResult.type === 'conflict') { - reporter.warn(reporter.lang('lockfileConflict')); - } - } - lockfile = parseResult.object; - } else if (reporter) { - reporter.info(reporter.lang('noLockfileFound')); - } +/***/ }), +/* 69 */ +/***/ (function(module, exports) { - return new Lockfile({ cache: lockfile, source: rawLockfile, parseResultType: parseResult && parseResult.type }); - })(); - } +module.exports = true; - getLocked(pattern) { - const cache = this.cache; - if (!cache) { - return undefined; - } - const shrunk = pattern in cache && cache[pattern]; +/***/ }), +/* 70 */ +/***/ (function(module, exports, __webpack_require__) { - if (typeof shrunk === 'string') { - return this.getLocked(shrunk); - } else if (shrunk) { - explodeEntry(pattern, shrunk); - return shrunk; - } +"use strict"; - return undefined; - } +// 25.4.1.5 NewPromiseCapability(C) +var aFunction = __webpack_require__(46); - removePattern(pattern) { - const cache = this.cache; - if (!cache) { - return; - } - delete cache[pattern]; - } +function PromiseCapability(C) { + var resolve, reject; + this.promise = new C(function ($$resolve, $$reject) { + if (resolve !== undefined || reject !== undefined) throw TypeError('Bad Promise constructor'); + resolve = $$resolve; + reject = $$reject; + }); + this.resolve = aFunction(resolve); + this.reject = aFunction(reject); +} - getLockfile(patterns) { - const lockfile = {}; - const seen = new Map(); +module.exports.f = function (C) { + return new PromiseCapability(C); +}; - // order by name so that lockfile manifest is assigned to the first dependency with this manifest - // the others that have the same remoteKey will just refer to the first - // ordering allows for consistency in lockfile when it is serialized - const sortedPatternsKeys = Object.keys(patterns).sort((_misc || _load_misc()).sortAlpha); - for (var _iterator = sortedPatternsKeys, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { - var _ref; +/***/ }), +/* 71 */ +/***/ (function(module, exports, __webpack_require__) { - if (_isArray) { - if (_i >= _iterator.length) break; - _ref = _iterator[_i++]; - } else { - _i = _iterator.next(); - if (_i.done) break; - _ref = _i.value; - } +var def = __webpack_require__(50).f; +var has = __webpack_require__(49); +var TAG = __webpack_require__(13)('toStringTag'); - const pattern = _ref; +module.exports = function (it, tag, stat) { + if (it && !has(it = stat ? it : it.prototype, TAG)) def(it, TAG, { configurable: true, value: tag }); +}; - const pkg = patterns[pattern]; - const remote = pkg._remote, - ref = pkg._reference; - invariant(ref, 'Package is missing a reference'); - invariant(remote, 'Package is missing a remote'); +/***/ }), +/* 72 */ +/***/ (function(module, exports, __webpack_require__) { - const remoteKey = keyForRemote(remote); - const seenPattern = remoteKey && seen.get(remoteKey); - if (seenPattern) { - // no point in duplicating it - lockfile[pattern] = seenPattern; +var shared = __webpack_require__(107)('keys'); +var uid = __webpack_require__(111); +module.exports = function (key) { + return shared[key] || (shared[key] = uid(key)); +}; - // if we're relying on our name being inferred and two of the patterns have - // different inferred names then we need to set it - if (!seenPattern.name && getName(pattern) !== pkg.name) { - seenPattern.name = pkg.name; - } - continue; - } - const obj = implodeEntry(pattern, { - name: pkg.name, - version: pkg.version, - uid: pkg._uid, - resolved: remote.resolved, - integrity: remote.integrity, - registry: remote.registry, - dependencies: pkg.dependencies, - peerDependencies: pkg.peerDependencies, - optionalDependencies: pkg.optionalDependencies, - permissions: ref.permissions, - prebuiltVariants: pkg.prebuiltVariants - }); - lockfile[pattern] = obj; +/***/ }), +/* 73 */ +/***/ (function(module, exports) { - if (remoteKey) { - seen.set(remoteKey, obj); - } - } +// 7.1.4 ToInteger +var ceil = Math.ceil; +var floor = Math.floor; +module.exports = function (it) { + return isNaN(it = +it) ? 0 : (it > 0 ? floor : ceil)(it); +}; - return lockfile; - } -} -exports.default = Lockfile; /***/ }), -/* 15 */, -/* 16 */, -/* 17 */ -/***/ (function(module, exports) { +/* 74 */ +/***/ (function(module, exports, __webpack_require__) { + +// to indexed object, toObject with fallback for non-array-like ES3 strings +var IObject = __webpack_require__(131); +var defined = __webpack_require__(67); +module.exports = function (it) { + return IObject(defined(it)); +}; -module.exports = __webpack_require__(173); /***/ }), -/* 18 */, -/* 19 */, -/* 20 */ +/* 75 */ /***/ (function(module, exports, __webpack_require__) { -"use strict"; - +// Approach: +// +// 1. Get the minimatch set +// 2. For each pattern in the set, PROCESS(pattern, false) +// 3. Store matches per-set, then uniq them +// +// PROCESS(pattern, inGlobStar) +// Get the first [n] items from pattern that are all strings +// Join these together. This is PREFIX. +// If there is no more remaining, then stat(PREFIX) and +// add to matches if it succeeds. END. +// +// If inGlobStar and PREFIX is symlink and points to dir +// set ENTRIES = [] +// else readdir(PREFIX) as ENTRIES +// If fail, END +// +// with ENTRIES +// If pattern[n] is GLOBSTAR +// // handle the case where the globstar match is empty +// // by pruning it out, and testing the resulting pattern +// PROCESS(pattern[0..n] + pattern[n+1 .. $], false) +// // handle other cases. +// for ENTRY in ENTRIES (not dotfiles) +// // attach globstar + tail onto the entry +// // Mark that this entry is a globstar match +// PROCESS(pattern[0..n] + ENTRY + pattern[n .. $], true) +// +// else // not globstar +// for ENTRY in ENTRIES (not dotfiles, unless pattern[n] is dot) +// Test ENTRY against pattern[n] +// If fails, continue +// If passes, PROCESS(pattern[0..n] + item + pattern[n+1 .. $]) +// +// Caveat: +// Cache all stats and readdirs results to minimize syscall. Since all +// we ever care about is existence and directory-ness, we can just keep +// `true` for files, and [children,...] for directories, or `false` for +// things that don't exist. -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.default = nullify; -function nullify(obj = {}) { - if (Array.isArray(obj)) { - for (var _iterator = obj, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { - var _ref; +module.exports = glob - if (_isArray) { - if (_i >= _iterator.length) break; - _ref = _iterator[_i++]; - } else { - _i = _iterator.next(); - if (_i.done) break; - _ref = _i.value; - } +var fs = __webpack_require__(3) +var rp = __webpack_require__(114) +var minimatch = __webpack_require__(60) +var Minimatch = minimatch.Minimatch +var inherits = __webpack_require__(42) +var EE = __webpack_require__(54).EventEmitter +var path = __webpack_require__(0) +var assert = __webpack_require__(22) +var isAbsolute = __webpack_require__(76) +var globSync = __webpack_require__(218) +var common = __webpack_require__(115) +var alphasort = common.alphasort +var alphasorti = common.alphasorti +var setopts = common.setopts +var ownProp = common.ownProp +var inflight = __webpack_require__(223) +var util = __webpack_require__(2) +var childrenIgnored = common.childrenIgnored +var isIgnored = common.isIgnored - const item = _ref; +var once = __webpack_require__(61) - nullify(item); - } - } else if (obj !== null && typeof obj === 'object' || typeof obj === 'function') { - Object.setPrototypeOf(obj, null); +function glob (pattern, options, cb) { + if (typeof options === 'function') cb = options, options = {} + if (!options) options = {} - // for..in can only be applied to 'object', not 'function' - if (typeof obj === 'object') { - for (const key in obj) { - nullify(obj[key]); - } - } + if (options.sync) { + if (cb) + throw new TypeError('callback provided to sync glob') + return globSync(pattern, options) } - return obj; + return new Glob(pattern, options, cb) } -/***/ }), -/* 21 */, -/* 22 */ -/***/ (function(module, exports) { - -module.exports = __webpack_require__(162); - -/***/ }), -/* 23 */ -/***/ (function(module, exports) { - -var core = module.exports = { version: '2.5.7' }; -if (typeof __e == 'number') __e = core; // eslint-disable-line no-undef - - -/***/ }), -/* 24 */, -/* 25 */, -/* 26 */, -/* 27 */ -/***/ (function(module, exports, __webpack_require__) { - -var isObject = __webpack_require__(34); -module.exports = function (it) { - if (!isObject(it)) throw TypeError(it + ' is not an object!'); - return it; -}; +glob.sync = globSync +var GlobSync = glob.GlobSync = globSync.GlobSync +// old api surface +glob.glob = glob -/***/ }), -/* 28 */, -/* 29 */ -/***/ (function(module, exports, __webpack_require__) { +function extend (origin, add) { + if (add === null || typeof add !== 'object') { + return origin + } -"use strict"; + var keys = Object.keys(add) + var i = keys.length + while (i--) { + origin[keys[i]] = add[keys[i]] + } + return origin +} +glob.hasMagic = function (pattern, options_) { + var options = extend({}, options_) + options.noprocess = true -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.normalizePattern = normalizePattern; + var g = new Glob(pattern, options) + var set = g.minimatch.set -/** - * Explode and normalize a pattern into its name and range. - */ + if (!pattern) + return false -function normalizePattern(pattern) { - let hasVersion = false; - let range = 'latest'; - let name = pattern; + if (set.length > 1) + return true - // if we're a scope then remove the @ and add it back later - let isScoped = false; - if (name[0] === '@') { - isScoped = true; - name = name.slice(1); + for (var j = 0; j < set[0].length; j++) { + if (typeof set[0][j] !== 'string') + return true } - // take first part as the name - const parts = name.split('@'); - if (parts.length > 1) { - name = parts.shift(); - range = parts.join('@'); + return false +} - if (range) { - hasVersion = true; - } else { - range = '*'; - } +glob.Glob = Glob +inherits(Glob, EE) +function Glob (pattern, options, cb) { + if (typeof options === 'function') { + cb = options + options = null } - // add back @ scope suffix - if (isScoped) { - name = `@${name}`; + if (options && options.sync) { + if (cb) + throw new TypeError('callback provided to sync glob') + return new GlobSync(pattern, options) } - return { name, range, hasVersion }; -} - -/***/ }), -/* 30 */, -/* 31 */ -/***/ (function(module, exports, __webpack_require__) { - -var dP = __webpack_require__(50); -var createDesc = __webpack_require__(106); -module.exports = __webpack_require__(33) ? function (object, key, value) { - return dP.f(object, key, createDesc(1, value)); -} : function (object, key, value) { - object[key] = value; - return object; -}; + if (!(this instanceof Glob)) + return new Glob(pattern, options, cb) + setopts(this, pattern, options) + this._didRealPath = false -/***/ }), -/* 32 */ -/***/ (function(module, exports, __webpack_require__) { + // process each pattern in the minimatch set + var n = this.minimatch.set.length -/* eslint-disable node/no-deprecated-api */ -var buffer = __webpack_require__(63) -var Buffer = buffer.Buffer + // The matches are stored as {: true,...} so that + // duplicates are automagically pruned. + // Later, we do an Object.keys() on these. + // Keep them as a list so we can fill in when nonull is set. + this.matches = new Array(n) -// alternative to using Object.keys for old browsers -function copyProps (src, dst) { - for (var key in src) { - dst[key] = src[key] + if (typeof cb === 'function') { + cb = once(cb) + this.on('error', cb) + this.on('end', function (matches) { + cb(null, matches) + }) } -} -if (Buffer.from && Buffer.alloc && Buffer.allocUnsafe && Buffer.allocUnsafeSlow) { - module.exports = buffer -} else { - // Copy properties from require('buffer') - copyProps(buffer, exports) - exports.Buffer = SafeBuffer -} -function SafeBuffer (arg, encodingOrOffset, length) { - return Buffer(arg, encodingOrOffset, length) -} + var self = this + this._processing = 0 -// Copy static methods from Buffer -copyProps(Buffer, SafeBuffer) + this._emitQueue = [] + this._processQueue = [] + this.paused = false -SafeBuffer.from = function (arg, encodingOrOffset, length) { - if (typeof arg === 'number') { - throw new TypeError('Argument must not be a number') - } - return Buffer(arg, encodingOrOffset, length) -} + if (this.noprocess) + return this -SafeBuffer.alloc = function (size, fill, encoding) { - if (typeof size !== 'number') { - throw new TypeError('Argument must be a number') - } - var buf = Buffer(size) - if (fill !== undefined) { - if (typeof encoding === 'string') { - buf.fill(fill, encoding) - } else { - buf.fill(fill) - } - } else { - buf.fill(0) - } - return buf -} + if (n === 0) + return done() -SafeBuffer.allocUnsafe = function (size) { - if (typeof size !== 'number') { - throw new TypeError('Argument must be a number') + var sync = true + for (var i = 0; i < n; i ++) { + this._process(this.minimatch.set[i], i, false, done) } - return Buffer(size) -} + sync = false -SafeBuffer.allocUnsafeSlow = function (size) { - if (typeof size !== 'number') { - throw new TypeError('Argument must be a number') + function done () { + --self._processing + if (self._processing <= 0) { + if (sync) { + process.nextTick(function () { + self._finish() + }) + } else { + self._finish() + } + } } - return buffer.SlowBuffer(size) } +Glob.prototype._finish = function () { + assert(this instanceof Glob) + if (this.aborted) + return -/***/ }), -/* 33 */ -/***/ (function(module, exports, __webpack_require__) { - -// Thank's IE8 for his funny defineProperty -module.exports = !__webpack_require__(85)(function () { - return Object.defineProperty({}, 'a', { get: function () { return 7; } }).a != 7; -}); - - -/***/ }), -/* 34 */ -/***/ (function(module, exports) { + if (this.realpath && !this._didRealpath) + return this._realpath() -module.exports = function (it) { - return typeof it === 'object' ? it !== null : typeof it === 'function'; -}; + common.finish(this) + this.emit('end', this.found) +} +Glob.prototype._realpath = function () { + if (this._didRealpath) + return -/***/ }), -/* 35 */ -/***/ (function(module, exports) { + this._didRealpath = true -module.exports = {}; + var n = this.matches.length + if (n === 0) + return this._finish() + var self = this + for (var i = 0; i < this.matches.length; i++) + this._realpathSet(i, next) -/***/ }), -/* 36 */ -/***/ (function(module, exports) { + function next () { + if (--n === 0) + self._finish() + } +} -module.exports = __webpack_require__(122); +Glob.prototype._realpathSet = function (index, cb) { + var matchset = this.matches[index] + if (!matchset) + return cb() -/***/ }), -/* 37 */, -/* 38 */, -/* 39 */, -/* 40 */ -/***/ (function(module, exports, __webpack_require__) { + var found = Object.keys(matchset) + var self = this + var n = found.length -"use strict"; + if (n === 0) + return cb() + var set = this.matches[index] = Object.create(null) + found.forEach(function (p, i) { + // If there's a problem with the stat, then it means that + // one or more of the links in the realpath couldn't be + // resolved. just return the abs value in that case. + p = self._makeAbs(p) + rp.realpath(p, self.realpathCache, function (er, real) { + if (!er) + set[real] = true + else if (er.syscall === 'stat') + set[p] = true + else + self.emit('error', er) // srsly wtf right here -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.wait = wait; -exports.promisify = promisify; -exports.queue = queue; -function wait(delay) { - return new Promise(resolve => { - setTimeout(resolve, delay); - }); + if (--n === 0) { + self.matches[index] = set + cb() + } + }) + }) } -function promisify(fn, firstData) { - return function (...args) { - return new Promise(function (resolve, reject) { - args.push(function (err, ...result) { - let res = result; - - if (result.length <= 1) { - res = result[0]; - } - - if (firstData) { - res = err; - err = null; - } - - if (err) { - reject(err); - } else { - resolve(res); - } - }); - - fn.apply(null, args); - }); - }; +Glob.prototype._mark = function (p) { + return common.mark(this, p) } -function queue(arr, promiseProducer, concurrency = Infinity) { - concurrency = Math.min(concurrency, arr.length); +Glob.prototype._makeAbs = function (f) { + return common.makeAbs(this, f) +} - // clone - arr = arr.slice(); +Glob.prototype.abort = function () { + this.aborted = true + this.emit('abort') +} - const results = []; - let total = arr.length; - if (!total) { - return Promise.resolve(results); +Glob.prototype.pause = function () { + if (!this.paused) { + this.paused = true + this.emit('pause') } +} - return new Promise((resolve, reject) => { - for (let i = 0; i < concurrency; i++) { - next(); +Glob.prototype.resume = function () { + if (this.paused) { + this.emit('resume') + this.paused = false + if (this._emitQueue.length) { + var eq = this._emitQueue.slice(0) + this._emitQueue.length = 0 + for (var i = 0; i < eq.length; i ++) { + var e = eq[i] + this._emitMatch(e[0], e[1]) + } } - - function next() { - const item = arr.shift(); - const promise = promiseProducer(item); - - promise.then(function (result) { - results.push(result); - - total--; - if (total === 0) { - resolve(results); - } else { - if (arr.length) { - next(); - } - } - }, reject); + if (this._processQueue.length) { + var pq = this._processQueue.slice(0) + this._processQueue.length = 0 + for (var i = 0; i < pq.length; i ++) { + var p = pq[i] + this._processing-- + this._process(p[0], p[1], p[2], p[3]) + } } - }); + } } -/***/ }), -/* 41 */ -/***/ (function(module, exports, __webpack_require__) { +Glob.prototype._process = function (pattern, index, inGlobStar, cb) { + assert(this instanceof Glob) + assert(typeof cb === 'function') -var global = __webpack_require__(11); -var core = __webpack_require__(23); -var ctx = __webpack_require__(48); -var hide = __webpack_require__(31); -var has = __webpack_require__(49); -var PROTOTYPE = 'prototype'; + if (this.aborted) + return -var $export = function (type, name, source) { - var IS_FORCED = type & $export.F; - var IS_GLOBAL = type & $export.G; - var IS_STATIC = type & $export.S; - var IS_PROTO = type & $export.P; - var IS_BIND = type & $export.B; - var IS_WRAP = type & $export.W; - var exports = IS_GLOBAL ? core : core[name] || (core[name] = {}); - var expProto = exports[PROTOTYPE]; - var target = IS_GLOBAL ? global : IS_STATIC ? global[name] : (global[name] || {})[PROTOTYPE]; - var key, own, out; - if (IS_GLOBAL) source = name; - for (key in source) { - // contains in native - own = !IS_FORCED && target && target[key] !== undefined; - if (own && has(exports, key)) continue; - // export native or passed - out = own ? target[key] : source[key]; - // prevent global pollution for namespaces - exports[key] = IS_GLOBAL && typeof target[key] != 'function' ? source[key] - // bind timers to global for call from export context - : IS_BIND && own ? ctx(out, global) - // wrap global constructors for prevent change them in library - : IS_WRAP && target[key] == out ? (function (C) { - var F = function (a, b, c) { - if (this instanceof C) { - switch (arguments.length) { - case 0: return new C(); - case 1: return new C(a); - case 2: return new C(a, b); - } return new C(a, b, c); - } return C.apply(this, arguments); - }; - F[PROTOTYPE] = C[PROTOTYPE]; - return F; - // make static versions for prototype methods - })(out) : IS_PROTO && typeof out == 'function' ? ctx(Function.call, out) : out; - // export proto methods to core.%CONSTRUCTOR%.methods.%NAME% - if (IS_PROTO) { - (exports.virtual || (exports.virtual = {}))[key] = out; - // export proto methods to core.%CONSTRUCTOR%.prototype.%NAME% - if (type & $export.R && expProto && !expProto[key]) hide(expProto, key, out); - } + this._processing++ + if (this.paused) { + this._processQueue.push([pattern, index, inGlobStar, cb]) + return } -}; -// type bitmap -$export.F = 1; // forced -$export.G = 2; // global -$export.S = 4; // static -$export.P = 8; // proto -$export.B = 16; // bind -$export.W = 32; // wrap -$export.U = 64; // safe -$export.R = 128; // real proto method for `library` -module.exports = $export; + //console.error('PROCESS %d', this._processing, pattern) -/***/ }), -/* 42 */ -/***/ (function(module, exports, __webpack_require__) { + // Get the first [n] parts of pattern that are all strings. + var n = 0 + while (typeof pattern[n] === 'string') { + n ++ + } + // now n is the index of the first one that is *not* a string. -try { - var util = __webpack_require__(2); - if (typeof util.inherits !== 'function') throw ''; - module.exports = util.inherits; -} catch (e) { - module.exports = __webpack_require__(224); -} + // see if there's anything else + var prefix + switch (n) { + // if not, then this is rather simple + case pattern.length: + this._processSimple(pattern.join('/'), index, cb) + return + + case 0: + // pattern *starts* with some non-trivial item. + // going to readdir(cwd), but not include the prefix in matches. + prefix = null + break + default: + // pattern has some string bits in the front. + // whatever it starts with, whether that's 'absolute' like /foo/bar, + // or 'relative' like '../baz' + prefix = pattern.slice(0, n).join('/') + break + } -/***/ }), -/* 43 */, -/* 44 */, -/* 45 */ -/***/ (function(module, exports, __webpack_require__) { + var remain = pattern.slice(n) -"use strict"; + // get the list of entries. + var read + if (prefix === null) + read = '.' + else if (isAbsolute(prefix) || isAbsolute(pattern.join('/'))) { + if (!prefix || !isAbsolute(prefix)) + prefix = '/' + prefix + read = prefix + } else + read = prefix + var abs = this._makeAbs(read) -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.home = undefined; + //if ignored, skip _processing + if (childrenIgnored(this, read)) + return cb() -var _rootUser; + var isGlobStar = remain[0] === minimatch.GLOBSTAR + if (isGlobStar) + this._processGlobStar(prefix, read, abs, remain, index, inGlobStar, cb) + else + this._processReaddir(prefix, read, abs, remain, index, inGlobStar, cb) +} -function _load_rootUser() { - return _rootUser = _interopRequireDefault(__webpack_require__(169)); +Glob.prototype._processReaddir = function (prefix, read, abs, remain, index, inGlobStar, cb) { + var self = this + this._readdir(abs, inGlobStar, function (er, entries) { + return self._processReaddir2(prefix, read, abs, remain, index, inGlobStar, entries, cb) + }) } -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +Glob.prototype._processReaddir2 = function (prefix, read, abs, remain, index, inGlobStar, entries, cb) { -const path = __webpack_require__(0); + // if the abs isn't a dir, then nothing can match! + if (!entries) + return cb() -const home = exports.home = __webpack_require__(36).homedir(); + // It will only match dot entries if it starts with a dot, or if + // dot is set. Stuff like @(.foo|.bar) isn't allowed. + var pn = remain[0] + var negate = !!this.minimatch.negate + var rawGlob = pn._glob + var dotOk = this.dot || rawGlob.charAt(0) === '.' -const userHomeDir = (_rootUser || _load_rootUser()).default ? path.resolve('/usr/local/share') : home; + var matchedEntries = [] + for (var i = 0; i < entries.length; i++) { + var e = entries[i] + if (e.charAt(0) !== '.' || dotOk) { + var m + if (negate && !prefix) { + m = !e.match(pn) + } else { + m = e.match(pn) + } + if (m) + matchedEntries.push(e) + } + } -exports.default = userHomeDir; + //console.error('prd2', prefix, entries, remain[0]._glob, matchedEntries) -/***/ }), -/* 46 */ -/***/ (function(module, exports) { + var len = matchedEntries.length + // If there are no matched entries, then nothing matches. + if (len === 0) + return cb() -module.exports = function (it) { - if (typeof it != 'function') throw TypeError(it + ' is not a function!'); - return it; -}; + // if this is the last remaining pattern bit, then no need for + // an additional stat *unless* the user has specified mark or + // stat explicitly. We know they exist, since readdir returned + // them. + if (remain.length === 1 && !this.mark && !this.stat) { + if (!this.matches[index]) + this.matches[index] = Object.create(null) -/***/ }), -/* 47 */ -/***/ (function(module, exports) { + for (var i = 0; i < len; i ++) { + var e = matchedEntries[i] + if (prefix) { + if (prefix !== '/') + e = prefix + '/' + e + else + e = prefix + e + } -var toString = {}.toString; + if (e.charAt(0) === '/' && !this.nomount) { + e = path.join(this.root, e) + } + this._emitMatch(index, e) + } + // This was the last one, and no stats were needed + return cb() + } -module.exports = function (it) { - return toString.call(it).slice(8, -1); -}; + // now test all matched entries as stand-ins for that part + // of the pattern. + remain.shift() + for (var i = 0; i < len; i ++) { + var e = matchedEntries[i] + var newPattern + if (prefix) { + if (prefix !== '/') + e = prefix + '/' + e + else + e = prefix + e + } + this._process([e].concat(remain), index, inGlobStar, cb) + } + cb() +} +Glob.prototype._emitMatch = function (index, e) { + if (this.aborted) + return -/***/ }), -/* 48 */ -/***/ (function(module, exports, __webpack_require__) { + if (isIgnored(this, e)) + return -// optional / simple context binding -var aFunction = __webpack_require__(46); -module.exports = function (fn, that, length) { - aFunction(fn); - if (that === undefined) return fn; - switch (length) { - case 1: return function (a) { - return fn.call(that, a); - }; - case 2: return function (a, b) { - return fn.call(that, a, b); - }; - case 3: return function (a, b, c) { - return fn.call(that, a, b, c); - }; + if (this.paused) { + this._emitQueue.push([index, e]) + return } - return function (/* ...args */) { - return fn.apply(that, arguments); - }; -}; + var abs = isAbsolute(e) ? e : this._makeAbs(e) -/***/ }), -/* 49 */ -/***/ (function(module, exports) { + if (this.mark) + e = this._mark(e) -var hasOwnProperty = {}.hasOwnProperty; -module.exports = function (it, key) { - return hasOwnProperty.call(it, key); -}; + if (this.absolute) + e = abs + if (this.matches[index][e]) + return -/***/ }), -/* 50 */ -/***/ (function(module, exports, __webpack_require__) { + if (this.nodir) { + var c = this.cache[abs] + if (c === 'DIR' || Array.isArray(c)) + return + } -var anObject = __webpack_require__(27); -var IE8_DOM_DEFINE = __webpack_require__(184); -var toPrimitive = __webpack_require__(201); -var dP = Object.defineProperty; + this.matches[index][e] = true -exports.f = __webpack_require__(33) ? Object.defineProperty : function defineProperty(O, P, Attributes) { - anObject(O); - P = toPrimitive(P, true); - anObject(Attributes); - if (IE8_DOM_DEFINE) try { - return dP(O, P, Attributes); - } catch (e) { /* empty */ } - if ('get' in Attributes || 'set' in Attributes) throw TypeError('Accessors not supported!'); - if ('value' in Attributes) O[P] = Attributes.value; - return O; -}; + var st = this.statCache[abs] + if (st) + this.emit('stat', e, st) + this.emit('match', e) +} -/***/ }), -/* 51 */, -/* 52 */, -/* 53 */, -/* 54 */ -/***/ (function(module, exports) { +Glob.prototype._readdirInGlobStar = function (abs, cb) { + if (this.aborted) + return -module.exports = __webpack_require__(164); + // follow all symlinked directories forever + // just proceed as if this is a non-globstar situation + if (this.follow) + return this._readdir(abs, false, cb) -/***/ }), -/* 55 */ -/***/ (function(module, exports, __webpack_require__) { + var lstatkey = 'lstat\0' + abs + var self = this + var lstatcb = inflight(lstatkey, lstatcb_) -"use strict"; + if (lstatcb) + fs.lstat(abs, lstatcb) + function lstatcb_ (er, lstat) { + if (er && er.code === 'ENOENT') + return cb() -const Buffer = __webpack_require__(32).Buffer + var isSym = lstat && lstat.isSymbolicLink() + self.symlinks[abs] = isSym -const crypto = __webpack_require__(9) -const Transform = __webpack_require__(17).Transform + // If it's not a symlink or a dir, then it's definitely a regular file. + // don't bother doing a readdir in that case. + if (!isSym && lstat && !lstat.isDirectory()) { + self.cache[abs] = 'FILE' + cb() + } else + self._readdir(abs, false, cb) + } +} -const SPEC_ALGORITHMS = ['sha256', 'sha384', 'sha512'] +Glob.prototype._readdir = function (abs, inGlobStar, cb) { + if (this.aborted) + return -const BASE64_REGEX = /^[a-z0-9+/]+(?:=?=?)$/i -const SRI_REGEX = /^([^-]+)-([^?]+)([?\S*]*)$/ -const STRICT_SRI_REGEX = /^([^-]+)-([A-Za-z0-9+/=]{44,88})(\?[\x21-\x7E]*)*$/ -const VCHAR_REGEX = /^[\x21-\x7E]+$/ + cb = inflight('readdir\0'+abs+'\0'+inGlobStar, cb) + if (!cb) + return -class Hash { - get isHash () { return true } - constructor (hash, opts) { - const strict = !!(opts && opts.strict) - this.source = hash.trim() - // 3.1. Integrity metadata (called "Hash" by ssri) - // https://w3c.github.io/webappsec-subresource-integrity/#integrity-metadata-description - const match = this.source.match( - strict - ? STRICT_SRI_REGEX - : SRI_REGEX - ) - if (!match) { return } - if (strict && !SPEC_ALGORITHMS.some(a => a === match[1])) { return } - this.algorithm = match[1] - this.digest = match[2] + //console.error('RD %j %j', +inGlobStar, abs) + if (inGlobStar && !ownProp(this.symlinks, abs)) + return this._readdirInGlobStar(abs, cb) - const rawOpts = match[3] - this.options = rawOpts ? rawOpts.slice(1).split('?') : [] - } - hexDigest () { - return this.digest && Buffer.from(this.digest, 'base64').toString('hex') - } - toJSON () { - return this.toString() - } - toString (opts) { - if (opts && opts.strict) { - // Strict mode enforces the standard as close to the foot of the - // letter as it can. - if (!( - // The spec has very restricted productions for algorithms. - // https://www.w3.org/TR/CSP2/#source-list-syntax - SPEC_ALGORITHMS.some(x => x === this.algorithm) && - // Usually, if someone insists on using a "different" base64, we - // leave it as-is, since there's multiple standards, and the - // specified is not a URL-safe variant. - // https://www.w3.org/TR/CSP2/#base64_value - this.digest.match(BASE64_REGEX) && - // Option syntax is strictly visual chars. - // https://w3c.github.io/webappsec-subresource-integrity/#grammardef-option-expression - // https://tools.ietf.org/html/rfc5234#appendix-B.1 - (this.options || []).every(opt => opt.match(VCHAR_REGEX)) - )) { - return '' - } - } - const options = this.options && this.options.length - ? `?${this.options.join('?')}` - : '' - return `${this.algorithm}-${this.digest}${options}` - } -} + if (ownProp(this.cache, abs)) { + var c = this.cache[abs] + if (!c || c === 'FILE') + return cb() -class Integrity { - get isIntegrity () { return true } - toJSON () { - return this.toString() - } - toString (opts) { - opts = opts || {} - let sep = opts.sep || ' ' - if (opts.strict) { - // Entries must be separated by whitespace, according to spec. - sep = sep.replace(/\S+/g, ' ') - } - return Object.keys(this).map(k => { - return this[k].map(hash => { - return Hash.prototype.toString.call(hash, opts) - }).filter(x => x.length).join(sep) - }).filter(x => x.length).join(sep) - } - concat (integrity, opts) { - const other = typeof integrity === 'string' - ? integrity - : stringify(integrity, opts) - return parse(`${this.toString(opts)} ${other}`, opts) - } - hexDigest () { - return parse(this, {single: true}).hexDigest() - } - match (integrity, opts) { - const other = parse(integrity, opts) - const algo = other.pickAlgorithm(opts) - return ( - this[algo] && - other[algo] && - this[algo].find(hash => - other[algo].find(otherhash => - hash.digest === otherhash.digest - ) - ) - ) || false - } - pickAlgorithm (opts) { - const pickAlgorithm = (opts && opts.pickAlgorithm) || getPrioritizedHash - const keys = Object.keys(this) - if (!keys.length) { - throw new Error(`No algorithms available for ${ - JSON.stringify(this.toString()) - }`) - } - return keys.reduce((acc, algo) => { - return pickAlgorithm(acc, algo) || acc - }) + if (Array.isArray(c)) + return cb(null, c) } -} -module.exports.parse = parse -function parse (sri, opts) { - opts = opts || {} - if (typeof sri === 'string') { - return _parse(sri, opts) - } else if (sri.algorithm && sri.digest) { - const fullSri = new Integrity() - fullSri[sri.algorithm] = [sri] - return _parse(stringify(fullSri, opts), opts) - } else { - return _parse(stringify(sri, opts), opts) - } + var self = this + fs.readdir(abs, readdirCb(this, abs, cb)) } -function _parse (integrity, opts) { - // 3.4.3. Parse metadata - // https://w3c.github.io/webappsec-subresource-integrity/#parse-metadata - if (opts.single) { - return new Hash(integrity, opts) +function readdirCb (self, abs, cb) { + return function (er, entries) { + if (er) + self._readdirError(abs, er, cb) + else + self._readdirEntries(abs, entries, cb) } - return integrity.trim().split(/\s+/).reduce((acc, string) => { - const hash = new Hash(string, opts) - if (hash.algorithm && hash.digest) { - const algo = hash.algorithm - if (!acc[algo]) { acc[algo] = [] } - acc[algo].push(hash) - } - return acc - }, new Integrity()) } -module.exports.stringify = stringify -function stringify (obj, opts) { - if (obj.algorithm && obj.digest) { - return Hash.prototype.toString.call(obj, opts) - } else if (typeof obj === 'string') { - return stringify(parse(obj, opts), opts) - } else { - return Integrity.prototype.toString.call(obj, opts) +Glob.prototype._readdirEntries = function (abs, entries, cb) { + if (this.aborted) + return + + // if we haven't asked to stat everything, then just + // assume that everything in there exists, so we can avoid + // having to stat it a second time. + if (!this.mark && !this.stat) { + for (var i = 0; i < entries.length; i ++) { + var e = entries[i] + if (abs === '/') + e = abs + e + else + e = abs + '/' + e + this.cache[e] = true + } } -} -module.exports.fromHex = fromHex -function fromHex (hexDigest, algorithm, opts) { - const optString = (opts && opts.options && opts.options.length) - ? `?${opts.options.join('?')}` - : '' - return parse( - `${algorithm}-${ - Buffer.from(hexDigest, 'hex').toString('base64') - }${optString}`, opts - ) + this.cache[abs] = entries + return cb(null, entries) } -module.exports.fromData = fromData -function fromData (data, opts) { - opts = opts || {} - const algorithms = opts.algorithms || ['sha512'] - const optString = opts.options && opts.options.length - ? `?${opts.options.join('?')}` - : '' - return algorithms.reduce((acc, algo) => { - const digest = crypto.createHash(algo).update(data).digest('base64') - const hash = new Hash( - `${algo}-${digest}${optString}`, - opts - ) - if (hash.algorithm && hash.digest) { - const algo = hash.algorithm - if (!acc[algo]) { acc[algo] = [] } - acc[algo].push(hash) - } - return acc - }, new Integrity()) -} +Glob.prototype._readdirError = function (f, er, cb) { + if (this.aborted) + return -module.exports.fromStream = fromStream -function fromStream (stream, opts) { - opts = opts || {} - const P = opts.Promise || Promise - const istream = integrityStream(opts) - return new P((resolve, reject) => { - stream.pipe(istream) - stream.on('error', reject) - istream.on('error', reject) - let sri - istream.on('integrity', s => { sri = s }) - istream.on('end', () => resolve(sri)) - istream.on('data', () => {}) + // handle errors, and cache the information + switch (er.code) { + case 'ENOTSUP': // https://github.com/isaacs/node-glob/issues/205 + case 'ENOTDIR': // totally normal. means it *does* exist. + var abs = this._makeAbs(f) + this.cache[abs] = 'FILE' + if (abs === this.cwdAbs) { + var error = new Error(er.code + ' invalid cwd ' + this.cwd) + error.path = this.cwd + error.code = er.code + this.emit('error', error) + this.abort() + } + break + + case 'ENOENT': // not terribly unusual + case 'ELOOP': + case 'ENAMETOOLONG': + case 'UNKNOWN': + this.cache[this._makeAbs(f)] = false + break + + default: // some unusual error. Treat as failure. + this.cache[this._makeAbs(f)] = false + if (this.strict) { + this.emit('error', er) + // If the error is handled, then we abort + // if not, we threw out of here + this.abort() + } + if (!this.silent) + console.error('glob error', er) + break + } + + return cb() +} + +Glob.prototype._processGlobStar = function (prefix, read, abs, remain, index, inGlobStar, cb) { + var self = this + this._readdir(abs, inGlobStar, function (er, entries) { + self._processGlobStar2(prefix, read, abs, remain, index, inGlobStar, entries, cb) }) } -module.exports.checkData = checkData -function checkData (data, sri, opts) { - opts = opts || {} - sri = parse(sri, opts) - if (!Object.keys(sri).length) { - if (opts.error) { - throw Object.assign( - new Error('No valid integrity hashes to check against'), { - code: 'EINTEGRITY' - } - ) - } else { - return false - } - } - const algorithm = sri.pickAlgorithm(opts) - const digest = crypto.createHash(algorithm).update(data).digest('base64') - const newSri = parse({algorithm, digest}) - const match = newSri.match(sri, opts) - if (match || !opts.error) { - return match - } else if (typeof opts.size === 'number' && (data.length !== opts.size)) { - const err = new Error(`data size mismatch when checking ${sri}.\n Wanted: ${opts.size}\n Found: ${data.length}`) - err.code = 'EBADSIZE' - err.found = data.length - err.expected = opts.size - err.sri = sri - throw err - } else { - const err = new Error(`Integrity checksum failed when using ${algorithm}: Wanted ${sri}, but got ${newSri}. (${data.length} bytes)`) - err.code = 'EINTEGRITY' - err.found = newSri - err.expected = sri - err.algorithm = algorithm - err.sri = sri - throw err + +Glob.prototype._processGlobStar2 = function (prefix, read, abs, remain, index, inGlobStar, entries, cb) { + //console.error('pgs2', prefix, remain[0], entries) + + // no entries means not a dir, so it can never have matches + // foo.txt/** doesn't match foo.txt + if (!entries) + return cb() + + // test without the globstar, and with every child both below + // and replacing the globstar. + var remainWithoutGlobStar = remain.slice(1) + var gspref = prefix ? [ prefix ] : [] + var noGlobStar = gspref.concat(remainWithoutGlobStar) + + // the noGlobStar pattern exits the inGlobStar state + this._process(noGlobStar, index, false, cb) + + var isSym = this.symlinks[abs] + var len = entries.length + + // If it's a symlink, and we're in a globstar, then stop + if (isSym && inGlobStar) + return cb() + + for (var i = 0; i < len; i++) { + var e = entries[i] + if (e.charAt(0) === '.' && !this.dot) + continue + + // these two cases enter the inGlobStar state + var instead = gspref.concat(entries[i], remainWithoutGlobStar) + this._process(instead, index, true, cb) + + var below = gspref.concat(entries[i], remain) + this._process(below, index, true, cb) } -} -module.exports.checkStream = checkStream -function checkStream (stream, sri, opts) { - opts = opts || {} - const P = opts.Promise || Promise - const checker = integrityStream(Object.assign({}, opts, { - integrity: sri - })) - return new P((resolve, reject) => { - stream.pipe(checker) - stream.on('error', reject) - checker.on('error', reject) - let sri - checker.on('verified', s => { sri = s }) - checker.on('end', () => resolve(sri)) - checker.on('data', () => {}) - }) + cb() } -module.exports.integrityStream = integrityStream -function integrityStream (opts) { - opts = opts || {} - // For verification - const sri = opts.integrity && parse(opts.integrity, opts) - const goodSri = sri && Object.keys(sri).length - const algorithm = goodSri && sri.pickAlgorithm(opts) - const digests = goodSri && sri[algorithm] - // Calculating stream - const algorithms = Array.from( - new Set( - (opts.algorithms || ['sha512']) - .concat(algorithm ? [algorithm] : []) - ) - ) - const hashes = algorithms.map(crypto.createHash) - let streamSize = 0 - const stream = new Transform({ - transform (chunk, enc, cb) { - streamSize += chunk.length - hashes.forEach(h => h.update(chunk, enc)) - cb(null, chunk, enc) - } - }).on('end', () => { - const optString = (opts.options && opts.options.length) - ? `?${opts.options.join('?')}` - : '' - const newSri = parse(hashes.map((h, i) => { - return `${algorithms[i]}-${h.digest('base64')}${optString}` - }).join(' '), opts) - // Integrity verification mode - const match = goodSri && newSri.match(sri, opts) - if (typeof opts.size === 'number' && streamSize !== opts.size) { - const err = new Error(`stream size mismatch when checking ${sri}.\n Wanted: ${opts.size}\n Found: ${streamSize}`) - err.code = 'EBADSIZE' - err.found = streamSize - err.expected = opts.size - err.sri = sri - stream.emit('error', err) - } else if (opts.integrity && !match) { - const err = new Error(`${sri} integrity checksum failed when using ${algorithm}: wanted ${digests} but got ${newSri}. (${streamSize} bytes)`) - err.code = 'EINTEGRITY' - err.found = newSri - err.expected = digests - err.algorithm = algorithm - err.sri = sri - stream.emit('error', err) - } else { - stream.emit('size', streamSize) - stream.emit('integrity', newSri) - match && stream.emit('verified', match) - } +Glob.prototype._processSimple = function (prefix, index, cb) { + // XXX review this. Shouldn't it be doing the mounting etc + // before doing stat? kinda weird? + var self = this + this._stat(prefix, function (er, exists) { + self._processSimple2(prefix, index, er, exists, cb) }) - return stream } +Glob.prototype._processSimple2 = function (prefix, index, er, exists, cb) { -module.exports.create = createIntegrity -function createIntegrity (opts) { - opts = opts || {} - const algorithms = opts.algorithms || ['sha512'] - const optString = opts.options && opts.options.length - ? `?${opts.options.join('?')}` - : '' + //console.error('ps2', prefix, exists) - const hashes = algorithms.map(crypto.createHash) + if (!this.matches[index]) + this.matches[index] = Object.create(null) - return { - update: function (chunk, enc) { - hashes.forEach(h => h.update(chunk, enc)) - return this - }, - digest: function (enc) { - const integrity = algorithms.reduce((acc, algo) => { - const digest = hashes.shift().digest('base64') - const hash = new Hash( - `${algo}-${digest}${optString}`, - opts - ) - if (hash.algorithm && hash.digest) { - const algo = hash.algorithm - if (!acc[algo]) { acc[algo] = [] } - acc[algo].push(hash) - } - return acc - }, new Integrity()) + // If it doesn't exist, then just mark the lack of results + if (!exists) + return cb() - return integrity + if (prefix && isAbsolute(prefix) && !this.nomount) { + var trail = /[\/\\]$/.test(prefix) + if (prefix.charAt(0) === '/') { + prefix = path.join(this.root, prefix) + } else { + prefix = path.resolve(this.root, prefix) + if (trail) + prefix += '/' } } + + if (process.platform === 'win32') + prefix = prefix.replace(/\\/g, '/') + + // Mark this as a match + this._emitMatch(index, prefix) + cb() } -const NODE_HASHES = new Set(crypto.getHashes()) +// Returns either 'DIR', 'FILE', or false +Glob.prototype._stat = function (f, cb) { + var abs = this._makeAbs(f) + var needDir = f.slice(-1) === '/' -// This is a Best Effort™ at a reasonable priority for hash algos -const DEFAULT_PRIORITY = [ - 'md5', 'whirlpool', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512', - // TODO - it's unclear _which_ of these Node will actually use as its name - // for the algorithm, so we guesswork it based on the OpenSSL names. - 'sha3', - 'sha3-256', 'sha3-384', 'sha3-512', - 'sha3_256', 'sha3_384', 'sha3_512' -].filter(algo => NODE_HASHES.has(algo)) + if (f.length > this.maxLength) + return cb() -function getPrioritizedHash (algo1, algo2) { - return DEFAULT_PRIORITY.indexOf(algo1.toLowerCase()) >= DEFAULT_PRIORITY.indexOf(algo2.toLowerCase()) - ? algo1 - : algo2 -} + if (!this.stat && ownProp(this.cache, abs)) { + var c = this.cache[abs] + if (Array.isArray(c)) + c = 'DIR' -/***/ }), -/* 56 */, -/* 57 */, -/* 58 */, -/* 59 */, -/* 60 */ -/***/ (function(module, exports, __webpack_require__) { + // It exists, but maybe not how we need it + if (!needDir || c === 'DIR') + return cb(null, c) -module.exports = minimatch -minimatch.Minimatch = Minimatch + if (needDir && c === 'FILE') + return cb() -var path = { sep: '/' } -try { - path = __webpack_require__(0) -} catch (er) {} + // otherwise we have to stat, because maybe c=true + // if we know it exists, but not what it is. + } -var GLOBSTAR = minimatch.GLOBSTAR = Minimatch.GLOBSTAR = {} -var expand = __webpack_require__(175) + var exists + var stat = this.statCache[abs] + if (stat !== undefined) { + if (stat === false) + return cb(null, stat) + else { + var type = stat.isDirectory() ? 'DIR' : 'FILE' + if (needDir && type === 'FILE') + return cb() + else + return cb(null, type, stat) + } + } -var plTypes = { - '!': { open: '(?:(?!(?:', close: '))[^/]*?)'}, - '?': { open: '(?:', close: ')?' }, - '+': { open: '(?:', close: ')+' }, - '*': { open: '(?:', close: ')*' }, - '@': { open: '(?:', close: ')' } + var self = this + var statcb = inflight('stat\0' + abs, lstatcb_) + if (statcb) + fs.lstat(abs, statcb) + + function lstatcb_ (er, lstat) { + if (lstat && lstat.isSymbolicLink()) { + // If it's a symlink, then treat it as the target, unless + // the target does not exist, then treat it as a file. + return fs.stat(abs, function (er, stat) { + if (er) + self._stat2(f, abs, null, lstat, cb) + else + self._stat2(f, abs, er, stat, cb) + }) + } else { + self._stat2(f, abs, er, lstat, cb) + } + } } -// any single thing other than / -// don't need to escape / when using new RegExp() -var qmark = '[^/]' +Glob.prototype._stat2 = function (f, abs, er, stat, cb) { + if (er && (er.code === 'ENOENT' || er.code === 'ENOTDIR')) { + this.statCache[abs] = false + return cb() + } -// * => any number of characters -var star = qmark + '*?' + var needDir = f.slice(-1) === '/' + this.statCache[abs] = stat -// ** when dots are allowed. Anything goes, except .. and . -// not (^ or / followed by one or two dots followed by $ or /), -// followed by anything, any number of times. -var twoStarDot = '(?:(?!(?:\\\/|^)(?:\\.{1,2})($|\\\/)).)*?' + if (abs.slice(-1) === '/' && stat && !stat.isDirectory()) + return cb(null, false, stat) -// not a ^ or / followed by a dot, -// followed by anything, any number of times. -var twoStarNoDot = '(?:(?!(?:\\\/|^)\\.).)*?' + var c = true + if (stat) + c = stat.isDirectory() ? 'DIR' : 'FILE' + this.cache[abs] = this.cache[abs] || c -// characters that need to be escaped in RegExp. -var reSpecials = charSet('().*{}+?[]^$\\!') + if (needDir && c === 'FILE') + return cb() -// "abc" -> { a:true, b:true, c:true } -function charSet (s) { - return s.split('').reduce(function (set, c) { - set[c] = true - return set - }, {}) + return cb(null, c, stat) } -// normalizes slashes. -var slashSplit = /\/+/ -minimatch.filter = filter -function filter (pattern, options) { - options = options || {} - return function (p, i, list) { - return minimatch(p, pattern, options) - } +/***/ }), +/* 76 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +function posix(path) { + return path.charAt(0) === '/'; } -function ext (a, b) { - a = a || {} - b = b || {} - var t = {} - Object.keys(b).forEach(function (k) { - t[k] = b[k] - }) - Object.keys(a).forEach(function (k) { - t[k] = a[k] - }) - return t +function win32(path) { + // https://github.com/nodejs/node/blob/b3fcc245fb25539909ef1d5eaa01dbf92e168633/lib/path.js#L56 + var splitDeviceRe = /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?([\s\S]*?)$/; + var result = splitDeviceRe.exec(path); + var device = result[1] || ''; + var isUnc = Boolean(device && device.charAt(1) !== ':'); + + // UNC paths are always absolute + return Boolean(result[2] || isUnc); } -minimatch.defaults = function (def) { - if (!def || !Object.keys(def).length) return minimatch +module.exports = process.platform === 'win32' ? win32 : posix; +module.exports.posix = posix; +module.exports.win32 = win32; - var orig = minimatch - var m = function minimatch (p, pattern, options) { - return orig.minimatch(p, pattern, ext(def, options)) - } +/***/ }), +/* 77 */, +/* 78 */, +/* 79 */ +/***/ (function(module, exports) { - m.Minimatch = function Minimatch (pattern, options) { - return new orig.Minimatch(pattern, ext(def, options)) - } +module.exports = __webpack_require__(123); - return m -} +/***/ }), +/* 80 */, +/* 81 */ +/***/ (function(module, exports, __webpack_require__) { -Minimatch.defaults = function (def) { - if (!def || !Object.keys(def).length) return Minimatch - return minimatch.defaults(def).Minimatch -} +"use strict"; -function minimatch (p, pattern, options) { - if (typeof pattern !== 'string') { - throw new TypeError('glob pattern string required') - } - if (!options) options = {} +Object.defineProperty(exports, "__esModule", { + value: true +}); - // shortcut: comments match nothing. - if (!options.nocomment && pattern.charAt(0) === '#') { - return false - } +exports.default = function (str, fileLoc = 'lockfile') { + str = (0, (_stripBom || _load_stripBom()).default)(str); + return hasMergeConflicts(str) ? parseWithConflict(str, fileLoc) : { type: 'success', object: parse(str, fileLoc) }; +}; - // "" only matches "" - if (pattern.trim() === '') return p === '' +var _util; - return new Minimatch(pattern, options).match(p) +function _load_util() { + return _util = _interopRequireDefault(__webpack_require__(2)); } -function Minimatch (pattern, options) { - if (!(this instanceof Minimatch)) { - return new Minimatch(pattern, options) - } +var _invariant; - if (typeof pattern !== 'string') { - throw new TypeError('glob pattern string required') - } +function _load_invariant() { + return _invariant = _interopRequireDefault(__webpack_require__(7)); +} - if (!options) options = {} - pattern = pattern.trim() +var _stripBom; - // windows support: need to use /, not \ - if (path.sep !== '/') { - pattern = pattern.split(path.sep).join('/') - } +function _load_stripBom() { + return _stripBom = _interopRequireDefault(__webpack_require__(122)); +} - this.options = options - this.set = [] - this.pattern = pattern - this.regexp = null - this.negate = false - this.comment = false - this.empty = false +var _constants; - // make the set of regexps etc. - this.make() +function _load_constants() { + return _constants = __webpack_require__(6); } -Minimatch.prototype.debug = function () {} +var _errors; -Minimatch.prototype.make = make -function make () { - // don't do it more than once. - if (this._made) return +function _load_errors() { + return _errors = __webpack_require__(4); +} - var pattern = this.pattern - var options = this.options +var _map; - // empty patterns and comments match nothing. - if (!options.nocomment && pattern.charAt(0) === '#') { - this.comment = true - return - } - if (!pattern) { - this.empty = true - return - } +function _load_map() { + return _map = _interopRequireDefault(__webpack_require__(20)); +} - // step 1: figure out negation, etc. - this.parseNegate() +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - // step 2: expand braces - var set = this.globSet = this.braceExpand() +/* eslint quotes: 0 */ - if (options.debug) this.debug = console.error +const VERSION_REGEX = /^yarn lockfile v(\d+)$/; - this.debug(this.pattern, set) +const TOKEN_TYPES = { + boolean: 'BOOLEAN', + string: 'STRING', + identifier: 'IDENTIFIER', + eof: 'EOF', + colon: 'COLON', + newline: 'NEWLINE', + comment: 'COMMENT', + indent: 'INDENT', + invalid: 'INVALID', + number: 'NUMBER', + comma: 'COMMA' +}; - // step 3: now we have a set, so turn each one into a series of path-portion - // matching patterns. - // These will be regexps, except in the case of "**", which is - // set to the GLOBSTAR object for globstar behavior, - // and will not contain any / characters - set = this.globParts = set.map(function (s) { - return s.split(slashSplit) - }) +const VALID_PROP_VALUE_TOKENS = [TOKEN_TYPES.boolean, TOKEN_TYPES.string, TOKEN_TYPES.number]; - this.debug(this.pattern, set) +function isValidPropValueToken(token) { + return VALID_PROP_VALUE_TOKENS.indexOf(token.type) >= 0; +} - // glob --> regexps - set = set.map(function (s, si, set) { - return s.map(this.parse, this) - }, this) +function* tokenise(input) { + let lastNewline = false; + let line = 1; + let col = 0; - this.debug(this.pattern, set) + function buildToken(type, value) { + return { line, col, type, value }; + } - // filter out everything that didn't compile properly. - set = set.filter(function (s) { - return s.indexOf(false) === -1 - }) + while (input.length) { + let chop = 0; - this.debug(this.pattern, set) + if (input[0] === '\n' || input[0] === '\r') { + chop++; + // If this is a \r\n line, ignore both chars but only add one new line + if (input[1] === '\n') { + chop++; + } + line++; + col = 0; + yield buildToken(TOKEN_TYPES.newline); + } else if (input[0] === '#') { + chop++; - this.set = set -} + let val = ''; + while (input[chop] !== '\n') { + val += input[chop]; + chop++; + } + yield buildToken(TOKEN_TYPES.comment, val); + } else if (input[0] === ' ') { + if (lastNewline) { + let indent = ''; + for (let i = 0; input[i] === ' '; i++) { + indent += input[i]; + } -Minimatch.prototype.parseNegate = parseNegate -function parseNegate () { - var pattern = this.pattern - var negate = false - var options = this.options - var negateOffset = 0 + if (indent.length % 2) { + throw new TypeError('Invalid number of spaces'); + } else { + chop = indent.length; + yield buildToken(TOKEN_TYPES.indent, indent.length / 2); + } + } else { + chop++; + } + } else if (input[0] === '"') { + let val = ''; - if (options.nonegate) return + for (let i = 0;; i++) { + const currentChar = input[i]; + val += currentChar; - for (var i = 0, l = pattern.length - ; i < l && pattern.charAt(i) === '!' - ; i++) { - negate = !negate - negateOffset++ - } + if (i > 0 && currentChar === '"') { + const isEscaped = input[i - 1] === '\\' && input[i - 2] !== '\\'; + if (!isEscaped) { + break; + } + } + } - if (negateOffset) this.pattern = pattern.substr(negateOffset) - this.negate = negate -} + chop = val.length; -// Brace expansion: -// a{b,c}d -> abd acd -// a{b,}c -> abc ac -// a{0..3}d -> a0d a1d a2d a3d -// a{b,c{d,e}f}g -> abg acdfg acefg -// a{b,c}d{e,f}g -> abdeg acdeg abdeg abdfg -// -// Invalid sets are not expanded. -// a{2..}b -> a{2..}b -// a{b}c -> a{b}c -minimatch.braceExpand = function (pattern, options) { - return braceExpand(pattern, options) -} + try { + yield buildToken(TOKEN_TYPES.string, JSON.parse(val)); + } catch (err) { + if (err instanceof SyntaxError) { + yield buildToken(TOKEN_TYPES.invalid); + } else { + throw err; + } + } + } else if (/^[0-9]/.test(input)) { + let val = ''; + for (let i = 0; /^[0-9]$/.test(input[i]); i++) { + val += input[i]; + } + chop = val.length; -Minimatch.prototype.braceExpand = braceExpand + yield buildToken(TOKEN_TYPES.number, +val); + } else if (/^true/.test(input)) { + yield buildToken(TOKEN_TYPES.boolean, true); + chop = 4; + } else if (/^false/.test(input)) { + yield buildToken(TOKEN_TYPES.boolean, false); + chop = 5; + } else if (input[0] === ':') { + yield buildToken(TOKEN_TYPES.colon); + chop++; + } else if (input[0] === ',') { + yield buildToken(TOKEN_TYPES.comma); + chop++; + } else if (/^[a-zA-Z\/-]/g.test(input)) { + let name = ''; + for (let i = 0; i < input.length; i++) { + const char = input[i]; + if (char === ':' || char === ' ' || char === '\n' || char === '\r' || char === ',') { + break; + } else { + name += char; + } + } + chop = name.length; -function braceExpand (pattern, options) { - if (!options) { - if (this instanceof Minimatch) { - options = this.options + yield buildToken(TOKEN_TYPES.string, name); } else { - options = {} + yield buildToken(TOKEN_TYPES.invalid); } - } - - pattern = typeof pattern === 'undefined' - ? this.pattern : pattern - if (typeof pattern === 'undefined') { - throw new TypeError('undefined pattern') - } + if (!chop) { + // will trigger infinite recursion + yield buildToken(TOKEN_TYPES.invalid); + } - if (options.nobrace || - !pattern.match(/\{.*\}/)) { - // shortcut. no need to expand. - return [pattern] + col += chop; + lastNewline = input[0] === '\n' || input[0] === '\r' && input[1] === '\n'; + input = input.slice(chop); } - return expand(pattern) + yield buildToken(TOKEN_TYPES.eof); } -// parse a component of the expanded set. -// At this point, no pattern may contain "/" in it -// so we're going to return a 2d array, where each entry is the full -// pattern, split on '/', and then turned into a regular expression. -// A regexp is made at the end which joins each array with an -// escaped /, and another full one which joins each regexp with |. -// -// Following the lead of Bash 4.1, note that "**" only has special meaning -// when it is the *only* thing in a path portion. Otherwise, any series -// of * is equivalent to a single *. Globstar behavior is enabled by -// default, and can be disabled by setting options.noglobstar. -Minimatch.prototype.parse = parse -var SUBPARSE = {} -function parse (pattern, isSub) { - if (pattern.length > 1024 * 64) { - throw new TypeError('pattern is too long') +class Parser { + constructor(input, fileLoc = 'lockfile') { + this.comments = []; + this.tokens = tokenise(input); + this.fileLoc = fileLoc; } - var options = this.options - - // shortcuts - if (!options.noglobstar && pattern === '**') return GLOBSTAR - if (pattern === '') return '' + onComment(token) { + const value = token.value; + (0, (_invariant || _load_invariant()).default)(typeof value === 'string', 'expected token value to be a string'); - var re = '' - var hasMagic = !!options.nocase - var escaping = false - // ? => one single character - var patternListStack = [] - var negativeLists = [] - var stateChar - var inClass = false - var reClassStart = -1 - var classStart = -1 - // . and .. never match anything that doesn't start with ., - // even when options.dot is set. - var patternStart = pattern.charAt(0) === '.' ? '' // anything - // not (start or / followed by . or .. followed by / or end) - : options.dot ? '(?!(?:^|\\\/)\\.{1,2}(?:$|\\\/))' - : '(?!\\.)' - var self = this + const comment = value.trim(); - function clearStateChar () { - if (stateChar) { - // we had some state-tracking character - // that wasn't consumed by this pass. - switch (stateChar) { - case '*': - re += star - hasMagic = true - break - case '?': - re += qmark - hasMagic = true - break - default: - re += '\\' + stateChar - break + const versionMatch = comment.match(VERSION_REGEX); + if (versionMatch) { + const version = +versionMatch[1]; + if (version > (_constants || _load_constants()).LOCKFILE_VERSION) { + throw new (_errors || _load_errors()).MessageError(`Can't install from a lockfile of version ${version} as you're on an old yarn version that only supports ` + `versions up to ${(_constants || _load_constants()).LOCKFILE_VERSION}. Run \`$ yarn self-update\` to upgrade to the latest version.`); } - self.debug('clearStateChar %j %j', stateChar, re) - stateChar = false } - } - - for (var i = 0, len = pattern.length, c - ; (i < len) && (c = pattern.charAt(i)) - ; i++) { - this.debug('%s\t%s %s %j', pattern, i, re, c) - // skip over any that are escaped. - if (escaping && reSpecials[c]) { - re += '\\' + c - escaping = false - continue - } + this.comments.push(comment); + } - switch (c) { - case '/': - // completely not allowed, even escaped. - // Should already be path-split by now. - return false + next() { + const item = this.tokens.next(); + (0, (_invariant || _load_invariant()).default)(item, 'expected a token'); - case '\\': - clearStateChar() - escaping = true - continue + const done = item.done, + value = item.value; - // the various stateChar values - // for the "extglob" stuff. - case '?': - case '*': - case '+': - case '@': - case '!': - this.debug('%s\t%s %s %j <-- stateChar', pattern, i, re, c) + if (done || !value) { + throw new Error('No more tokens'); + } else if (value.type === TOKEN_TYPES.comment) { + this.onComment(value); + return this.next(); + } else { + return this.token = value; + } + } - // all of those are literals inside a class, except that - // the glob [!a] means [^a] in regexp - if (inClass) { - this.debug(' in class') - if (c === '!' && i === classStart + 1) c = '^' - re += c - continue - } + unexpected(msg = 'Unexpected token') { + throw new SyntaxError(`${msg} ${this.token.line}:${this.token.col} in ${this.fileLoc}`); + } - // if we already have a stateChar, then it means - // that there was something like ** or +? in there. - // Handle the stateChar, then proceed with this one. - self.debug('call clearStateChar %j', stateChar) - clearStateChar() - stateChar = c - // if extglob is disabled, then +(asdf|foo) isn't a thing. - // just clear the statechar *now*, rather than even diving into - // the patternList stuff. - if (options.noext) clearStateChar() - continue + expect(tokType) { + if (this.token.type === tokType) { + this.next(); + } else { + this.unexpected(); + } + } - case '(': - if (inClass) { - re += '(' - continue - } + eat(tokType) { + if (this.token.type === tokType) { + this.next(); + return true; + } else { + return false; + } + } - if (!stateChar) { - re += '\\(' - continue - } + parse(indent = 0) { + const obj = (0, (_map || _load_map()).default)(); - patternListStack.push({ - type: stateChar, - start: i - 1, - reStart: re.length, - open: plTypes[stateChar].open, - close: plTypes[stateChar].close - }) - // negation is (?:(?!js)[^/]*) - re += stateChar === '!' ? '(?:(?!(?:' : '(?:' - this.debug('plType %j %j', stateChar, re) - stateChar = false - continue + while (true) { + const propToken = this.token; - case ')': - if (inClass || !patternListStack.length) { - re += '\\)' - continue + if (propToken.type === TOKEN_TYPES.newline) { + const nextToken = this.next(); + if (!indent) { + // if we have 0 indentation then the next token doesn't matter + continue; } - clearStateChar() - hasMagic = true - var pl = patternListStack.pop() - // negation is (?:(?!js)[^/]*) - // The others are (?:) - re += pl.close - if (pl.type === '!') { - negativeLists.push(pl) + if (nextToken.type !== TOKEN_TYPES.indent) { + // if we have no indentation after a newline then we've gone down a level + break; } - pl.reEnd = re.length - continue - case '|': - if (inClass || !patternListStack.length || escaping) { - re += '\\|' - escaping = false - continue + if (nextToken.value === indent) { + // all is good, the indent is on our level + this.next(); + } else { + // the indentation is less than our level + break; } - - clearStateChar() - re += '|' - continue - - // these are mostly the same in regexp and glob - case '[': - // swallow any state-tracking char before the [ - clearStateChar() - - if (inClass) { - re += '\\' + c - continue + } else if (propToken.type === TOKEN_TYPES.indent) { + if (propToken.value === indent) { + this.next(); + } else { + break; } + } else if (propToken.type === TOKEN_TYPES.eof) { + break; + } else if (propToken.type === TOKEN_TYPES.string) { + // property key + const key = propToken.value; + (0, (_invariant || _load_invariant()).default)(key, 'Expected a key'); - inClass = true - classStart = i - reClassStart = re.length - re += c - continue + const keys = [key]; + this.next(); - case ']': - // a right bracket shall lose its special - // meaning and represent itself in - // a bracket expression if it occurs - // first in the list. -- POSIX.2 2.8.3.2 - if (i === classStart + 1 || !inClass) { - re += '\\' + c - escaping = false - continue - } + // support multiple keys + while (this.token.type === TOKEN_TYPES.comma) { + this.next(); // skip comma - // handle the case where we left a class open. - // "[z-a]" is valid, equivalent to "\[z-a\]" - if (inClass) { - // split where the last [ was, make sure we don't have - // an invalid re. if so, re-walk the contents of the - // would-be class to re-translate any characters that - // were passed through as-is - // TODO: It would probably be faster to determine this - // without a try/catch and a new RegExp, but it's tricky - // to do safely. For now, this is safe and works. - var cs = pattern.substring(classStart + 1, i) - try { - RegExp('[' + cs + ']') - } catch (er) { - // not a valid class! - var sp = this.parse(cs, SUBPARSE) - re = re.substr(0, reClassStart) + '\\[' + sp[0] + '\\]' - hasMagic = hasMagic || sp[1] - inClass = false - continue + const keyToken = this.token; + if (keyToken.type !== TOKEN_TYPES.string) { + this.unexpected('Expected string'); } - } - - // finish up the class. - hasMagic = true - inClass = false - re += c - continue - - default: - // swallow any state char that wasn't consumed - clearStateChar() - if (escaping) { - // no need - escaping = false - } else if (reSpecials[c] - && !(c === '^' && inClass)) { - re += '\\' + const key = keyToken.value; + (0, (_invariant || _load_invariant()).default)(key, 'Expected a key'); + keys.push(key); + this.next(); } - re += c - - } // switch - } // for - - // handle the case where we left a class open. - // "[abc" is valid, equivalent to "\[abc" - if (inClass) { - // split where the last [ was, and escape it - // this is a huge pita. We now have to re-walk - // the contents of the would-be class to re-translate - // any characters that were passed through as-is - cs = pattern.substr(classStart + 1) - sp = this.parse(cs, SUBPARSE) - re = re.substr(0, reClassStart) + '\\[' + sp[0] - hasMagic = hasMagic || sp[1] - } + const valToken = this.token; - // handle the case where we had a +( thing at the *end* - // of the pattern. - // each pattern list stack adds 3 chars, and we need to go through - // and escape any | chars that were passed through as-is for the regexp. - // Go through and escape them, taking care not to double-escape any - // | chars that were already escaped. - for (pl = patternListStack.pop(); pl; pl = patternListStack.pop()) { - var tail = re.slice(pl.reStart + pl.open.length) - this.debug('setting tail', re, pl) - // maybe some even number of \, then maybe 1 \, followed by a | - tail = tail.replace(/((?:\\{2}){0,64})(\\?)\|/g, function (_, $1, $2) { - if (!$2) { - // the | isn't already escaped, so escape it. - $2 = '\\' - } + if (valToken.type === TOKEN_TYPES.colon) { + // object + this.next(); - // need to escape all those slashes *again*, without escaping the - // one that we need for escaping the | character. As it works out, - // escaping an even number of slashes can be done by simply repeating - // it exactly after itself. That's why this trick works. - // - // I am sorry that you have to see this. - return $1 + $1 + $2 + '|' - }) + // parse object + const val = this.parse(indent + 1); - this.debug('tail=%j\n %s', tail, tail, pl, re) - var t = pl.type === '*' ? star - : pl.type === '?' ? qmark - : '\\' + pl.type + for (var _iterator = keys, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { + var _ref; - hasMagic = true - re = re.slice(0, pl.reStart) + t + '\\(' + tail - } + if (_isArray) { + if (_i >= _iterator.length) break; + _ref = _iterator[_i++]; + } else { + _i = _iterator.next(); + if (_i.done) break; + _ref = _i.value; + } - // handle trailing things that only matter at the very end. - clearStateChar() - if (escaping) { - // trailing \\ - re += '\\\\' - } + const key = _ref; - // only need to apply the nodot start if the re starts with - // something that could conceivably capture a dot - var addPatternStart = false - switch (re.charAt(0)) { - case '.': - case '[': - case '(': addPatternStart = true - } + obj[key] = val; + } - // Hack to work around lack of negative lookbehind in JS - // A pattern like: *.!(x).!(y|z) needs to ensure that a name - // like 'a.xyz.yz' doesn't match. So, the first negative - // lookahead, has to look ALL the way ahead, to the end of - // the pattern. - for (var n = negativeLists.length - 1; n > -1; n--) { - var nl = negativeLists[n] + if (indent && this.token.type !== TOKEN_TYPES.indent) { + break; + } + } else if (isValidPropValueToken(valToken)) { + // plain value + for (var _iterator2 = keys, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) { + var _ref2; - var nlBefore = re.slice(0, nl.reStart) - var nlFirst = re.slice(nl.reStart, nl.reEnd - 8) - var nlLast = re.slice(nl.reEnd - 8, nl.reEnd) - var nlAfter = re.slice(nl.reEnd) + if (_isArray2) { + if (_i2 >= _iterator2.length) break; + _ref2 = _iterator2[_i2++]; + } else { + _i2 = _iterator2.next(); + if (_i2.done) break; + _ref2 = _i2.value; + } - nlLast += nlAfter + const key = _ref2; - // Handle nested stuff like *(*.js|!(*.json)), where open parens - // mean that we should *not* include the ) in the bit that is considered - // "after" the negated section. - var openParensBefore = nlBefore.split('(').length - 1 - var cleanAfter = nlAfter - for (i = 0; i < openParensBefore; i++) { - cleanAfter = cleanAfter.replace(/\)[+*?]?/, '') - } - nlAfter = cleanAfter + obj[key] = valToken.value; + } - var dollar = '' - if (nlAfter === '' && isSub !== SUBPARSE) { - dollar = '$' + this.next(); + } else { + this.unexpected('Invalid value type'); + } + } else { + this.unexpected(`Unknown token: ${(_util || _load_util()).default.inspect(propToken)}`); + } } - var newRe = nlBefore + nlFirst + nlAfter + dollar + nlLast - re = newRe - } - // if the re is not "" at this point, then we need to make sure - // it doesn't match against an empty path part. - // Otherwise a/* will match a/, which it should not. - if (re !== '' && hasMagic) { - re = '(?=.)' + re + return obj; } +} - if (addPatternStart) { - re = patternStart + re - } +const MERGE_CONFLICT_ANCESTOR = '|||||||'; +const MERGE_CONFLICT_END = '>>>>>>>'; +const MERGE_CONFLICT_SEP = '======='; +const MERGE_CONFLICT_START = '<<<<<<<'; - // parsing just a piece of a larger pattern. - if (isSub === SUBPARSE) { - return [re, hasMagic] - } +/** + * Extract the two versions of the lockfile from a merge conflict. + */ +function extractConflictVariants(str) { + const variants = [[], []]; + const lines = str.split(/\r?\n/g); + let skip = false; - // skip the regexp for non-magical patterns - // unescape anything in it, though, so that it'll be - // an exact match against a file etc. - if (!hasMagic) { - return globUnescape(pattern) - } + while (lines.length) { + const line = lines.shift(); + if (line.startsWith(MERGE_CONFLICT_START)) { + // get the first variant + while (lines.length) { + const conflictLine = lines.shift(); + if (conflictLine === MERGE_CONFLICT_SEP) { + skip = false; + break; + } else if (skip || conflictLine.startsWith(MERGE_CONFLICT_ANCESTOR)) { + skip = true; + continue; + } else { + variants[0].push(conflictLine); + } + } - var flags = options.nocase ? 'i' : '' - try { - var regExp = new RegExp('^' + re + '$', flags) - } catch (er) { - // If it was an invalid regular expression, then it can't match - // anything. This trick looks for a character after the end of - // the string, which is of course impossible, except in multi-line - // mode, but it's not a /m regex. - return new RegExp('$.') + // get the second variant + while (lines.length) { + const conflictLine = lines.shift(); + if (conflictLine.startsWith(MERGE_CONFLICT_END)) { + break; + } else { + variants[1].push(conflictLine); + } + } + } else { + variants[0].push(line); + variants[1].push(line); + } } - regExp._glob = pattern - regExp._src = re - - return regExp + return [variants[0].join('\n'), variants[1].join('\n')]; } -minimatch.makeRe = function (pattern, options) { - return new Minimatch(pattern, options || {}).makeRe() +/** + * Check if a lockfile has merge conflicts. + */ +function hasMergeConflicts(str) { + return str.includes(MERGE_CONFLICT_START) && str.includes(MERGE_CONFLICT_SEP) && str.includes(MERGE_CONFLICT_END); } -Minimatch.prototype.makeRe = makeRe -function makeRe () { - if (this.regexp || this.regexp === false) return this.regexp - - // at this point, this.set is a 2d array of partial - // pattern strings, or "**". - // - // It's better to use .match(). This function shouldn't - // be used, really, but it's pretty convenient sometimes, - // when you just want to work with a regex. - var set = this.set +/** + * Parse the lockfile. + */ +function parse(str, fileLoc) { + const parser = new Parser(str, fileLoc); + parser.next(); + return parser.parse(); +} - if (!set.length) { - this.regexp = false - return this.regexp +/** + * Parse and merge the two variants in a conflicted lockfile. + */ +function parseWithConflict(str, fileLoc) { + const variants = extractConflictVariants(str); + try { + return { type: 'merge', object: Object.assign({}, parse(variants[0], fileLoc), parse(variants[1], fileLoc)) }; + } catch (err) { + if (err instanceof SyntaxError) { + return { type: 'conflict', object: {} }; + } else { + throw err; + } } - var options = this.options +} - var twoStar = options.noglobstar ? star - : options.dot ? twoStarDot - : twoStarNoDot - var flags = options.nocase ? 'i' : '' +/***/ }), +/* 82 */, +/* 83 */, +/* 84 */ +/***/ (function(module, exports, __webpack_require__) { - var re = set.map(function (pattern) { - return pattern.map(function (p) { - return (p === GLOBSTAR) ? twoStar - : (typeof p === 'string') ? regExpEscape(p) - : p._src - }).join('\\\/') - }).join('|') +"use strict"; - // must match entire pattern - // ending in a * or ** will make it less strict. - re = '^(?:' + re + ')$' - // can match anything, as long as it's not this. - if (this.negate) re = '^(?!' + re + ').*$' +Object.defineProperty(exports, "__esModule", { + value: true +}); - try { - this.regexp = new RegExp(re, flags) - } catch (ex) { - this.regexp = false - } - return this.regexp -} +var _map; -minimatch.match = function (list, pattern, options) { - options = options || {} - var mm = new Minimatch(pattern, options) - list = list.filter(function (f) { - return mm.match(f) - }) - if (mm.options.nonull && !list.length) { - list.push(pattern) - } - return list +function _load_map() { + return _map = _interopRequireDefault(__webpack_require__(20)); } -Minimatch.prototype.match = match -function match (f, partial) { - this.debug('match', f, this.pattern) - // short-circuit in the case of busted things. - // comments, etc. - if (this.comment) return false - if (this.empty) return f === '' +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - if (f === '/' && partial) return true +const debug = __webpack_require__(212)('yarn'); - var options = this.options +class BlockingQueue { + constructor(alias, maxConcurrency = Infinity) { + this.concurrencyQueue = []; + this.maxConcurrency = maxConcurrency; + this.runningCount = 0; + this.warnedStuck = false; + this.alias = alias; + this.first = true; - // windows: need to use /, not \ - if (path.sep !== '/') { - f = f.split(path.sep).join('/') - } + this.running = (0, (_map || _load_map()).default)(); + this.queue = (0, (_map || _load_map()).default)(); - // treat the test path as a set of pathparts. - f = f.split(slashSplit) - this.debug(this.pattern, 'split', f) + this.stuckTick = this.stuckTick.bind(this); + } - // just ONE of the pattern sets in this.set needs to match - // in order for it to be valid. If negating, then just one - // match means that we have failed. - // Either way, return on the first hit. + stillActive() { + if (this.stuckTimer) { + clearTimeout(this.stuckTimer); + } - var set = this.set - this.debug(this.pattern, 'set', set) + this.stuckTimer = setTimeout(this.stuckTick, 5000); - // Find the basename of the path by looking for the last non-empty segment - var filename - var i - for (i = f.length - 1; i >= 0; i--) { - filename = f[i] - if (filename) break + // We need to check the existence of unref because of https://github.com/facebook/jest/issues/4559 + // $FlowFixMe: Node's setInterval returns a Timeout, not a Number + this.stuckTimer.unref && this.stuckTimer.unref(); } - for (i = 0; i < set.length; i++) { - var pattern = set[i] - var file = f - if (options.matchBase && pattern.length === 1) { - file = [filename] - } - var hit = this.matchOne(file, pattern, partial) - if (hit) { - if (options.flipNegate) return true - return !this.negate + stuckTick() { + if (this.runningCount === 1) { + this.warnedStuck = true; + debug(`The ${JSON.stringify(this.alias)} blocking queue may be stuck. 5 seconds ` + `without any activity with 1 worker: ${Object.keys(this.running)[0]}`); } } - // didn't get any hits. this is success if it's a negative - // pattern, failure otherwise. - if (options.flipNegate) return false - return this.negate -} - -// set partial to true to test if, for example, -// "/a/b" matches the start of "/*/b/*/d" -// Partial means, if you run out of file before you run -// out of pattern, then that's fine, as long as all -// the parts match. -Minimatch.prototype.matchOne = function (file, pattern, partial) { - var options = this.options - - this.debug('matchOne', - { 'this': this, file: file, pattern: pattern }) - - this.debug('matchOne', file.length, pattern.length) - - for (var fi = 0, - pi = 0, - fl = file.length, - pl = pattern.length - ; (fi < fl) && (pi < pl) - ; fi++, pi++) { - this.debug('matchOne loop') - var p = pattern[pi] - var f = file[fi] + push(key, factory) { + if (this.first) { + this.first = false; + } else { + this.stillActive(); + } - this.debug(pattern, p, f) + return new Promise((resolve, reject) => { + // we're already running so push ourselves to the queue + const queue = this.queue[key] = this.queue[key] || []; + queue.push({ factory, resolve, reject }); - // should be impossible. - // some invalid regexp stuff in the set. - if (p === false) return false + if (!this.running[key]) { + this.shift(key); + } + }); + } - if (p === GLOBSTAR) { - this.debug('GLOBSTAR', [pattern, p, f]) + shift(key) { + if (this.running[key]) { + delete this.running[key]; + this.runningCount--; - // "**" - // a/**/b/**/c would match the following: - // a/b/x/y/z/c - // a/x/y/z/b/c - // a/b/x/b/x/c - // a/b/c - // To do this, take the rest of the pattern after - // the **, and see if it would match the file remainder. - // If so, return success. - // If not, the ** "swallows" a segment, and try again. - // This is recursively awful. - // - // a/**/b/**/c matching a/b/x/y/z/c - // - a matches a - // - doublestar - // - matchOne(b/x/y/z/c, b/**/c) - // - b matches b - // - doublestar - // - matchOne(x/y/z/c, c) -> no - // - matchOne(y/z/c, c) -> no - // - matchOne(z/c, c) -> no - // - matchOne(c, c) yes, hit - var fr = fi - var pr = pi + 1 - if (pr === pl) { - this.debug('** at the end') - // a ** at the end will just swallow the rest. - // We have found a match. - // however, it will not swallow /.x, unless - // options.dot is set. - // . and .. are *never* matched by **, for explosively - // exponential reasons. - for (; fi < fl; fi++) { - if (file[fi] === '.' || file[fi] === '..' || - (!options.dot && file[fi].charAt(0) === '.')) return false - } - return true + if (this.stuckTimer) { + clearTimeout(this.stuckTimer); + this.stuckTimer = null; } - // ok, let's see if we can swallow whatever we can. - while (fr < fl) { - var swallowee = file[fr] + if (this.warnedStuck) { + this.warnedStuck = false; + debug(`${JSON.stringify(this.alias)} blocking queue finally resolved. Nothing to worry about.`); + } + } - this.debug('\nglobstar while', file, fr, pattern, pr, swallowee) + const queue = this.queue[key]; + if (!queue) { + return; + } - // XXX remove this slice. Just pass the start index. - if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) { - this.debug('globstar found match!', fr, fl, swallowee) - // found a match. - return true - } else { - // can't swallow "." or ".." ever. - // can only swallow ".foo" when explicitly asked. - if (swallowee === '.' || swallowee === '..' || - (!options.dot && swallowee.charAt(0) === '.')) { - this.debug('dot detected!', file, fr, pattern, pr) - break - } + var _queue$shift = queue.shift(); - // ** swallows a segment, and continue. - this.debug('globstar swallow a segment, and continue') - fr++ - } - } + const resolve = _queue$shift.resolve, + reject = _queue$shift.reject, + factory = _queue$shift.factory; - // no match was found. - // However, in partial mode, we can't say this is necessarily over. - // If there's more *pattern* left, then - if (partial) { - // ran out of file - this.debug('\n>>> no match, partial?', file, fr, pattern, pr) - if (fr === fl) return true - } - return false + if (!queue.length) { + delete this.queue[key]; } - // something other than ** - // non-magic patterns just have to match exactly - // patterns with magic have been turned into regexps. - var hit - if (typeof p === 'string') { - if (options.nocase) { - hit = f.toLowerCase() === p.toLowerCase() - } else { - hit = f === p - } - this.debug('string match', p, f, hit) - } else { - hit = f.match(p) - this.debug('pattern match', p, f, hit) - } + const next = () => { + this.shift(key); + this.shiftConcurrencyQueue(); + }; - if (!hit) return false - } + const run = () => { + this.running[key] = true; + this.runningCount++; - // Note: ending in / means that we'll get a final "" - // at the end of the pattern. This can only match a - // corresponding "" at the end of the file. - // If the file ends in /, then it can only match a - // a pattern that ends in /, unless the pattern just - // doesn't have any more for it. But, a/b/ should *not* - // match "a/b/*", even though "" matches against the - // [^/]*? pattern, except in partial mode, where it might - // simply not be reached yet. - // However, a/b/ should still satisfy a/* + factory().then(function (val) { + resolve(val); + next(); + return null; + }).catch(function (err) { + reject(err); + next(); + }); + }; - // now either we fell off the end of the pattern, or we're done. - if (fi === fl && pi === pl) { - // ran out of pattern and filename at the same time. - // an exact hit! - return true - } else if (fi === fl) { - // ran out of file, but still had pattern left. - // this is ok if we're doing the match as part of - // a glob fs traversal. - return partial - } else if (pi === pl) { - // ran out of pattern, still have file left. - // this is only acceptable if we're on the very last - // empty segment of a file with a trailing slash. - // a/* should match a/b/ - var emptyFileEnd = (fi === fl - 1) && (file[fi] === '') - return emptyFileEnd + this.maybePushConcurrencyQueue(run); + } + + maybePushConcurrencyQueue(run) { + if (this.runningCount < this.maxConcurrency) { + run(); + } else { + this.concurrencyQueue.push(run); + } } - // should be unreachable. - throw new Error('wtf?') + shiftConcurrencyQueue() { + if (this.runningCount < this.maxConcurrency) { + const fn = this.concurrencyQueue.shift(); + if (fn) { + fn(); + } + } + } } +exports.default = BlockingQueue; -// replace stuff like \* with * -function globUnescape (s) { - return s.replace(/\\(.)/g, '$1') -} +/***/ }), +/* 85 */ +/***/ (function(module, exports) { -function regExpEscape (s) { - return s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&') -} +module.exports = function (exec) { + try { + return !!exec(); + } catch (e) { + return true; + } +}; /***/ }), -/* 61 */ +/* 86 */, +/* 87 */, +/* 88 */, +/* 89 */, +/* 90 */, +/* 91 */, +/* 92 */, +/* 93 */, +/* 94 */, +/* 95 */, +/* 96 */, +/* 97 */, +/* 98 */, +/* 99 */, +/* 100 */ /***/ (function(module, exports, __webpack_require__) { -var wrappy = __webpack_require__(123) -module.exports = wrappy(once) -module.exports.strict = wrappy(onceStrict) - -once.proto = once(function () { - Object.defineProperty(Function.prototype, 'once', { - value: function () { - return once(this) - }, - configurable: true - }) - - Object.defineProperty(Function.prototype, 'onceStrict', { - value: function () { - return onceStrict(this) - }, - configurable: true - }) -}) +// getting tag from 19.1.3.6 Object.prototype.toString() +var cof = __webpack_require__(47); +var TAG = __webpack_require__(13)('toStringTag'); +// ES3 wrong here +var ARG = cof(function () { return arguments; }()) == 'Arguments'; -function once (fn) { - var f = function () { - if (f.called) return f.value - f.called = true - return f.value = fn.apply(this, arguments) - } - f.called = false - return f -} +// fallback for IE11 Script Access Denied error +var tryGet = function (it, key) { + try { + return it[key]; + } catch (e) { /* empty */ } +}; -function onceStrict (fn) { - var f = function () { - if (f.called) - throw new Error(f.onceError) - f.called = true - return f.value = fn.apply(this, arguments) - } - var name = fn.name || 'Function wrapped with `once`' - f.onceError = name + " shouldn't be called more than once" - f.called = false - return f -} +module.exports = function (it) { + var O, T, B; + return it === undefined ? 'Undefined' : it === null ? 'Null' + // @@toStringTag case + : typeof (T = tryGet(O = Object(it), TAG)) == 'string' ? T + // builtinTag case + : ARG ? cof(O) + // ES3 arguments fallback + : (B = cof(O)) == 'Object' && typeof O.callee == 'function' ? 'Arguments' : B; +}; /***/ }), -/* 62 */, -/* 63 */ +/* 101 */ /***/ (function(module, exports) { -module.exports = __webpack_require__(410); +// IE 8- don't enum bug keys +module.exports = ( + 'constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf' +).split(','); + /***/ }), -/* 64 */, -/* 65 */, -/* 66 */, -/* 67 */ -/***/ (function(module, exports) { +/* 102 */ +/***/ (function(module, exports, __webpack_require__) { -// 7.2.1 RequireObjectCoercible(argument) -module.exports = function (it) { - if (it == undefined) throw TypeError("Can't call method on " + it); - return it; -}; +var document = __webpack_require__(11).document; +module.exports = document && document.documentElement; /***/ }), -/* 68 */ +/* 103 */ /***/ (function(module, exports, __webpack_require__) { -var isObject = __webpack_require__(34); -var document = __webpack_require__(11).document; -// typeof document.createElement is 'object' in old IE -var is = isObject(document) && isObject(document.createElement); -module.exports = function (it) { - return is ? document.createElement(it) : {}; +"use strict"; + +var LIBRARY = __webpack_require__(69); +var $export = __webpack_require__(41); +var redefine = __webpack_require__(197); +var hide = __webpack_require__(31); +var Iterators = __webpack_require__(35); +var $iterCreate = __webpack_require__(188); +var setToStringTag = __webpack_require__(71); +var getPrototypeOf = __webpack_require__(194); +var ITERATOR = __webpack_require__(13)('iterator'); +var BUGGY = !([].keys && 'next' in [].keys()); // Safari has buggy iterators w/o `next` +var FF_ITERATOR = '@@iterator'; +var KEYS = 'keys'; +var VALUES = 'values'; + +var returnThis = function () { return this; }; + +module.exports = function (Base, NAME, Constructor, next, DEFAULT, IS_SET, FORCED) { + $iterCreate(Constructor, NAME, next); + var getMethod = function (kind) { + if (!BUGGY && kind in proto) return proto[kind]; + switch (kind) { + case KEYS: return function keys() { return new Constructor(this, kind); }; + case VALUES: return function values() { return new Constructor(this, kind); }; + } return function entries() { return new Constructor(this, kind); }; + }; + var TAG = NAME + ' Iterator'; + var DEF_VALUES = DEFAULT == VALUES; + var VALUES_BUG = false; + var proto = Base.prototype; + var $native = proto[ITERATOR] || proto[FF_ITERATOR] || DEFAULT && proto[DEFAULT]; + var $default = $native || getMethod(DEFAULT); + var $entries = DEFAULT ? !DEF_VALUES ? $default : getMethod('entries') : undefined; + var $anyNative = NAME == 'Array' ? proto.entries || $native : $native; + var methods, key, IteratorPrototype; + // Fix native + if ($anyNative) { + IteratorPrototype = getPrototypeOf($anyNative.call(new Base())); + if (IteratorPrototype !== Object.prototype && IteratorPrototype.next) { + // Set @@toStringTag to native iterators + setToStringTag(IteratorPrototype, TAG, true); + // fix for some old engines + if (!LIBRARY && typeof IteratorPrototype[ITERATOR] != 'function') hide(IteratorPrototype, ITERATOR, returnThis); + } + } + // fix Array#{values, @@iterator}.name in V8 / FF + if (DEF_VALUES && $native && $native.name !== VALUES) { + VALUES_BUG = true; + $default = function values() { return $native.call(this); }; + } + // Define iterator + if ((!LIBRARY || FORCED) && (BUGGY || VALUES_BUG || !proto[ITERATOR])) { + hide(proto, ITERATOR, $default); + } + // Plug for library + Iterators[NAME] = $default; + Iterators[TAG] = returnThis; + if (DEFAULT) { + methods = { + values: DEF_VALUES ? $default : getMethod(VALUES), + keys: IS_SET ? $default : getMethod(KEYS), + entries: $entries + }; + if (FORCED) for (key in methods) { + if (!(key in proto)) redefine(proto, key, methods[key]); + } else $export($export.P + $export.F * (BUGGY || VALUES_BUG), NAME, methods); + } + return methods; }; /***/ }), -/* 69 */ +/* 104 */ /***/ (function(module, exports) { -module.exports = true; +module.exports = function (exec) { + try { + return { e: false, v: exec() }; + } catch (e) { + return { e: true, v: e }; + } +}; /***/ }), -/* 70 */ +/* 105 */ /***/ (function(module, exports, __webpack_require__) { -"use strict"; +var anObject = __webpack_require__(27); +var isObject = __webpack_require__(34); +var newPromiseCapability = __webpack_require__(70); -// 25.4.1.5 NewPromiseCapability(C) -var aFunction = __webpack_require__(46); +module.exports = function (C, x) { + anObject(C); + if (isObject(x) && x.constructor === C) return x; + var promiseCapability = newPromiseCapability.f(C); + var resolve = promiseCapability.resolve; + resolve(x); + return promiseCapability.promise; +}; -function PromiseCapability(C) { - var resolve, reject; - this.promise = new C(function ($$resolve, $$reject) { - if (resolve !== undefined || reject !== undefined) throw TypeError('Bad Promise constructor'); - resolve = $$resolve; - reject = $$reject; - }); - this.resolve = aFunction(resolve); - this.reject = aFunction(reject); -} -module.exports.f = function (C) { - return new PromiseCapability(C); +/***/ }), +/* 106 */ +/***/ (function(module, exports) { + +module.exports = function (bitmap, value) { + return { + enumerable: !(bitmap & 1), + configurable: !(bitmap & 2), + writable: !(bitmap & 4), + value: value + }; }; /***/ }), -/* 71 */ +/* 107 */ /***/ (function(module, exports, __webpack_require__) { -var def = __webpack_require__(50).f; -var has = __webpack_require__(49); -var TAG = __webpack_require__(13)('toStringTag'); +var core = __webpack_require__(23); +var global = __webpack_require__(11); +var SHARED = '__core-js_shared__'; +var store = global[SHARED] || (global[SHARED] = {}); -module.exports = function (it, tag, stat) { - if (it && !has(it = stat ? it : it.prototype, TAG)) def(it, TAG, { configurable: true, value: tag }); -}; +(module.exports = function (key, value) { + return store[key] || (store[key] = value !== undefined ? value : {}); +})('versions', []).push({ + version: core.version, + mode: __webpack_require__(69) ? 'pure' : 'global', + copyright: '© 2018 Denis Pushkarev (zloirock.ru)' +}); /***/ }), -/* 72 */ +/* 108 */ /***/ (function(module, exports, __webpack_require__) { -var shared = __webpack_require__(107)('keys'); -var uid = __webpack_require__(111); -module.exports = function (key) { - return shared[key] || (shared[key] = uid(key)); +// 7.3.20 SpeciesConstructor(O, defaultConstructor) +var anObject = __webpack_require__(27); +var aFunction = __webpack_require__(46); +var SPECIES = __webpack_require__(13)('species'); +module.exports = function (O, D) { + var C = anObject(O).constructor; + var S; + return C === undefined || (S = anObject(C)[SPECIES]) == undefined ? D : aFunction(S); }; /***/ }), -/* 73 */ -/***/ (function(module, exports) { +/* 109 */ +/***/ (function(module, exports, __webpack_require__) { -// 7.1.4 ToInteger -var ceil = Math.ceil; -var floor = Math.floor; -module.exports = function (it) { - return isNaN(it = +it) ? 0 : (it > 0 ? floor : ceil)(it); +var ctx = __webpack_require__(48); +var invoke = __webpack_require__(185); +var html = __webpack_require__(102); +var cel = __webpack_require__(68); +var global = __webpack_require__(11); +var process = global.process; +var setTask = global.setImmediate; +var clearTask = global.clearImmediate; +var MessageChannel = global.MessageChannel; +var Dispatch = global.Dispatch; +var counter = 0; +var queue = {}; +var ONREADYSTATECHANGE = 'onreadystatechange'; +var defer, channel, port; +var run = function () { + var id = +this; + // eslint-disable-next-line no-prototype-builtins + if (queue.hasOwnProperty(id)) { + var fn = queue[id]; + delete queue[id]; + fn(); + } +}; +var listener = function (event) { + run.call(event.data); +}; +// Node.js 0.9+ & IE10+ has setImmediate, otherwise: +if (!setTask || !clearTask) { + setTask = function setImmediate(fn) { + var args = []; + var i = 1; + while (arguments.length > i) args.push(arguments[i++]); + queue[++counter] = function () { + // eslint-disable-next-line no-new-func + invoke(typeof fn == 'function' ? fn : Function(fn), args); + }; + defer(counter); + return counter; + }; + clearTask = function clearImmediate(id) { + delete queue[id]; + }; + // Node.js 0.8- + if (__webpack_require__(47)(process) == 'process') { + defer = function (id) { + process.nextTick(ctx(run, id, 1)); + }; + // Sphere (JS game engine) Dispatch API + } else if (Dispatch && Dispatch.now) { + defer = function (id) { + Dispatch.now(ctx(run, id, 1)); + }; + // Browsers with MessageChannel, includes WebWorkers + } else if (MessageChannel) { + channel = new MessageChannel(); + port = channel.port2; + channel.port1.onmessage = listener; + defer = ctx(port.postMessage, port, 1); + // Browsers with postMessage, skip WebWorkers + // IE8 has postMessage, but it's sync & typeof its postMessage is 'object' + } else if (global.addEventListener && typeof postMessage == 'function' && !global.importScripts) { + defer = function (id) { + global.postMessage(id + '', '*'); + }; + global.addEventListener('message', listener, false); + // IE8- + } else if (ONREADYSTATECHANGE in cel('script')) { + defer = function (id) { + html.appendChild(cel('script'))[ONREADYSTATECHANGE] = function () { + html.removeChild(this); + run.call(id); + }; + }; + // Rest old browsers + } else { + defer = function (id) { + setTimeout(ctx(run, id, 1), 0); + }; + } +} +module.exports = { + set: setTask, + clear: clearTask }; /***/ }), -/* 74 */ +/* 110 */ /***/ (function(module, exports, __webpack_require__) { -// to indexed object, toObject with fallback for non-array-like ES3 strings -var IObject = __webpack_require__(131); -var defined = __webpack_require__(67); +// 7.1.15 ToLength +var toInteger = __webpack_require__(73); +var min = Math.min; module.exports = function (it) { - return IObject(defined(it)); + return it > 0 ? min(toInteger(it), 0x1fffffffffffff) : 0; // pow(2, 53) - 1 == 9007199254740991 }; /***/ }), -/* 75 */ -/***/ (function(module, exports, __webpack_require__) { - -// Approach: -// -// 1. Get the minimatch set -// 2. For each pattern in the set, PROCESS(pattern, false) -// 3. Store matches per-set, then uniq them -// -// PROCESS(pattern, inGlobStar) -// Get the first [n] items from pattern that are all strings -// Join these together. This is PREFIX. -// If there is no more remaining, then stat(PREFIX) and -// add to matches if it succeeds. END. -// -// If inGlobStar and PREFIX is symlink and points to dir -// set ENTRIES = [] -// else readdir(PREFIX) as ENTRIES -// If fail, END -// -// with ENTRIES -// If pattern[n] is GLOBSTAR -// // handle the case where the globstar match is empty -// // by pruning it out, and testing the resulting pattern -// PROCESS(pattern[0..n] + pattern[n+1 .. $], false) -// // handle other cases. -// for ENTRY in ENTRIES (not dotfiles) -// // attach globstar + tail onto the entry -// // Mark that this entry is a globstar match -// PROCESS(pattern[0..n] + ENTRY + pattern[n .. $], true) -// -// else // not globstar -// for ENTRY in ENTRIES (not dotfiles, unless pattern[n] is dot) -// Test ENTRY against pattern[n] -// If fails, continue -// If passes, PROCESS(pattern[0..n] + item + pattern[n+1 .. $]) -// -// Caveat: -// Cache all stats and readdirs results to minimize syscall. Since all -// we ever care about is existence and directory-ness, we can just keep -// `true` for files, and [children,...] for directories, or `false` for -// things that don't exist. - -module.exports = glob - -var fs = __webpack_require__(3) -var rp = __webpack_require__(114) -var minimatch = __webpack_require__(60) -var Minimatch = minimatch.Minimatch -var inherits = __webpack_require__(42) -var EE = __webpack_require__(54).EventEmitter -var path = __webpack_require__(0) -var assert = __webpack_require__(22) -var isAbsolute = __webpack_require__(76) -var globSync = __webpack_require__(218) -var common = __webpack_require__(115) -var alphasort = common.alphasort -var alphasorti = common.alphasorti -var setopts = common.setopts -var ownProp = common.ownProp -var inflight = __webpack_require__(223) -var util = __webpack_require__(2) -var childrenIgnored = common.childrenIgnored -var isIgnored = common.isIgnored - -var once = __webpack_require__(61) +/* 111 */ +/***/ (function(module, exports) { -function glob (pattern, options, cb) { - if (typeof options === 'function') cb = options, options = {} - if (!options) options = {} +var id = 0; +var px = Math.random(); +module.exports = function (key) { + return 'Symbol('.concat(key === undefined ? '' : key, ')_', (++id + px).toString(36)); +}; - if (options.sync) { - if (cb) - throw new TypeError('callback provided to sync glob') - return globSync(pattern, options) - } - return new Glob(pattern, options, cb) -} +/***/ }), +/* 112 */ +/***/ (function(module, exports, __webpack_require__) { -glob.sync = globSync -var GlobSync = glob.GlobSync = globSync.GlobSync -// old api surface -glob.glob = glob +/** + * This is the common logic for both the Node.js and web browser + * implementations of `debug()`. + * + * Expose `debug()` as the module. + */ -function extend (origin, add) { - if (add === null || typeof add !== 'object') { - return origin - } +exports = module.exports = createDebug.debug = createDebug['default'] = createDebug; +exports.coerce = coerce; +exports.disable = disable; +exports.enable = enable; +exports.enabled = enabled; +exports.humanize = __webpack_require__(229); - var keys = Object.keys(add) - var i = keys.length - while (i--) { - origin[keys[i]] = add[keys[i]] - } - return origin -} +/** + * Active `debug` instances. + */ +exports.instances = []; -glob.hasMagic = function (pattern, options_) { - var options = extend({}, options_) - options.noprocess = true +/** + * The currently active debug mode names, and names to skip. + */ - var g = new Glob(pattern, options) - var set = g.minimatch.set +exports.names = []; +exports.skips = []; - if (!pattern) - return false +/** + * Map of special "%n" handling functions, for the debug "format" argument. + * + * Valid key names are a single, lower or upper-case letter, i.e. "n" and "N". + */ - if (set.length > 1) - return true +exports.formatters = {}; - for (var j = 0; j < set[0].length; j++) { - if (typeof set[0][j] !== 'string') - return true - } +/** + * Select a color. + * @param {String} namespace + * @return {Number} + * @api private + */ - return false -} +function selectColor(namespace) { + var hash = 0, i; -glob.Glob = Glob -inherits(Glob, EE) -function Glob (pattern, options, cb) { - if (typeof options === 'function') { - cb = options - options = null + for (i in namespace) { + hash = ((hash << 5) - hash) + namespace.charCodeAt(i); + hash |= 0; // Convert to 32bit integer } - if (options && options.sync) { - if (cb) - throw new TypeError('callback provided to sync glob') - return new GlobSync(pattern, options) - } + return exports.colors[Math.abs(hash) % exports.colors.length]; +} - if (!(this instanceof Glob)) - return new Glob(pattern, options, cb) +/** + * Create a debugger with the given `namespace`. + * + * @param {String} namespace + * @return {Function} + * @api public + */ - setopts(this, pattern, options) - this._didRealPath = false +function createDebug(namespace) { - // process each pattern in the minimatch set - var n = this.minimatch.set.length + var prevTime; - // The matches are stored as {: true,...} so that - // duplicates are automagically pruned. - // Later, we do an Object.keys() on these. - // Keep them as a list so we can fill in when nonull is set. - this.matches = new Array(n) + function debug() { + // disabled? + if (!debug.enabled) return; - if (typeof cb === 'function') { - cb = once(cb) - this.on('error', cb) - this.on('end', function (matches) { - cb(null, matches) - }) - } + var self = debug; - var self = this - this._processing = 0 + // set `diff` timestamp + var curr = +new Date(); + var ms = curr - (prevTime || curr); + self.diff = ms; + self.prev = prevTime; + self.curr = curr; + prevTime = curr; - this._emitQueue = [] - this._processQueue = [] - this.paused = false + // turn the `arguments` into a proper Array + var args = new Array(arguments.length); + for (var i = 0; i < args.length; i++) { + args[i] = arguments[i]; + } - if (this.noprocess) - return this + args[0] = exports.coerce(args[0]); - if (n === 0) - return done() + if ('string' !== typeof args[0]) { + // anything else let's inspect with %O + args.unshift('%O'); + } - var sync = true - for (var i = 0; i < n; i ++) { - this._process(this.minimatch.set[i], i, false, done) - } - sync = false + // apply any `formatters` transformations + var index = 0; + args[0] = args[0].replace(/%([a-zA-Z%])/g, function(match, format) { + // if we encounter an escaped % then don't increase the array index + if (match === '%%') return match; + index++; + var formatter = exports.formatters[format]; + if ('function' === typeof formatter) { + var val = args[index]; + match = formatter.call(self, val); - function done () { - --self._processing - if (self._processing <= 0) { - if (sync) { - process.nextTick(function () { - self._finish() - }) - } else { - self._finish() + // now we need to remove `args[index]` since it's inlined in the `format` + args.splice(index, 1); + index--; } - } - } -} - -Glob.prototype._finish = function () { - assert(this instanceof Glob) - if (this.aborted) - return + return match; + }); - if (this.realpath && !this._didRealpath) - return this._realpath() + // apply env-specific formatting (colors, etc.) + exports.formatArgs.call(self, args); - common.finish(this) - this.emit('end', this.found) -} + var logFn = debug.log || exports.log || console.log.bind(console); + logFn.apply(self, args); + } -Glob.prototype._realpath = function () { - if (this._didRealpath) - return + debug.namespace = namespace; + debug.enabled = exports.enabled(namespace); + debug.useColors = exports.useColors(); + debug.color = selectColor(namespace); + debug.destroy = destroy; - this._didRealpath = true + // env-specific initialization logic for debug instances + if ('function' === typeof exports.init) { + exports.init(debug); + } - var n = this.matches.length - if (n === 0) - return this._finish() + exports.instances.push(debug); - var self = this - for (var i = 0; i < this.matches.length; i++) - this._realpathSet(i, next) + return debug; +} - function next () { - if (--n === 0) - self._finish() +function destroy () { + var index = exports.instances.indexOf(this); + if (index !== -1) { + exports.instances.splice(index, 1); + return true; + } else { + return false; } } -Glob.prototype._realpathSet = function (index, cb) { - var matchset = this.matches[index] - if (!matchset) - return cb() +/** + * Enables a debug mode by namespaces. This can include modes + * separated by a colon and wildcards. + * + * @param {String} namespaces + * @api public + */ - var found = Object.keys(matchset) - var self = this - var n = found.length +function enable(namespaces) { + exports.save(namespaces); - if (n === 0) - return cb() + exports.names = []; + exports.skips = []; - var set = this.matches[index] = Object.create(null) - found.forEach(function (p, i) { - // If there's a problem with the stat, then it means that - // one or more of the links in the realpath couldn't be - // resolved. just return the abs value in that case. - p = self._makeAbs(p) - rp.realpath(p, self.realpathCache, function (er, real) { - if (!er) - set[real] = true - else if (er.syscall === 'stat') - set[p] = true - else - self.emit('error', er) // srsly wtf right here + var i; + var split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/); + var len = split.length; - if (--n === 0) { - self.matches[index] = set - cb() - } - }) - }) -} + for (i = 0; i < len; i++) { + if (!split[i]) continue; // ignore empty strings + namespaces = split[i].replace(/\*/g, '.*?'); + if (namespaces[0] === '-') { + exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); + } else { + exports.names.push(new RegExp('^' + namespaces + '$')); + } + } -Glob.prototype._mark = function (p) { - return common.mark(this, p) + for (i = 0; i < exports.instances.length; i++) { + var instance = exports.instances[i]; + instance.enabled = exports.enabled(instance.namespace); + } } -Glob.prototype._makeAbs = function (f) { - return common.makeAbs(this, f) -} +/** + * Disable debug output. + * + * @api public + */ -Glob.prototype.abort = function () { - this.aborted = true - this.emit('abort') +function disable() { + exports.enable(''); } -Glob.prototype.pause = function () { - if (!this.paused) { - this.paused = true - this.emit('pause') - } -} +/** + * Returns true if the given mode name is enabled, false otherwise. + * + * @param {String} name + * @return {Boolean} + * @api public + */ -Glob.prototype.resume = function () { - if (this.paused) { - this.emit('resume') - this.paused = false - if (this._emitQueue.length) { - var eq = this._emitQueue.slice(0) - this._emitQueue.length = 0 - for (var i = 0; i < eq.length; i ++) { - var e = eq[i] - this._emitMatch(e[0], e[1]) - } +function enabled(name) { + if (name[name.length - 1] === '*') { + return true; + } + var i, len; + for (i = 0, len = exports.skips.length; i < len; i++) { + if (exports.skips[i].test(name)) { + return false; } - if (this._processQueue.length) { - var pq = this._processQueue.slice(0) - this._processQueue.length = 0 - for (var i = 0; i < pq.length; i ++) { - var p = pq[i] - this._processing-- - this._process(p[0], p[1], p[2], p[3]) - } + } + for (i = 0, len = exports.names.length; i < len; i++) { + if (exports.names[i].test(name)) { + return true; } } + return false; } -Glob.prototype._process = function (pattern, index, inGlobStar, cb) { - assert(this instanceof Glob) - assert(typeof cb === 'function') - - if (this.aborted) - return - - this._processing++ - if (this.paused) { - this._processQueue.push([pattern, index, inGlobStar, cb]) - return - } - - //console.error('PROCESS %d', this._processing, pattern) - - // Get the first [n] parts of pattern that are all strings. - var n = 0 - while (typeof pattern[n] === 'string') { - n ++ - } - // now n is the index of the first one that is *not* a string. - - // see if there's anything else - var prefix - switch (n) { - // if not, then this is rather simple - case pattern.length: - this._processSimple(pattern.join('/'), index, cb) - return +/** + * Coerce `val`. + * + * @param {Mixed} val + * @return {Mixed} + * @api private + */ - case 0: - // pattern *starts* with some non-trivial item. - // going to readdir(cwd), but not include the prefix in matches. - prefix = null - break +function coerce(val) { + if (val instanceof Error) return val.stack || val.message; + return val; +} - default: - // pattern has some string bits in the front. - // whatever it starts with, whether that's 'absolute' like /foo/bar, - // or 'relative' like '../baz' - prefix = pattern.slice(0, n).join('/') - break - } - var remain = pattern.slice(n) +/***/ }), +/* 113 */, +/* 114 */ +/***/ (function(module, exports, __webpack_require__) { - // get the list of entries. - var read - if (prefix === null) - read = '.' - else if (isAbsolute(prefix) || isAbsolute(pattern.join('/'))) { - if (!prefix || !isAbsolute(prefix)) - prefix = '/' + prefix - read = prefix - } else - read = prefix +module.exports = realpath +realpath.realpath = realpath +realpath.sync = realpathSync +realpath.realpathSync = realpathSync +realpath.monkeypatch = monkeypatch +realpath.unmonkeypatch = unmonkeypatch - var abs = this._makeAbs(read) +var fs = __webpack_require__(3) +var origRealpath = fs.realpath +var origRealpathSync = fs.realpathSync - //if ignored, skip _processing - if (childrenIgnored(this, read)) - return cb() +var version = process.version +var ok = /^v[0-5]\./.test(version) +var old = __webpack_require__(217) - var isGlobStar = remain[0] === minimatch.GLOBSTAR - if (isGlobStar) - this._processGlobStar(prefix, read, abs, remain, index, inGlobStar, cb) - else - this._processReaddir(prefix, read, abs, remain, index, inGlobStar, cb) +function newError (er) { + return er && er.syscall === 'realpath' && ( + er.code === 'ELOOP' || + er.code === 'ENOMEM' || + er.code === 'ENAMETOOLONG' + ) } -Glob.prototype._processReaddir = function (prefix, read, abs, remain, index, inGlobStar, cb) { - var self = this - this._readdir(abs, inGlobStar, function (er, entries) { - return self._processReaddir2(prefix, read, abs, remain, index, inGlobStar, entries, cb) +function realpath (p, cache, cb) { + if (ok) { + return origRealpath(p, cache, cb) + } + + if (typeof cache === 'function') { + cb = cache + cache = null + } + origRealpath(p, cache, function (er, result) { + if (newError(er)) { + old.realpath(p, cache, cb) + } else { + cb(er, result) + } }) } -Glob.prototype._processReaddir2 = function (prefix, read, abs, remain, index, inGlobStar, entries, cb) { - - // if the abs isn't a dir, then nothing can match! - if (!entries) - return cb() - - // It will only match dot entries if it starts with a dot, or if - // dot is set. Stuff like @(.foo|.bar) isn't allowed. - var pn = remain[0] - var negate = !!this.minimatch.negate - var rawGlob = pn._glob - var dotOk = this.dot || rawGlob.charAt(0) === '.' +function realpathSync (p, cache) { + if (ok) { + return origRealpathSync(p, cache) + } - var matchedEntries = [] - for (var i = 0; i < entries.length; i++) { - var e = entries[i] - if (e.charAt(0) !== '.' || dotOk) { - var m - if (negate && !prefix) { - m = !e.match(pn) - } else { - m = e.match(pn) - } - if (m) - matchedEntries.push(e) + try { + return origRealpathSync(p, cache) + } catch (er) { + if (newError(er)) { + return old.realpathSync(p, cache) + } else { + throw er } } +} - //console.error('prd2', prefix, entries, remain[0]._glob, matchedEntries) - - var len = matchedEntries.length - // If there are no matched entries, then nothing matches. - if (len === 0) - return cb() - - // if this is the last remaining pattern bit, then no need for - // an additional stat *unless* the user has specified mark or - // stat explicitly. We know they exist, since readdir returned - // them. +function monkeypatch () { + fs.realpath = realpath + fs.realpathSync = realpathSync +} - if (remain.length === 1 && !this.mark && !this.stat) { - if (!this.matches[index]) - this.matches[index] = Object.create(null) +function unmonkeypatch () { + fs.realpath = origRealpath + fs.realpathSync = origRealpathSync +} - for (var i = 0; i < len; i ++) { - var e = matchedEntries[i] - if (prefix) { - if (prefix !== '/') - e = prefix + '/' + e - else - e = prefix + e - } - if (e.charAt(0) === '/' && !this.nomount) { - e = path.join(this.root, e) - } - this._emitMatch(index, e) - } - // This was the last one, and no stats were needed - return cb() - } +/***/ }), +/* 115 */ +/***/ (function(module, exports, __webpack_require__) { - // now test all matched entries as stand-ins for that part - // of the pattern. - remain.shift() - for (var i = 0; i < len; i ++) { - var e = matchedEntries[i] - var newPattern - if (prefix) { - if (prefix !== '/') - e = prefix + '/' + e - else - e = prefix + e - } - this._process([e].concat(remain), index, inGlobStar, cb) - } - cb() +exports.alphasort = alphasort +exports.alphasorti = alphasorti +exports.setopts = setopts +exports.ownProp = ownProp +exports.makeAbs = makeAbs +exports.finish = finish +exports.mark = mark +exports.isIgnored = isIgnored +exports.childrenIgnored = childrenIgnored + +function ownProp (obj, field) { + return Object.prototype.hasOwnProperty.call(obj, field) } -Glob.prototype._emitMatch = function (index, e) { - if (this.aborted) - return +var path = __webpack_require__(0) +var minimatch = __webpack_require__(60) +var isAbsolute = __webpack_require__(76) +var Minimatch = minimatch.Minimatch - if (isIgnored(this, e)) - return +function alphasorti (a, b) { + return a.toLowerCase().localeCompare(b.toLowerCase()) +} - if (this.paused) { - this._emitQueue.push([index, e]) - return - } +function alphasort (a, b) { + return a.localeCompare(b) +} - var abs = isAbsolute(e) ? e : this._makeAbs(e) +function setupIgnores (self, options) { + self.ignore = options.ignore || [] - if (this.mark) - e = this._mark(e) + if (!Array.isArray(self.ignore)) + self.ignore = [self.ignore] - if (this.absolute) - e = abs + if (self.ignore.length) { + self.ignore = self.ignore.map(ignoreMap) + } +} - if (this.matches[index][e]) - return +// ignore patterns are always in dot:true mode. +function ignoreMap (pattern) { + var gmatcher = null + if (pattern.slice(-3) === '/**') { + var gpattern = pattern.replace(/(\/\*\*)+$/, '') + gmatcher = new Minimatch(gpattern, { dot: true }) + } - if (this.nodir) { - var c = this.cache[abs] - if (c === 'DIR' || Array.isArray(c)) - return + return { + matcher: new Minimatch(pattern, { dot: true }), + gmatcher: gmatcher } +} - this.matches[index][e] = true +function setopts (self, pattern, options) { + if (!options) + options = {} - var st = this.statCache[abs] - if (st) - this.emit('stat', e, st) + // base-matching: just use globstar for that. + if (options.matchBase && -1 === pattern.indexOf("/")) { + if (options.noglobstar) { + throw new Error("base matching requires globstar") + } + pattern = "**/" + pattern + } - this.emit('match', e) -} + self.silent = !!options.silent + self.pattern = pattern + self.strict = options.strict !== false + self.realpath = !!options.realpath + self.realpathCache = options.realpathCache || Object.create(null) + self.follow = !!options.follow + self.dot = !!options.dot + self.mark = !!options.mark + self.nodir = !!options.nodir + if (self.nodir) + self.mark = true + self.sync = !!options.sync + self.nounique = !!options.nounique + self.nonull = !!options.nonull + self.nosort = !!options.nosort + self.nocase = !!options.nocase + self.stat = !!options.stat + self.noprocess = !!options.noprocess + self.absolute = !!options.absolute -Glob.prototype._readdirInGlobStar = function (abs, cb) { - if (this.aborted) - return + self.maxLength = options.maxLength || Infinity + self.cache = options.cache || Object.create(null) + self.statCache = options.statCache || Object.create(null) + self.symlinks = options.symlinks || Object.create(null) - // follow all symlinked directories forever - // just proceed as if this is a non-globstar situation - if (this.follow) - return this._readdir(abs, false, cb) + setupIgnores(self, options) - var lstatkey = 'lstat\0' + abs - var self = this - var lstatcb = inflight(lstatkey, lstatcb_) + self.changedCwd = false + var cwd = process.cwd() + if (!ownProp(options, "cwd")) + self.cwd = cwd + else { + self.cwd = path.resolve(options.cwd) + self.changedCwd = self.cwd !== cwd + } - if (lstatcb) - fs.lstat(abs, lstatcb) + self.root = options.root || path.resolve(self.cwd, "/") + self.root = path.resolve(self.root) + if (process.platform === "win32") + self.root = self.root.replace(/\\/g, "/") - function lstatcb_ (er, lstat) { - if (er && er.code === 'ENOENT') - return cb() + // TODO: is an absolute `cwd` supposed to be resolved against `root`? + // e.g. { cwd: '/test', root: __dirname } === path.join(__dirname, '/test') + self.cwdAbs = isAbsolute(self.cwd) ? self.cwd : makeAbs(self, self.cwd) + if (process.platform === "win32") + self.cwdAbs = self.cwdAbs.replace(/\\/g, "/") + self.nomount = !!options.nomount - var isSym = lstat && lstat.isSymbolicLink() - self.symlinks[abs] = isSym + // disable comments and negation in Minimatch. + // Note that they are not supported in Glob itself anyway. + options.nonegate = true + options.nocomment = true - // If it's not a symlink or a dir, then it's definitely a regular file. - // don't bother doing a readdir in that case. - if (!isSym && lstat && !lstat.isDirectory()) { - self.cache[abs] = 'FILE' - cb() - } else - self._readdir(abs, false, cb) - } + self.minimatch = new Minimatch(pattern, options) + self.options = self.minimatch.options } -Glob.prototype._readdir = function (abs, inGlobStar, cb) { - if (this.aborted) - return +function finish (self) { + var nou = self.nounique + var all = nou ? [] : Object.create(null) - cb = inflight('readdir\0'+abs+'\0'+inGlobStar, cb) - if (!cb) - return + for (var i = 0, l = self.matches.length; i < l; i ++) { + var matches = self.matches[i] + if (!matches || Object.keys(matches).length === 0) { + if (self.nonull) { + // do like the shell, and spit out the literal glob + var literal = self.minimatch.globSet[i] + if (nou) + all.push(literal) + else + all[literal] = true + } + } else { + // had matches + var m = Object.keys(matches) + if (nou) + all.push.apply(all, m) + else + m.forEach(function (m) { + all[m] = true + }) + } + } - //console.error('RD %j %j', +inGlobStar, abs) - if (inGlobStar && !ownProp(this.symlinks, abs)) - return this._readdirInGlobStar(abs, cb) + if (!nou) + all = Object.keys(all) - if (ownProp(this.cache, abs)) { - var c = this.cache[abs] - if (!c || c === 'FILE') - return cb() + if (!self.nosort) + all = all.sort(self.nocase ? alphasorti : alphasort) - if (Array.isArray(c)) - return cb(null, c) + // at *some* point we statted all of these + if (self.mark) { + for (var i = 0; i < all.length; i++) { + all[i] = self._mark(all[i]) + } + if (self.nodir) { + all = all.filter(function (e) { + var notDir = !(/\/$/.test(e)) + var c = self.cache[e] || self.cache[makeAbs(self, e)] + if (notDir && c) + notDir = c !== 'DIR' && !Array.isArray(c) + return notDir + }) + } } - var self = this - fs.readdir(abs, readdirCb(this, abs, cb)) -} + if (self.ignore.length) + all = all.filter(function(m) { + return !isIgnored(self, m) + }) -function readdirCb (self, abs, cb) { - return function (er, entries) { - if (er) - self._readdirError(abs, er, cb) - else - self._readdirEntries(abs, entries, cb) - } + self.found = all } -Glob.prototype._readdirEntries = function (abs, entries, cb) { - if (this.aborted) - return +function mark (self, p) { + var abs = makeAbs(self, p) + var c = self.cache[abs] + var m = p + if (c) { + var isDir = c === 'DIR' || Array.isArray(c) + var slash = p.slice(-1) === '/' - // if we haven't asked to stat everything, then just - // assume that everything in there exists, so we can avoid - // having to stat it a second time. - if (!this.mark && !this.stat) { - for (var i = 0; i < entries.length; i ++) { - var e = entries[i] - if (abs === '/') - e = abs + e - else - e = abs + '/' + e - this.cache[e] = true + if (isDir && !slash) + m += '/' + else if (!isDir && slash) + m = m.slice(0, -1) + + if (m !== p) { + var mabs = makeAbs(self, m) + self.statCache[mabs] = self.statCache[abs] + self.cache[mabs] = self.cache[abs] } } - this.cache[abs] = entries - return cb(null, entries) + return m } -Glob.prototype._readdirError = function (f, er, cb) { - if (this.aborted) - return +// lotta situps... +function makeAbs (self, f) { + var abs = f + if (f.charAt(0) === '/') { + abs = path.join(self.root, f) + } else if (isAbsolute(f) || f === '') { + abs = f + } else if (self.changedCwd) { + abs = path.resolve(self.cwd, f) + } else { + abs = path.resolve(f) + } - // handle errors, and cache the information - switch (er.code) { - case 'ENOTSUP': // https://github.com/isaacs/node-glob/issues/205 - case 'ENOTDIR': // totally normal. means it *does* exist. - var abs = this._makeAbs(f) - this.cache[abs] = 'FILE' - if (abs === this.cwdAbs) { - var error = new Error(er.code + ' invalid cwd ' + this.cwd) - error.path = this.cwd - error.code = er.code - this.emit('error', error) - this.abort() - } - break + if (process.platform === 'win32') + abs = abs.replace(/\\/g, '/') - case 'ENOENT': // not terribly unusual - case 'ELOOP': - case 'ENAMETOOLONG': - case 'UNKNOWN': - this.cache[this._makeAbs(f)] = false - break + return abs +} - default: // some unusual error. Treat as failure. - this.cache[this._makeAbs(f)] = false - if (this.strict) { - this.emit('error', er) - // If the error is handled, then we abort - // if not, we threw out of here - this.abort() - } - if (!this.silent) - console.error('glob error', er) - break - } - return cb() -} +// Return true, if pattern ends with globstar '**', for the accompanying parent directory. +// Ex:- If node_modules/** is the pattern, add 'node_modules' to ignore list along with it's contents +function isIgnored (self, path) { + if (!self.ignore.length) + return false -Glob.prototype._processGlobStar = function (prefix, read, abs, remain, index, inGlobStar, cb) { - var self = this - this._readdir(abs, inGlobStar, function (er, entries) { - self._processGlobStar2(prefix, read, abs, remain, index, inGlobStar, entries, cb) + return self.ignore.some(function(item) { + return item.matcher.match(path) || !!(item.gmatcher && item.gmatcher.match(path)) }) } +function childrenIgnored (self, path) { + if (!self.ignore.length) + return false -Glob.prototype._processGlobStar2 = function (prefix, read, abs, remain, index, inGlobStar, entries, cb) { - //console.error('pgs2', prefix, remain[0], entries) + return self.ignore.some(function(item) { + return !!(item.gmatcher && item.gmatcher.match(path)) + }) +} - // no entries means not a dir, so it can never have matches - // foo.txt/** doesn't match foo.txt - if (!entries) - return cb() - // test without the globstar, and with every child both below - // and replacing the globstar. - var remainWithoutGlobStar = remain.slice(1) - var gspref = prefix ? [ prefix ] : [] - var noGlobStar = gspref.concat(remainWithoutGlobStar) +/***/ }), +/* 116 */ +/***/ (function(module, exports, __webpack_require__) { - // the noGlobStar pattern exits the inGlobStar state - this._process(noGlobStar, index, false, cb) +var path = __webpack_require__(0); +var fs = __webpack_require__(3); +var _0777 = parseInt('0777', 8); - var isSym = this.symlinks[abs] - var len = entries.length +module.exports = mkdirP.mkdirp = mkdirP.mkdirP = mkdirP; - // If it's a symlink, and we're in a globstar, then stop - if (isSym && inGlobStar) - return cb() +function mkdirP (p, opts, f, made) { + if (typeof opts === 'function') { + f = opts; + opts = {}; + } + else if (!opts || typeof opts !== 'object') { + opts = { mode: opts }; + } + + var mode = opts.mode; + var xfs = opts.fs || fs; + + if (mode === undefined) { + mode = _0777 & (~process.umask()); + } + if (!made) made = null; + + var cb = f || function () {}; + p = path.resolve(p); + + xfs.mkdir(p, mode, function (er) { + if (!er) { + made = made || p; + return cb(null, made); + } + switch (er.code) { + case 'ENOENT': + mkdirP(path.dirname(p), opts, function (er, made) { + if (er) cb(er, made); + else mkdirP(p, opts, cb, made); + }); + break; - for (var i = 0; i < len; i++) { - var e = entries[i] - if (e.charAt(0) === '.' && !this.dot) - continue + // In the case of any other error, just see if there's a dir + // there already. If so, then hooray! If not, then something + // is borked. + default: + xfs.stat(p, function (er2, stat) { + // if the stat fails, then that's super weird. + // let the original error be the failure reason. + if (er2 || !stat.isDirectory()) cb(er, made) + else cb(null, made); + }); + break; + } + }); +} - // these two cases enter the inGlobStar state - var instead = gspref.concat(entries[i], remainWithoutGlobStar) - this._process(instead, index, true, cb) +mkdirP.sync = function sync (p, opts, made) { + if (!opts || typeof opts !== 'object') { + opts = { mode: opts }; + } + + var mode = opts.mode; + var xfs = opts.fs || fs; + + if (mode === undefined) { + mode = _0777 & (~process.umask()); + } + if (!made) made = null; - var below = gspref.concat(entries[i], remain) - this._process(below, index, true, cb) - } + p = path.resolve(p); - cb() -} + try { + xfs.mkdirSync(p, mode); + made = made || p; + } + catch (err0) { + switch (err0.code) { + case 'ENOENT' : + made = sync(path.dirname(p), opts, made); + sync(p, opts, made); + break; -Glob.prototype._processSimple = function (prefix, index, cb) { - // XXX review this. Shouldn't it be doing the mounting etc - // before doing stat? kinda weird? - var self = this - this._stat(prefix, function (er, exists) { - self._processSimple2(prefix, index, er, exists, cb) - }) -} -Glob.prototype._processSimple2 = function (prefix, index, er, exists, cb) { + // In the case of any other error, just see if there's a dir + // there already. If so, then hooray! If not, then something + // is borked. + default: + var stat; + try { + stat = xfs.statSync(p); + } + catch (err1) { + throw err0; + } + if (!stat.isDirectory()) throw err0; + break; + } + } - //console.error('ps2', prefix, exists) + return made; +}; - if (!this.matches[index]) - this.matches[index] = Object.create(null) - // If it doesn't exist, then just mark the lack of results - if (!exists) - return cb() +/***/ }), +/* 117 */, +/* 118 */, +/* 119 */, +/* 120 */, +/* 121 */, +/* 122 */ +/***/ (function(module, exports, __webpack_require__) { - if (prefix && isAbsolute(prefix) && !this.nomount) { - var trail = /[\/\\]$/.test(prefix) - if (prefix.charAt(0) === '/') { - prefix = path.join(this.root, prefix) - } else { - prefix = path.resolve(this.root, prefix) - if (trail) - prefix += '/' - } - } +"use strict"; - if (process.platform === 'win32') - prefix = prefix.replace(/\\/g, '/') +module.exports = x => { + if (typeof x !== 'string') { + throw new TypeError('Expected a string, got ' + typeof x); + } - // Mark this as a match - this._emitMatch(index, prefix) - cb() -} + // Catches EFBBBF (UTF-8 BOM) because the buffer-to-string + // conversion translates it to FEFF (UTF-16 BOM) + if (x.charCodeAt(0) === 0xFEFF) { + return x.slice(1); + } -// Returns either 'DIR', 'FILE', or false -Glob.prototype._stat = function (f, cb) { - var abs = this._makeAbs(f) - var needDir = f.slice(-1) === '/' + return x; +}; - if (f.length > this.maxLength) - return cb() - if (!this.stat && ownProp(this.cache, abs)) { - var c = this.cache[abs] +/***/ }), +/* 123 */ +/***/ (function(module, exports) { - if (Array.isArray(c)) - c = 'DIR' +// Returns a wrapper function that returns a wrapped callback +// The wrapper function should do some stuff, and return a +// presumably different callback function. +// This makes sure that own properties are retained, so that +// decorations and such are not lost along the way. +module.exports = wrappy +function wrappy (fn, cb) { + if (fn && cb) return wrappy(fn)(cb) - // It exists, but maybe not how we need it - if (!needDir || c === 'DIR') - return cb(null, c) + if (typeof fn !== 'function') + throw new TypeError('need wrapper function') - if (needDir && c === 'FILE') - return cb() + Object.keys(fn).forEach(function (k) { + wrapper[k] = fn[k] + }) - // otherwise we have to stat, because maybe c=true - // if we know it exists, but not what it is. - } + return wrapper - var exists - var stat = this.statCache[abs] - if (stat !== undefined) { - if (stat === false) - return cb(null, stat) - else { - var type = stat.isDirectory() ? 'DIR' : 'FILE' - if (needDir && type === 'FILE') - return cb() - else - return cb(null, type, stat) + function wrapper() { + var args = new Array(arguments.length) + for (var i = 0; i < args.length; i++) { + args[i] = arguments[i] } - } - - var self = this - var statcb = inflight('stat\0' + abs, lstatcb_) - if (statcb) - fs.lstat(abs, statcb) - - function lstatcb_ (er, lstat) { - if (lstat && lstat.isSymbolicLink()) { - // If it's a symlink, then treat it as the target, unless - // the target does not exist, then treat it as a file. - return fs.stat(abs, function (er, stat) { - if (er) - self._stat2(f, abs, null, lstat, cb) - else - self._stat2(f, abs, er, stat, cb) + var ret = fn.apply(this, args) + var cb = args[args.length-1] + if (typeof ret === 'function' && ret !== cb) { + Object.keys(cb).forEach(function (k) { + ret[k] = cb[k] }) - } else { - self._stat2(f, abs, er, lstat, cb) } + return ret } } -Glob.prototype._stat2 = function (f, abs, er, stat, cb) { - if (er && (er.code === 'ENOENT' || er.code === 'ENOTDIR')) { - this.statCache[abs] = false - return cb() - } - - var needDir = f.slice(-1) === '/' - this.statCache[abs] = stat - - if (abs.slice(-1) === '/' && stat && !stat.isDirectory()) - return cb(null, false, stat) - - var c = true - if (stat) - c = stat.isDirectory() ? 'DIR' : 'FILE' - this.cache[abs] = this.cache[abs] || c - if (needDir && c === 'FILE') - return cb() +/***/ }), +/* 124 */, +/* 125 */, +/* 126 */, +/* 127 */, +/* 128 */, +/* 129 */, +/* 130 */, +/* 131 */ +/***/ (function(module, exports, __webpack_require__) { - return cb(null, c, stat) -} +// fallback for non-array-like ES3 and non-enumerable old V8 strings +var cof = __webpack_require__(47); +// eslint-disable-next-line no-prototype-builtins +module.exports = Object('z').propertyIsEnumerable(0) ? Object : function (it) { + return cof(it) == 'String' ? it.split('') : Object(it); +}; /***/ }), -/* 76 */ +/* 132 */ /***/ (function(module, exports, __webpack_require__) { -"use strict"; - +// 19.1.2.14 / 15.2.3.14 Object.keys(O) +var $keys = __webpack_require__(195); +var enumBugKeys = __webpack_require__(101); -function posix(path) { - return path.charAt(0) === '/'; -} +module.exports = Object.keys || function keys(O) { + return $keys(O, enumBugKeys); +}; -function win32(path) { - // https://github.com/nodejs/node/blob/b3fcc245fb25539909ef1d5eaa01dbf92e168633/lib/path.js#L56 - var splitDeviceRe = /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?([\s\S]*?)$/; - var result = splitDeviceRe.exec(path); - var device = result[1] || ''; - var isUnc = Boolean(device && device.charAt(1) !== ':'); - // UNC paths are always absolute - return Boolean(result[2] || isUnc); -} +/***/ }), +/* 133 */ +/***/ (function(module, exports, __webpack_require__) { -module.exports = process.platform === 'win32' ? win32 : posix; -module.exports.posix = posix; -module.exports.win32 = win32; +// 7.1.13 ToObject(argument) +var defined = __webpack_require__(67); +module.exports = function (it) { + return Object(defined(it)); +}; /***/ }), -/* 77 */, -/* 78 */, -/* 79 */ +/* 134 */, +/* 135 */, +/* 136 */, +/* 137 */, +/* 138 */, +/* 139 */, +/* 140 */, +/* 141 */, +/* 142 */, +/* 143 */, +/* 144 */, +/* 145 */ /***/ (function(module, exports) { -module.exports = __webpack_require__(123); +module.exports = {"name":"yarn","installationMethod":"unknown","version":"1.10.0-0","license":"BSD-2-Clause","preferGlobal":true,"description":"📦🐈 Fast, reliable, and secure dependency management.","dependencies":{"@zkochan/cmd-shim":"^2.2.4","babel-runtime":"^6.26.0","bytes":"^3.0.0","camelcase":"^4.0.0","chalk":"^2.1.0","commander":"^2.9.0","death":"^1.0.0","debug":"^3.0.0","deep-equal":"^1.0.1","detect-indent":"^5.0.0","dnscache":"^1.0.1","glob":"^7.1.1","gunzip-maybe":"^1.4.0","hash-for-dep":"^1.2.3","imports-loader":"^0.8.0","ini":"^1.3.4","inquirer":"^3.0.1","invariant":"^2.2.0","is-builtin-module":"^2.0.0","is-ci":"^1.0.10","is-webpack-bundle":"^1.0.0","leven":"^2.0.0","loud-rejection":"^1.2.0","micromatch":"^2.3.11","mkdirp":"^0.5.1","node-emoji":"^1.6.1","normalize-url":"^2.0.0","npm-logical-tree":"^1.2.1","object-path":"^0.11.2","proper-lockfile":"^2.0.0","puka":"^1.0.0","read":"^1.0.7","request":"^2.87.0","request-capture-har":"^1.2.2","rimraf":"^2.5.0","semver":"^5.1.0","ssri":"^5.3.0","strip-ansi":"^4.0.0","strip-bom":"^3.0.0","tar-fs":"^1.16.0","tar-stream":"^1.6.1","uuid":"^3.0.1","v8-compile-cache":"^2.0.0","validate-npm-package-license":"^3.0.3","yn":"^2.0.0"},"devDependencies":{"babel-core":"^6.26.0","babel-eslint":"^7.2.3","babel-loader":"^6.2.5","babel-plugin-array-includes":"^2.0.3","babel-plugin-transform-builtin-extend":"^1.1.2","babel-plugin-transform-inline-imports-commonjs":"^1.0.0","babel-plugin-transform-runtime":"^6.4.3","babel-preset-env":"^1.6.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.0.0","babylon":"^6.5.0","commitizen":"^2.9.6","cz-conventional-changelog":"^2.0.0","eslint":"^4.3.0","eslint-config-fb-strict":"^22.0.0","eslint-plugin-babel":"^5.0.0","eslint-plugin-flowtype":"^2.35.0","eslint-plugin-jasmine":"^2.6.2","eslint-plugin-jest":"^21.0.0","eslint-plugin-jsx-a11y":"^6.0.2","eslint-plugin-prefer-object-spread":"^1.2.1","eslint-plugin-prettier":"^2.1.2","eslint-plugin-react":"^7.1.0","eslint-plugin-relay":"^0.0.24","eslint-plugin-yarn-internal":"file:scripts/eslint-rules","execa":"^0.10.0","flow-bin":"^0.66.0","git-release-notes":"^3.0.0","gulp":"^3.9.0","gulp-babel":"^7.0.0","gulp-if":"^2.0.1","gulp-newer":"^1.0.0","gulp-plumber":"^1.0.1","gulp-sourcemaps":"^2.2.0","gulp-util":"^3.0.7","gulp-watch":"^5.0.0","jest":"^22.4.4","jsinspect":"^0.12.6","minimatch":"^3.0.4","mock-stdin":"^0.3.0","prettier":"^1.5.2","temp":"^0.8.3","webpack":"^2.1.0-beta.25","yargs":"^6.3.0"},"resolutions":{"sshpk":"^1.14.2"},"engines":{"node":">=4.0.0"},"repository":"yarnpkg/yarn","bin":{"yarn":"./bin/yarn.js","yarnpkg":"./bin/yarn.js"},"scripts":{"build":"gulp build","build-bundle":"node ./scripts/build-webpack.js","build-chocolatey":"powershell ./scripts/build-chocolatey.ps1","build-deb":"./scripts/build-deb.sh","build-dist":"bash ./scripts/build-dist.sh","build-win-installer":"scripts\\build-windows-installer.bat","changelog":"git-release-notes $(git describe --tags --abbrev=0 $(git describe --tags --abbrev=0)^)..$(git describe --tags --abbrev=0) scripts/changelog.md","dupe-check":"yarn jsinspect ./src","lint":"eslint . && flow check","pkg-tests":"yarn --cwd packages/pkg-tests jest yarn.test.js","prettier":"eslint src __tests__ --fix","release-branch":"./scripts/release-branch.sh","test":"yarn lint && yarn test-only","test-only":"node --max_old_space_size=4096 node_modules/jest/bin/jest.js --verbose","test-only-debug":"node --inspect-brk --max_old_space_size=4096 node_modules/jest/bin/jest.js --runInBand --verbose","test-coverage":"node --max_old_space_size=4096 node_modules/jest/bin/jest.js --coverage --verbose","watch":"gulp watch","commit":"git-cz"},"jest":{"collectCoverageFrom":["src/**/*.js"],"testEnvironment":"node","modulePathIgnorePatterns":["__tests__/fixtures/","packages/pkg-tests/pkg-tests-fixtures","dist/"],"testPathIgnorePatterns":["__tests__/(fixtures|__mocks__)/","updates/","_(temp|mock|install|init|helpers).js$","packages/pkg-tests"]},"config":{"commitizen":{"path":"./node_modules/cz-conventional-changelog"}}} /***/ }), -/* 80 */, -/* 81 */ +/* 146 */, +/* 147 */, +/* 148 */, +/* 149 */, +/* 150 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -45859,18649 +49694,18595 @@ module.exports = __webpack_require__(123); Object.defineProperty(exports, "__esModule", { value: true }); +exports.default = stringify; -exports.default = function (str, fileLoc = 'lockfile') { - str = (0, (_stripBom || _load_stripBom()).default)(str); - return hasMergeConflicts(str) ? parseWithConflict(str, fileLoc) : { type: 'success', object: parse(str, fileLoc) }; -}; - -var _util; +var _misc; -function _load_util() { - return _util = _interopRequireDefault(__webpack_require__(2)); +function _load_misc() { + return _misc = __webpack_require__(12); } -var _invariant; +var _constants; -function _load_invariant() { - return _invariant = _interopRequireDefault(__webpack_require__(7)); +function _load_constants() { + return _constants = __webpack_require__(6); } -var _stripBom; +var _package; -function _load_stripBom() { - return _stripBom = _interopRequireDefault(__webpack_require__(122)); +function _load_package() { + return _package = __webpack_require__(145); } -var _constants; +const NODE_VERSION = process.version; -function _load_constants() { - return _constants = __webpack_require__(6); +function shouldWrapKey(str) { + return str.indexOf('true') === 0 || str.indexOf('false') === 0 || /[:\s\n\\",\[\]]/g.test(str) || /^[0-9]/g.test(str) || !/^[a-zA-Z]/g.test(str); } -var _errors; - -function _load_errors() { - return _errors = __webpack_require__(4); +function maybeWrap(str) { + if (typeof str === 'boolean' || typeof str === 'number' || shouldWrapKey(str)) { + return JSON.stringify(str); + } else { + return str; + } } -var _map; +const priorities = { + name: 1, + version: 2, + uid: 3, + resolved: 4, + integrity: 5, + registry: 6, + dependencies: 7 +}; -function _load_map() { - return _map = _interopRequireDefault(__webpack_require__(20)); +function priorityThenAlphaSort(a, b) { + if (priorities[a] || priorities[b]) { + return (priorities[a] || 100) > (priorities[b] || 100) ? 1 : -1; + } else { + return (0, (_misc || _load_misc()).sortAlpha)(a, b); + } } -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } +function _stringify(obj, options) { + if (typeof obj !== 'object') { + throw new TypeError(); + } -/* eslint quotes: 0 */ + const indent = options.indent; + const lines = []; -const VERSION_REGEX = /^yarn lockfile v(\d+)$/; + // Sorting order needs to be consistent between runs, we run native sort by name because there are no + // problems with it being unstable because there are no to keys the same + // However priorities can be duplicated and native sort can shuffle things from run to run + const keys = Object.keys(obj).sort(priorityThenAlphaSort); -const TOKEN_TYPES = { - boolean: 'BOOLEAN', - string: 'STRING', - identifier: 'IDENTIFIER', - eof: 'EOF', - colon: 'COLON', - newline: 'NEWLINE', - comment: 'COMMENT', - indent: 'INDENT', - invalid: 'INVALID', - number: 'NUMBER', - comma: 'COMMA' -}; + let addedKeys = []; -const VALID_PROP_VALUE_TOKENS = [TOKEN_TYPES.boolean, TOKEN_TYPES.string, TOKEN_TYPES.number]; + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + const val = obj[key]; + if (val == null || addedKeys.indexOf(key) >= 0) { + continue; + } -function isValidPropValueToken(token) { - return VALID_PROP_VALUE_TOKENS.indexOf(token.type) >= 0; + const valKeys = [key]; + + // get all keys that have the same value equality, we only want this for objects + if (typeof val === 'object') { + for (let j = i + 1; j < keys.length; j++) { + const key = keys[j]; + if (val === obj[key]) { + valKeys.push(key); + } + } + } + + const keyLine = valKeys.sort((_misc || _load_misc()).sortAlpha).map(maybeWrap).join(', '); + + if (typeof val === 'string' || typeof val === 'boolean' || typeof val === 'number') { + lines.push(`${keyLine} ${maybeWrap(val)}`); + } else if (typeof val === 'object') { + lines.push(`${keyLine}:\n${_stringify(val, { indent: indent + ' ' })}` + (options.topLevel ? '\n' : '')); + } else { + throw new TypeError(); + } + + addedKeys = addedKeys.concat(valKeys); + } + + return indent + lines.join(`\n${indent}`); } -function* tokenise(input) { - let lastNewline = false; - let line = 1; - let col = 0; +function stringify(obj, noHeader, enableVersions) { + const val = _stringify(obj, { + indent: '', + topLevel: true + }); + if (noHeader) { + return val; + } - function buildToken(type, value) { - return { line, col, type, value }; + const lines = []; + lines.push('# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.'); + lines.push(`# yarn lockfile v${(_constants || _load_constants()).LOCKFILE_VERSION}`); + if (enableVersions) { + lines.push(`# yarn v${(_package || _load_package()).version}`); + lines.push(`# node ${NODE_VERSION}`); } + lines.push('\n'); + lines.push(val); - while (input.length) { - let chop = 0; + return lines.join('\n'); +} - if (input[0] === '\n' || input[0] === '\r') { - chop++; - // If this is a \r\n line, ignore both chars but only add one new line - if (input[1] === '\n') { - chop++; - } - line++; - col = 0; - yield buildToken(TOKEN_TYPES.newline); - } else if (input[0] === '#') { - chop++; +/***/ }), +/* 151 */, +/* 152 */, +/* 153 */, +/* 154 */, +/* 155 */, +/* 156 */, +/* 157 */, +/* 158 */, +/* 159 */, +/* 160 */, +/* 161 */, +/* 162 */, +/* 163 */, +/* 164 */ +/***/ (function(module, exports, __webpack_require__) { - let val = ''; - while (input[chop] !== '\n') { - val += input[chop]; - chop++; - } - yield buildToken(TOKEN_TYPES.comment, val); - } else if (input[0] === ' ') { - if (lastNewline) { - let indent = ''; - for (let i = 0; input[i] === ' '; i++) { - indent += input[i]; - } +"use strict"; - if (indent.length % 2) { - throw new TypeError('Invalid number of spaces'); - } else { - chop = indent.length; - yield buildToken(TOKEN_TYPES.indent, indent.length / 2); - } - } else { - chop++; - } - } else if (input[0] === '"') { - let val = ''; - for (let i = 0;; i++) { - const currentChar = input[i]; - val += currentChar; +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.fileDatesEqual = exports.copyFile = exports.unlink = undefined; - if (i > 0 && currentChar === '"') { - const isEscaped = input[i - 1] === '\\' && input[i - 2] !== '\\'; - if (!isEscaped) { - break; - } - } - } +var _asyncToGenerator2; - chop = val.length; +function _load_asyncToGenerator() { + return _asyncToGenerator2 = _interopRequireDefault(__webpack_require__(1)); +} + +// We want to preserve file timestamps when copying a file, since yarn uses them to decide if a file has +// changed compared to the cache. +// There are some OS specific cases here: +// * On linux, fs.copyFile does not preserve timestamps, but does on OSX and Win. +// * On windows, you must open a file with write permissions to call `fs.futimes`. +// * On OSX you can open with read permissions and still call `fs.futimes`. +let fixTimes = (() => { + var _ref3 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (fd, dest, data) { + const doOpen = fd === undefined; + let openfd = fd ? fd : -1; + + if (disableTimestampCorrection === undefined) { + // if timestamps match already, no correction is needed. + // the need to correct timestamps varies based on OS and node versions. + const destStat = yield lstat(dest); + disableTimestampCorrection = fileDatesEqual(destStat.mtime, data.mtime); + } + + if (disableTimestampCorrection) { + return; + } + if (doOpen) { try { - yield buildToken(TOKEN_TYPES.string, JSON.parse(val)); - } catch (err) { - if (err instanceof SyntaxError) { - yield buildToken(TOKEN_TYPES.invalid); - } else { - throw err; + openfd = yield open(dest, 'a', data.mode); + } catch (er) { + // file is likely read-only + try { + openfd = yield open(dest, 'r', data.mode); + } catch (err) { + // We can't even open this file for reading. + return; } } - } else if (/^[0-9]/.test(input)) { - let val = ''; - for (let i = 0; /^[0-9]$/.test(input[i]); i++) { - val += input[i]; + } + + try { + if (openfd) { + yield futimes(openfd, data.atime, data.mtime); } - chop = val.length; + } catch (er) { + // If `futimes` throws an exception, we probably have a case of a read-only file on Windows. + // In this case we can just return. The incorrect timestamp will just cause that file to be recopied + // on subsequent installs, which will effect yarn performance but not break anything. + } finally { + if (doOpen && openfd) { + yield close(openfd); + } + } + }); - yield buildToken(TOKEN_TYPES.number, +val); - } else if (/^true/.test(input)) { - yield buildToken(TOKEN_TYPES.boolean, true); - chop = 4; - } else if (/^false/.test(input)) { - yield buildToken(TOKEN_TYPES.boolean, false); - chop = 5; - } else if (input[0] === ':') { - yield buildToken(TOKEN_TYPES.colon); - chop++; - } else if (input[0] === ',') { - yield buildToken(TOKEN_TYPES.comma); - chop++; - } else if (/^[a-zA-Z\/-]/g.test(input)) { - let name = ''; - for (let i = 0; i < input.length; i++) { - const char = input[i]; - if (char === ':' || char === ' ' || char === '\n' || char === '\r' || char === ',') { - break; - } else { - name += char; - } + return function fixTimes(_x7, _x8, _x9) { + return _ref3.apply(this, arguments); + }; +})(); + +// Compare file timestamps. +// Some versions of Node on windows zero the milliseconds when utime is used. + + +var _fs; + +function _load_fs() { + return _fs = _interopRequireDefault(__webpack_require__(3)); +} + +var _promise; + +function _load_promise() { + return _promise = __webpack_require__(40); +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +// This module serves as a wrapper for file operations that are inconsistant across node and OS versions. + +let disableTimestampCorrection = undefined; // OS dependent. will be detected on first file copy. + +const readFileBuffer = (0, (_promise || _load_promise()).promisify)((_fs || _load_fs()).default.readFile); +const close = (0, (_promise || _load_promise()).promisify)((_fs || _load_fs()).default.close); +const lstat = (0, (_promise || _load_promise()).promisify)((_fs || _load_fs()).default.lstat); +const open = (0, (_promise || _load_promise()).promisify)((_fs || _load_fs()).default.open); +const futimes = (0, (_promise || _load_promise()).promisify)((_fs || _load_fs()).default.futimes); + +const write = (0, (_promise || _load_promise()).promisify)((_fs || _load_fs()).default.write); + +const unlink = exports.unlink = (0, (_promise || _load_promise()).promisify)(__webpack_require__(233)); + +/** + * Unlinks the destination to force a recreation. This is needed on case-insensitive file systems + * to force the correct naming when the filename has changed only in character-casing. (Jest -> jest). + */ +const copyFile = exports.copyFile = (() => { + var _ref = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (data, cleanup) { + try { + yield unlink(data.dest); + yield copyFilePoly(data.src, data.dest, 0, data); + } finally { + if (cleanup) { + cleanup(); } - chop = name.length; + } + }); + + return function copyFile(_x, _x2) { + return _ref.apply(this, arguments); + }; +})(); + +// Node 8.5.0 introduced `fs.copyFile` which is much faster, so use that when available. +// Otherwise we fall back to reading and writing files as buffers. +const copyFilePoly = (src, dest, flags, data) => { + if ((_fs || _load_fs()).default.copyFile) { + return new Promise((resolve, reject) => (_fs || _load_fs()).default.copyFile(src, dest, flags, err => { + if (err) { + reject(err); + } else { + fixTimes(undefined, dest, data).then(() => resolve()).catch(ex => reject(ex)); + } + })); + } else { + return copyWithBuffer(src, dest, flags, data); + } +}; - yield buildToken(TOKEN_TYPES.string, name); - } else { - yield buildToken(TOKEN_TYPES.invalid); +const copyWithBuffer = (() => { + var _ref2 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (src, dest, flags, data) { + // Use open -> write -> futimes -> close sequence to avoid opening the file twice: + // one with writeFile and one with utimes + const fd = yield open(dest, 'w', data.mode); + try { + const buffer = yield readFileBuffer(src); + yield write(fd, buffer, 0, buffer.length); + yield fixTimes(fd, dest, data); + } finally { + yield close(fd); } + }); - if (!chop) { - // will trigger infinite recursion - yield buildToken(TOKEN_TYPES.invalid); - } + return function copyWithBuffer(_x3, _x4, _x5, _x6) { + return _ref2.apply(this, arguments); + }; +})();const fileDatesEqual = exports.fileDatesEqual = (a, b) => { + const aTime = a.getTime(); + const bTime = b.getTime(); - col += chop; - lastNewline = input[0] === '\n' || input[0] === '\r' && input[1] === '\n'; - input = input.slice(chop); + if (process.platform !== 'win32') { + return aTime === bTime; } - yield buildToken(TOKEN_TYPES.eof); -} - -class Parser { - constructor(input, fileLoc = 'lockfile') { - this.comments = []; - this.tokens = tokenise(input); - this.fileLoc = fileLoc; + // See https://github.com/nodejs/node/pull/12607 + // Submillisecond times from stat and utimes are truncated on Windows, + // causing a file with mtime 8.0079998 and 8.0081144 to become 8.007 and 8.008 + // and making it impossible to update these files to their correct timestamps. + if (Math.abs(aTime - bTime) <= 1) { + return true; } - onComment(token) { - const value = token.value; - (0, (_invariant || _load_invariant()).default)(typeof value === 'string', 'expected token value to be a string'); - - const comment = value.trim(); - - const versionMatch = comment.match(VERSION_REGEX); - if (versionMatch) { - const version = +versionMatch[1]; - if (version > (_constants || _load_constants()).LOCKFILE_VERSION) { - throw new (_errors || _load_errors()).MessageError(`Can't install from a lockfile of version ${version} as you're on an old yarn version that only supports ` + `versions up to ${(_constants || _load_constants()).LOCKFILE_VERSION}. Run \`$ yarn self-update\` to upgrade to the latest version.`); - } - } + const aTimeSec = Math.floor(aTime / 1000); + const bTimeSec = Math.floor(bTime / 1000); - this.comments.push(comment); + // See https://github.com/nodejs/node/issues/2069 + // Some versions of Node on windows zero the milliseconds when utime is used + // So if any of the time has a milliseconds part of zero we suspect that the + // bug is present and compare only seconds. + if (aTime - aTimeSec * 1000 === 0 || bTime - bTimeSec * 1000 === 0) { + return aTimeSec === bTimeSec; } - next() { - const item = this.tokens.next(); - (0, (_invariant || _load_invariant()).default)(item, 'expected a token'); - - const done = item.done, - value = item.value; + return aTime === bTime; +}; - if (done || !value) { - throw new Error('No more tokens'); - } else if (value.type === TOKEN_TYPES.comment) { - this.onComment(value); - return this.next(); - } else { - return this.token = value; - } - } +/***/ }), +/* 165 */, +/* 166 */, +/* 167 */, +/* 168 */, +/* 169 */ +/***/ (function(module, exports, __webpack_require__) { - unexpected(msg = 'Unexpected token') { - throw new SyntaxError(`${msg} ${this.token.line}:${this.token.col} in ${this.fileLoc}`); - } +"use strict"; - expect(tokType) { - if (this.token.type === tokType) { - this.next(); - } else { - this.unexpected(); - } - } - eat(tokType) { - if (this.token.type === tokType) { - this.next(); - return true; - } else { - return false; - } +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.isFakeRoot = isFakeRoot; +exports.isRootUser = isRootUser; +function getUid() { + if (process.platform !== 'win32' && process.getuid) { + return process.getuid(); } + return null; +} - parse(indent = 0) { - const obj = (0, (_map || _load_map()).default)(); - - while (true) { - const propToken = this.token; - - if (propToken.type === TOKEN_TYPES.newline) { - const nextToken = this.next(); - if (!indent) { - // if we have 0 indentation then the next token doesn't matter - continue; - } +exports.default = isRootUser(getUid()) && !isFakeRoot(); +function isFakeRoot() { + return Boolean(process.env.FAKEROOTKEY); +} - if (nextToken.type !== TOKEN_TYPES.indent) { - // if we have no indentation after a newline then we've gone down a level - break; - } +function isRootUser(uid) { + return uid === 0; +} - if (nextToken.value === indent) { - // all is good, the indent is on our level - this.next(); - } else { - // the indentation is less than our level - break; - } - } else if (propToken.type === TOKEN_TYPES.indent) { - if (propToken.value === indent) { - this.next(); - } else { - break; - } - } else if (propToken.type === TOKEN_TYPES.eof) { - break; - } else if (propToken.type === TOKEN_TYPES.string) { - // property key - const key = propToken.value; - (0, (_invariant || _load_invariant()).default)(key, 'Expected a key'); +/***/ }), +/* 170 */, +/* 171 */ +/***/ (function(module, exports, __webpack_require__) { - const keys = [key]; - this.next(); +"use strict"; - // support multiple keys - while (this.token.type === TOKEN_TYPES.comma) { - this.next(); // skip comma - const keyToken = this.token; - if (keyToken.type !== TOKEN_TYPES.string) { - this.unexpected('Expected string'); - } +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.getDataDir = getDataDir; +exports.getCacheDir = getCacheDir; +exports.getConfigDir = getConfigDir; +const path = __webpack_require__(0); +const userHome = __webpack_require__(45).default; - const key = keyToken.value; - (0, (_invariant || _load_invariant()).default)(key, 'Expected a key'); - keys.push(key); - this.next(); - } +const FALLBACK_CONFIG_DIR = path.join(userHome, '.config', 'yarn'); +const FALLBACK_CACHE_DIR = path.join(userHome, '.cache', 'yarn'); - const valToken = this.token; +function getDataDir() { + if (process.platform === 'win32') { + const WIN32_APPDATA_DIR = getLocalAppDataDir(); + return WIN32_APPDATA_DIR == null ? FALLBACK_CONFIG_DIR : path.join(WIN32_APPDATA_DIR, 'Data'); + } else if (process.env.XDG_DATA_HOME) { + return path.join(process.env.XDG_DATA_HOME, 'yarn'); + } else { + // This could arguably be ~/Library/Application Support/Yarn on Macs, + // but that feels unintuitive for a cli tool - if (valToken.type === TOKEN_TYPES.colon) { - // object - this.next(); + // Instead, use our prior fallback. Some day this could be + // path.join(userHome, '.local', 'share', 'yarn') + // or return path.join(WIN32_APPDATA_DIR, 'Data') on win32 + return FALLBACK_CONFIG_DIR; + } +} - // parse object - const val = this.parse(indent + 1); +function getCacheDir() { + if (process.platform === 'win32') { + // process.env.TEMP also exists, but most apps put caches here + return path.join(getLocalAppDataDir() || path.join(userHome, 'AppData', 'Local', 'Yarn'), 'Cache'); + } else if (process.env.XDG_CACHE_HOME) { + return path.join(process.env.XDG_CACHE_HOME, 'yarn'); + } else if (process.platform === 'darwin') { + return path.join(userHome, 'Library', 'Caches', 'Yarn'); + } else { + return FALLBACK_CACHE_DIR; + } +} - for (var _iterator = keys, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { - var _ref; +function getConfigDir() { + if (process.platform === 'win32') { + // Use our prior fallback. Some day this could be + // return path.join(WIN32_APPDATA_DIR, 'Config') + const WIN32_APPDATA_DIR = getLocalAppDataDir(); + return WIN32_APPDATA_DIR == null ? FALLBACK_CONFIG_DIR : path.join(WIN32_APPDATA_DIR, 'Config'); + } else if (process.env.XDG_CONFIG_HOME) { + return path.join(process.env.XDG_CONFIG_HOME, 'yarn'); + } else { + return FALLBACK_CONFIG_DIR; + } +} - if (_isArray) { - if (_i >= _iterator.length) break; - _ref = _iterator[_i++]; - } else { - _i = _iterator.next(); - if (_i.done) break; - _ref = _i.value; - } +function getLocalAppDataDir() { + return process.env.LOCALAPPDATA ? path.join(process.env.LOCALAPPDATA, 'Yarn') : null; +} - const key = _ref; +/***/ }), +/* 172 */, +/* 173 */ +/***/ (function(module, exports, __webpack_require__) { - obj[key] = val; - } +module.exports = { "default": __webpack_require__(179), __esModule: true }; - if (indent && this.token.type !== TOKEN_TYPES.indent) { - break; - } - } else if (isValidPropValueToken(valToken)) { - // plain value - for (var _iterator2 = keys, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) { - var _ref2; +/***/ }), +/* 174 */ +/***/ (function(module, exports, __webpack_require__) { - if (_isArray2) { - if (_i2 >= _iterator2.length) break; - _ref2 = _iterator2[_i2++]; - } else { - _i2 = _iterator2.next(); - if (_i2.done) break; - _ref2 = _i2.value; - } +"use strict"; - const key = _ref2; +module.exports = balanced; +function balanced(a, b, str) { + if (a instanceof RegExp) a = maybeMatch(a, str); + if (b instanceof RegExp) b = maybeMatch(b, str); - obj[key] = valToken.value; - } + var r = range(a, b, str); - this.next(); - } else { - this.unexpected('Invalid value type'); - } - } else { - this.unexpected(`Unknown token: ${(_util || _load_util()).default.inspect(propToken)}`); - } - } + return r && { + start: r[0], + end: r[1], + pre: str.slice(0, r[0]), + body: str.slice(r[0] + a.length, r[1]), + post: str.slice(r[1] + b.length) + }; +} - return obj; - } +function maybeMatch(reg, str) { + var m = str.match(reg); + return m ? m[0] : null; } -const MERGE_CONFLICT_ANCESTOR = '|||||||'; -const MERGE_CONFLICT_END = '>>>>>>>'; -const MERGE_CONFLICT_SEP = '======='; -const MERGE_CONFLICT_START = '<<<<<<<'; +balanced.range = range; +function range(a, b, str) { + var begs, beg, left, right, result; + var ai = str.indexOf(a); + var bi = str.indexOf(b, ai + 1); + var i = ai; -/** - * Extract the two versions of the lockfile from a merge conflict. - */ -function extractConflictVariants(str) { - const variants = [[], []]; - const lines = str.split(/\r?\n/g); - let skip = false; + if (ai >= 0 && bi > 0) { + begs = []; + left = str.length; - while (lines.length) { - const line = lines.shift(); - if (line.startsWith(MERGE_CONFLICT_START)) { - // get the first variant - while (lines.length) { - const conflictLine = lines.shift(); - if (conflictLine === MERGE_CONFLICT_SEP) { - skip = false; - break; - } else if (skip || conflictLine.startsWith(MERGE_CONFLICT_ANCESTOR)) { - skip = true; - continue; - } else { - variants[0].push(conflictLine); + while (i >= 0 && !result) { + if (i == ai) { + begs.push(i); + ai = str.indexOf(a, i + 1); + } else if (begs.length == 1) { + result = [ begs.pop(), bi ]; + } else { + beg = begs.pop(); + if (beg < left) { + left = beg; + right = bi; } - } - // get the second variant - while (lines.length) { - const conflictLine = lines.shift(); - if (conflictLine.startsWith(MERGE_CONFLICT_END)) { - break; - } else { - variants[1].push(conflictLine); - } + bi = str.indexOf(b, i + 1); } - } else { - variants[0].push(line); - variants[1].push(line); - } - } - - return [variants[0].join('\n'), variants[1].join('\n')]; -} - -/** - * Check if a lockfile has merge conflicts. - */ -function hasMergeConflicts(str) { - return str.includes(MERGE_CONFLICT_START) && str.includes(MERGE_CONFLICT_SEP) && str.includes(MERGE_CONFLICT_END); -} -/** - * Parse the lockfile. - */ -function parse(str, fileLoc) { - const parser = new Parser(str, fileLoc); - parser.next(); - return parser.parse(); -} + i = ai < bi && ai >= 0 ? ai : bi; + } -/** - * Parse and merge the two variants in a conflicted lockfile. - */ -function parseWithConflict(str, fileLoc) { - const variants = extractConflictVariants(str); - try { - return { type: 'merge', object: Object.assign({}, parse(variants[0], fileLoc), parse(variants[1], fileLoc)) }; - } catch (err) { - if (err instanceof SyntaxError) { - return { type: 'conflict', object: {} }; - } else { - throw err; + if (begs.length) { + result = [ left, right ]; } } + + return result; } + /***/ }), -/* 82 */, -/* 83 */, -/* 84 */ +/* 175 */ /***/ (function(module, exports, __webpack_require__) { -"use strict"; - +var concatMap = __webpack_require__(178); +var balanced = __webpack_require__(174); -Object.defineProperty(exports, "__esModule", { - value: true -}); +module.exports = expandTop; -var _map; +var escSlash = '\0SLASH'+Math.random()+'\0'; +var escOpen = '\0OPEN'+Math.random()+'\0'; +var escClose = '\0CLOSE'+Math.random()+'\0'; +var escComma = '\0COMMA'+Math.random()+'\0'; +var escPeriod = '\0PERIOD'+Math.random()+'\0'; -function _load_map() { - return _map = _interopRequireDefault(__webpack_require__(20)); +function numeric(str) { + return parseInt(str, 10) == str + ? parseInt(str, 10) + : str.charCodeAt(0); } -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -const debug = __webpack_require__(212)('yarn'); - -class BlockingQueue { - constructor(alias, maxConcurrency = Infinity) { - this.concurrencyQueue = []; - this.maxConcurrency = maxConcurrency; - this.runningCount = 0; - this.warnedStuck = false; - this.alias = alias; - this.first = true; - - this.running = (0, (_map || _load_map()).default)(); - this.queue = (0, (_map || _load_map()).default)(); - - this.stuckTick = this.stuckTick.bind(this); - } - - stillActive() { - if (this.stuckTimer) { - clearTimeout(this.stuckTimer); - } +function escapeBraces(str) { + return str.split('\\\\').join(escSlash) + .split('\\{').join(escOpen) + .split('\\}').join(escClose) + .split('\\,').join(escComma) + .split('\\.').join(escPeriod); +} - this.stuckTimer = setTimeout(this.stuckTick, 5000); +function unescapeBraces(str) { + return str.split(escSlash).join('\\') + .split(escOpen).join('{') + .split(escClose).join('}') + .split(escComma).join(',') + .split(escPeriod).join('.'); +} - // We need to check the existence of unref because of https://github.com/facebook/jest/issues/4559 - // $FlowFixMe: Node's setInterval returns a Timeout, not a Number - this.stuckTimer.unref && this.stuckTimer.unref(); - } - stuckTick() { - if (this.runningCount === 1) { - this.warnedStuck = true; - debug(`The ${JSON.stringify(this.alias)} blocking queue may be stuck. 5 seconds ` + `without any activity with 1 worker: ${Object.keys(this.running)[0]}`); - } - } +// Basically just str.split(","), but handling cases +// where we have nested braced sections, which should be +// treated as individual members, like {a,{b,c},d} +function parseCommaParts(str) { + if (!str) + return ['']; - push(key, factory) { - if (this.first) { - this.first = false; - } else { - this.stillActive(); - } + var parts = []; + var m = balanced('{', '}', str); - return new Promise((resolve, reject) => { - // we're already running so push ourselves to the queue - const queue = this.queue[key] = this.queue[key] || []; - queue.push({ factory, resolve, reject }); + if (!m) + return str.split(','); - if (!this.running[key]) { - this.shift(key); - } - }); - } + var pre = m.pre; + var body = m.body; + var post = m.post; + var p = pre.split(','); - shift(key) { - if (this.running[key]) { - delete this.running[key]; - this.runningCount--; + p[p.length-1] += '{' + body + '}'; + var postParts = parseCommaParts(post); + if (post.length) { + p[p.length-1] += postParts.shift(); + p.push.apply(p, postParts); + } - if (this.stuckTimer) { - clearTimeout(this.stuckTimer); - this.stuckTimer = null; - } + parts.push.apply(parts, p); - if (this.warnedStuck) { - this.warnedStuck = false; - debug(`${JSON.stringify(this.alias)} blocking queue finally resolved. Nothing to worry about.`); - } - } + return parts; +} - const queue = this.queue[key]; - if (!queue) { - return; - } +function expandTop(str) { + if (!str) + return []; - var _queue$shift = queue.shift(); + // I don't know why Bash 4.3 does this, but it does. + // Anything starting with {} will have the first two bytes preserved + // but *only* at the top level, so {},a}b will not expand to anything, + // but a{},b}c will be expanded to [a}c,abc]. + // One could argue that this is a bug in Bash, but since the goal of + // this module is to match Bash's rules, we escape a leading {} + if (str.substr(0, 2) === '{}') { + str = '\\{\\}' + str.substr(2); + } - const resolve = _queue$shift.resolve, - reject = _queue$shift.reject, - factory = _queue$shift.factory; + return expand(escapeBraces(str), true).map(unescapeBraces); +} - if (!queue.length) { - delete this.queue[key]; - } +function identity(e) { + return e; +} - const next = () => { - this.shift(key); - this.shiftConcurrencyQueue(); - }; +function embrace(str) { + return '{' + str + '}'; +} +function isPadded(el) { + return /^-?0\d/.test(el); +} - const run = () => { - this.running[key] = true; - this.runningCount++; +function lte(i, y) { + return i <= y; +} +function gte(i, y) { + return i >= y; +} - factory().then(function (val) { - resolve(val); - next(); - return null; - }).catch(function (err) { - reject(err); - next(); - }); - }; +function expand(str, isTop) { + var expansions = []; - this.maybePushConcurrencyQueue(run); - } + var m = balanced('{', '}', str); + if (!m || /\$$/.test(m.pre)) return [str]; - maybePushConcurrencyQueue(run) { - if (this.runningCount < this.maxConcurrency) { - run(); - } else { - this.concurrencyQueue.push(run); + var isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body); + var isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body); + var isSequence = isNumericSequence || isAlphaSequence; + var isOptions = m.body.indexOf(',') >= 0; + if (!isSequence && !isOptions) { + // {a},b} + if (m.post.match(/,.*\}/)) { + str = m.pre + '{' + m.body + escClose + m.post; + return expand(str); } + return [str]; } - shiftConcurrencyQueue() { - if (this.runningCount < this.maxConcurrency) { - const fn = this.concurrencyQueue.shift(); - if (fn) { - fn(); + var n; + if (isSequence) { + n = m.body.split(/\.\./); + } else { + n = parseCommaParts(m.body); + if (n.length === 1) { + // x{{a,b}}y ==> x{a}y x{b}y + n = expand(n[0], false).map(embrace); + if (n.length === 1) { + var post = m.post.length + ? expand(m.post, false) + : ['']; + return post.map(function(p) { + return m.pre + n[0] + p; + }); } } } -} -exports.default = BlockingQueue; - -/***/ }), -/* 85 */ -/***/ (function(module, exports) { -module.exports = function (exec) { - try { - return !!exec(); - } catch (e) { - return true; - } -}; + // at this point, n is the parts, and we know it's not a comma set + // with a single entry. + // no need to expand pre, since it is guaranteed to be free of brace-sets + var pre = m.pre; + var post = m.post.length + ? expand(m.post, false) + : ['']; -/***/ }), -/* 86 */, -/* 87 */, -/* 88 */, -/* 89 */, -/* 90 */, -/* 91 */, -/* 92 */, -/* 93 */, -/* 94 */, -/* 95 */, -/* 96 */, -/* 97 */, -/* 98 */, -/* 99 */, -/* 100 */ -/***/ (function(module, exports, __webpack_require__) { + var N; -// getting tag from 19.1.3.6 Object.prototype.toString() -var cof = __webpack_require__(47); -var TAG = __webpack_require__(13)('toStringTag'); -// ES3 wrong here -var ARG = cof(function () { return arguments; }()) == 'Arguments'; + if (isSequence) { + var x = numeric(n[0]); + var y = numeric(n[1]); + var width = Math.max(n[0].length, n[1].length) + var incr = n.length == 3 + ? Math.abs(numeric(n[2])) + : 1; + var test = lte; + var reverse = y < x; + if (reverse) { + incr *= -1; + test = gte; + } + var pad = n.some(isPadded); -// fallback for IE11 Script Access Denied error -var tryGet = function (it, key) { - try { - return it[key]; - } catch (e) { /* empty */ } -}; + N = []; -module.exports = function (it) { - var O, T, B; - return it === undefined ? 'Undefined' : it === null ? 'Null' - // @@toStringTag case - : typeof (T = tryGet(O = Object(it), TAG)) == 'string' ? T - // builtinTag case - : ARG ? cof(O) - // ES3 arguments fallback - : (B = cof(O)) == 'Object' && typeof O.callee == 'function' ? 'Arguments' : B; -}; + for (var i = x; test(i, y); i += incr) { + var c; + if (isAlphaSequence) { + c = String.fromCharCode(i); + if (c === '\\') + c = ''; + } else { + c = String(i); + if (pad) { + var need = width - c.length; + if (need > 0) { + var z = new Array(need + 1).join('0'); + if (i < 0) + c = '-' + z + c.slice(1); + else + c = z + c; + } + } + } + N.push(c); + } + } else { + N = concatMap(n, function(el) { return expand(el, false) }); + } + for (var j = 0; j < N.length; j++) { + for (var k = 0; k < post.length; k++) { + var expansion = pre + N[j] + post[k]; + if (!isTop || isSequence || expansion) + expansions.push(expansion); + } + } -/***/ }), -/* 101 */ -/***/ (function(module, exports) { + return expansions; +} -// IE 8- don't enum bug keys -module.exports = ( - 'constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf' -).split(','); /***/ }), -/* 102 */ +/* 176 */ /***/ (function(module, exports, __webpack_require__) { -var document = __webpack_require__(11).document; -module.exports = document && document.documentElement; - +"use strict"; -/***/ }), -/* 103 */ -/***/ (function(module, exports, __webpack_require__) { -"use strict"; +function preserveCamelCase(str) { + let isLastCharLower = false; + let isLastCharUpper = false; + let isLastLastCharUpper = false; -var LIBRARY = __webpack_require__(69); -var $export = __webpack_require__(41); -var redefine = __webpack_require__(197); -var hide = __webpack_require__(31); -var Iterators = __webpack_require__(35); -var $iterCreate = __webpack_require__(188); -var setToStringTag = __webpack_require__(71); -var getPrototypeOf = __webpack_require__(194); -var ITERATOR = __webpack_require__(13)('iterator'); -var BUGGY = !([].keys && 'next' in [].keys()); // Safari has buggy iterators w/o `next` -var FF_ITERATOR = '@@iterator'; -var KEYS = 'keys'; -var VALUES = 'values'; + for (let i = 0; i < str.length; i++) { + const c = str[i]; -var returnThis = function () { return this; }; + if (isLastCharLower && /[a-zA-Z]/.test(c) && c.toUpperCase() === c) { + str = str.substr(0, i) + '-' + str.substr(i); + isLastCharLower = false; + isLastLastCharUpper = isLastCharUpper; + isLastCharUpper = true; + i++; + } else if (isLastCharUpper && isLastLastCharUpper && /[a-zA-Z]/.test(c) && c.toLowerCase() === c) { + str = str.substr(0, i - 1) + '-' + str.substr(i - 1); + isLastLastCharUpper = isLastCharUpper; + isLastCharUpper = false; + isLastCharLower = true; + } else { + isLastCharLower = c.toLowerCase() === c; + isLastLastCharUpper = isLastCharUpper; + isLastCharUpper = c.toUpperCase() === c; + } + } -module.exports = function (Base, NAME, Constructor, next, DEFAULT, IS_SET, FORCED) { - $iterCreate(Constructor, NAME, next); - var getMethod = function (kind) { - if (!BUGGY && kind in proto) return proto[kind]; - switch (kind) { - case KEYS: return function keys() { return new Constructor(this, kind); }; - case VALUES: return function values() { return new Constructor(this, kind); }; - } return function entries() { return new Constructor(this, kind); }; - }; - var TAG = NAME + ' Iterator'; - var DEF_VALUES = DEFAULT == VALUES; - var VALUES_BUG = false; - var proto = Base.prototype; - var $native = proto[ITERATOR] || proto[FF_ITERATOR] || DEFAULT && proto[DEFAULT]; - var $default = $native || getMethod(DEFAULT); - var $entries = DEFAULT ? !DEF_VALUES ? $default : getMethod('entries') : undefined; - var $anyNative = NAME == 'Array' ? proto.entries || $native : $native; - var methods, key, IteratorPrototype; - // Fix native - if ($anyNative) { - IteratorPrototype = getPrototypeOf($anyNative.call(new Base())); - if (IteratorPrototype !== Object.prototype && IteratorPrototype.next) { - // Set @@toStringTag to native iterators - setToStringTag(IteratorPrototype, TAG, true); - // fix for some old engines - if (!LIBRARY && typeof IteratorPrototype[ITERATOR] != 'function') hide(IteratorPrototype, ITERATOR, returnThis); - } - } - // fix Array#{values, @@iterator}.name in V8 / FF - if (DEF_VALUES && $native && $native.name !== VALUES) { - VALUES_BUG = true; - $default = function values() { return $native.call(this); }; - } - // Define iterator - if ((!LIBRARY || FORCED) && (BUGGY || VALUES_BUG || !proto[ITERATOR])) { - hide(proto, ITERATOR, $default); - } - // Plug for library - Iterators[NAME] = $default; - Iterators[TAG] = returnThis; - if (DEFAULT) { - methods = { - values: DEF_VALUES ? $default : getMethod(VALUES), - keys: IS_SET ? $default : getMethod(KEYS), - entries: $entries - }; - if (FORCED) for (key in methods) { - if (!(key in proto)) redefine(proto, key, methods[key]); - } else $export($export.P + $export.F * (BUGGY || VALUES_BUG), NAME, methods); - } - return methods; -}; + return str; +} +module.exports = function (str) { + if (arguments.length > 1) { + str = Array.from(arguments) + .map(x => x.trim()) + .filter(x => x.length) + .join('-'); + } else { + str = str.trim(); + } -/***/ }), -/* 104 */ -/***/ (function(module, exports) { + if (str.length === 0) { + return ''; + } -module.exports = function (exec) { - try { - return { e: false, v: exec() }; - } catch (e) { - return { e: true, v: e }; - } -}; + if (str.length === 1) { + return str.toLowerCase(); + } + if (/^[a-z0-9]+$/.test(str)) { + return str; + } -/***/ }), -/* 105 */ -/***/ (function(module, exports, __webpack_require__) { + const hasUpperCase = str !== str.toLowerCase(); -var anObject = __webpack_require__(27); -var isObject = __webpack_require__(34); -var newPromiseCapability = __webpack_require__(70); + if (hasUpperCase) { + str = preserveCamelCase(str); + } -module.exports = function (C, x) { - anObject(C); - if (isObject(x) && x.constructor === C) return x; - var promiseCapability = newPromiseCapability.f(C); - var resolve = promiseCapability.resolve; - resolve(x); - return promiseCapability.promise; + return str + .replace(/^[_.\- ]+/, '') + .toLowerCase() + .replace(/[_.\- ]+(\w|$)/g, (m, p1) => p1.toUpperCase()); }; /***/ }), -/* 106 */ +/* 177 */, +/* 178 */ /***/ (function(module, exports) { -module.exports = function (bitmap, value) { - return { - enumerable: !(bitmap & 1), - configurable: !(bitmap & 2), - writable: !(bitmap & 4), - value: value - }; +module.exports = function (xs, fn) { + var res = []; + for (var i = 0; i < xs.length; i++) { + var x = fn(xs[i], i); + if (isArray(x)) res.push.apply(res, x); + else res.push(x); + } + return res; }; - -/***/ }), -/* 107 */ -/***/ (function(module, exports, __webpack_require__) { - -var core = __webpack_require__(23); -var global = __webpack_require__(11); -var SHARED = '__core-js_shared__'; -var store = global[SHARED] || (global[SHARED] = {}); - -(module.exports = function (key, value) { - return store[key] || (store[key] = value !== undefined ? value : {}); -})('versions', []).push({ - version: core.version, - mode: __webpack_require__(69) ? 'pure' : 'global', - copyright: '© 2018 Denis Pushkarev (zloirock.ru)' -}); - - -/***/ }), -/* 108 */ -/***/ (function(module, exports, __webpack_require__) { - -// 7.3.20 SpeciesConstructor(O, defaultConstructor) -var anObject = __webpack_require__(27); -var aFunction = __webpack_require__(46); -var SPECIES = __webpack_require__(13)('species'); -module.exports = function (O, D) { - var C = anObject(O).constructor; - var S; - return C === undefined || (S = anObject(C)[SPECIES]) == undefined ? D : aFunction(S); +var isArray = Array.isArray || function (xs) { + return Object.prototype.toString.call(xs) === '[object Array]'; }; /***/ }), -/* 109 */ -/***/ (function(module, exports, __webpack_require__) { - -var ctx = __webpack_require__(48); -var invoke = __webpack_require__(185); -var html = __webpack_require__(102); -var cel = __webpack_require__(68); -var global = __webpack_require__(11); -var process = global.process; -var setTask = global.setImmediate; -var clearTask = global.clearImmediate; -var MessageChannel = global.MessageChannel; -var Dispatch = global.Dispatch; -var counter = 0; -var queue = {}; -var ONREADYSTATECHANGE = 'onreadystatechange'; -var defer, channel, port; -var run = function () { - var id = +this; - // eslint-disable-next-line no-prototype-builtins - if (queue.hasOwnProperty(id)) { - var fn = queue[id]; - delete queue[id]; - fn(); - } -}; -var listener = function (event) { - run.call(event.data); -}; -// Node.js 0.9+ & IE10+ has setImmediate, otherwise: -if (!setTask || !clearTask) { - setTask = function setImmediate(fn) { - var args = []; - var i = 1; - while (arguments.length > i) args.push(arguments[i++]); - queue[++counter] = function () { - // eslint-disable-next-line no-new-func - invoke(typeof fn == 'function' ? fn : Function(fn), args); - }; - defer(counter); - return counter; - }; - clearTask = function clearImmediate(id) { - delete queue[id]; - }; - // Node.js 0.8- - if (__webpack_require__(47)(process) == 'process') { - defer = function (id) { - process.nextTick(ctx(run, id, 1)); - }; - // Sphere (JS game engine) Dispatch API - } else if (Dispatch && Dispatch.now) { - defer = function (id) { - Dispatch.now(ctx(run, id, 1)); - }; - // Browsers with MessageChannel, includes WebWorkers - } else if (MessageChannel) { - channel = new MessageChannel(); - port = channel.port2; - channel.port1.onmessage = listener; - defer = ctx(port.postMessage, port, 1); - // Browsers with postMessage, skip WebWorkers - // IE8 has postMessage, but it's sync & typeof its postMessage is 'object' - } else if (global.addEventListener && typeof postMessage == 'function' && !global.importScripts) { - defer = function (id) { - global.postMessage(id + '', '*'); - }; - global.addEventListener('message', listener, false); - // IE8- - } else if (ONREADYSTATECHANGE in cel('script')) { - defer = function (id) { - html.appendChild(cel('script'))[ONREADYSTATECHANGE] = function () { - html.removeChild(this); - run.call(id); - }; - }; - // Rest old browsers - } else { - defer = function (id) { - setTimeout(ctx(run, id, 1), 0); - }; - } -} -module.exports = { - set: setTask, - clear: clearTask +/* 179 */ +/***/ (function(module, exports, __webpack_require__) { + +__webpack_require__(205); +__webpack_require__(207); +__webpack_require__(210); +__webpack_require__(206); +__webpack_require__(208); +__webpack_require__(209); +module.exports = __webpack_require__(23).Promise; + + +/***/ }), +/* 180 */ +/***/ (function(module, exports) { + +module.exports = function () { /* empty */ }; + + +/***/ }), +/* 181 */ +/***/ (function(module, exports) { + +module.exports = function (it, Constructor, name, forbiddenField) { + if (!(it instanceof Constructor) || (forbiddenField !== undefined && forbiddenField in it)) { + throw TypeError(name + ': incorrect invocation!'); + } return it; }; /***/ }), -/* 110 */ +/* 182 */ /***/ (function(module, exports, __webpack_require__) { -// 7.1.15 ToLength -var toInteger = __webpack_require__(73); -var min = Math.min; -module.exports = function (it) { - return it > 0 ? min(toInteger(it), 0x1fffffffffffff) : 0; // pow(2, 53) - 1 == 9007199254740991 +// false -> Array#indexOf +// true -> Array#includes +var toIObject = __webpack_require__(74); +var toLength = __webpack_require__(110); +var toAbsoluteIndex = __webpack_require__(200); +module.exports = function (IS_INCLUDES) { + return function ($this, el, fromIndex) { + var O = toIObject($this); + var length = toLength(O.length); + var index = toAbsoluteIndex(fromIndex, length); + var value; + // Array#includes uses SameValueZero equality algorithm + // eslint-disable-next-line no-self-compare + if (IS_INCLUDES && el != el) while (length > index) { + value = O[index++]; + // eslint-disable-next-line no-self-compare + if (value != value) return true; + // Array#indexOf ignores holes, Array#includes - not + } else for (;length > index; index++) if (IS_INCLUDES || index in O) { + if (O[index] === el) return IS_INCLUDES || index || 0; + } return !IS_INCLUDES && -1; + }; }; /***/ }), -/* 111 */ -/***/ (function(module, exports) { +/* 183 */ +/***/ (function(module, exports, __webpack_require__) { -var id = 0; -var px = Math.random(); -module.exports = function (key) { - return 'Symbol('.concat(key === undefined ? '' : key, ')_', (++id + px).toString(36)); +var ctx = __webpack_require__(48); +var call = __webpack_require__(187); +var isArrayIter = __webpack_require__(186); +var anObject = __webpack_require__(27); +var toLength = __webpack_require__(110); +var getIterFn = __webpack_require__(203); +var BREAK = {}; +var RETURN = {}; +var exports = module.exports = function (iterable, entries, fn, that, ITERATOR) { + var iterFn = ITERATOR ? function () { return iterable; } : getIterFn(iterable); + var f = ctx(fn, that, entries ? 2 : 1); + var index = 0; + var length, step, iterator, result; + if (typeof iterFn != 'function') throw TypeError(iterable + ' is not iterable!'); + // fast case for arrays with default iterator + if (isArrayIter(iterFn)) for (length = toLength(iterable.length); length > index; index++) { + result = entries ? f(anObject(step = iterable[index])[0], step[1]) : f(iterable[index]); + if (result === BREAK || result === RETURN) return result; + } else for (iterator = iterFn.call(iterable); !(step = iterator.next()).done;) { + result = call(iterator, f, step.value, entries); + if (result === BREAK || result === RETURN) return result; + } }; +exports.BREAK = BREAK; +exports.RETURN = RETURN; /***/ }), -/* 112 */ +/* 184 */ /***/ (function(module, exports, __webpack_require__) { +module.exports = !__webpack_require__(33) && !__webpack_require__(85)(function () { + return Object.defineProperty(__webpack_require__(68)('div'), 'a', { get: function () { return 7; } }).a != 7; +}); -/** - * This is the common logic for both the Node.js and web browser - * implementations of `debug()`. - * - * Expose `debug()` as the module. - */ -exports = module.exports = createDebug.debug = createDebug['default'] = createDebug; -exports.coerce = coerce; -exports.disable = disable; -exports.enable = enable; -exports.enabled = enabled; -exports.humanize = __webpack_require__(229); +/***/ }), +/* 185 */ +/***/ (function(module, exports) { -/** - * Active `debug` instances. - */ -exports.instances = []; +// fast apply, http://jsperf.lnkit.com/fast-apply/5 +module.exports = function (fn, args, that) { + var un = that === undefined; + switch (args.length) { + case 0: return un ? fn() + : fn.call(that); + case 1: return un ? fn(args[0]) + : fn.call(that, args[0]); + case 2: return un ? fn(args[0], args[1]) + : fn.call(that, args[0], args[1]); + case 3: return un ? fn(args[0], args[1], args[2]) + : fn.call(that, args[0], args[1], args[2]); + case 4: return un ? fn(args[0], args[1], args[2], args[3]) + : fn.call(that, args[0], args[1], args[2], args[3]); + } return fn.apply(that, args); +}; -/** - * The currently active debug mode names, and names to skip. - */ -exports.names = []; -exports.skips = []; +/***/ }), +/* 186 */ +/***/ (function(module, exports, __webpack_require__) { -/** - * Map of special "%n" handling functions, for the debug "format" argument. - * - * Valid key names are a single, lower or upper-case letter, i.e. "n" and "N". - */ +// check on default Array iterator +var Iterators = __webpack_require__(35); +var ITERATOR = __webpack_require__(13)('iterator'); +var ArrayProto = Array.prototype; -exports.formatters = {}; +module.exports = function (it) { + return it !== undefined && (Iterators.Array === it || ArrayProto[ITERATOR] === it); +}; -/** - * Select a color. - * @param {String} namespace - * @return {Number} - * @api private - */ -function selectColor(namespace) { - var hash = 0, i; +/***/ }), +/* 187 */ +/***/ (function(module, exports, __webpack_require__) { - for (i in namespace) { - hash = ((hash << 5) - hash) + namespace.charCodeAt(i); - hash |= 0; // Convert to 32bit integer +// call something on iterator step with safe closing on error +var anObject = __webpack_require__(27); +module.exports = function (iterator, fn, value, entries) { + try { + return entries ? fn(anObject(value)[0], value[1]) : fn(value); + // 7.4.6 IteratorClose(iterator, completion) + } catch (e) { + var ret = iterator['return']; + if (ret !== undefined) anObject(ret.call(iterator)); + throw e; } +}; - return exports.colors[Math.abs(hash) % exports.colors.length]; -} -/** - * Create a debugger with the given `namespace`. - * - * @param {String} namespace - * @return {Function} - * @api public - */ +/***/ }), +/* 188 */ +/***/ (function(module, exports, __webpack_require__) { -function createDebug(namespace) { +"use strict"; - var prevTime; +var create = __webpack_require__(192); +var descriptor = __webpack_require__(106); +var setToStringTag = __webpack_require__(71); +var IteratorPrototype = {}; - function debug() { - // disabled? - if (!debug.enabled) return; +// 25.1.2.1.1 %IteratorPrototype%[@@iterator]() +__webpack_require__(31)(IteratorPrototype, __webpack_require__(13)('iterator'), function () { return this; }); - var self = debug; +module.exports = function (Constructor, NAME, next) { + Constructor.prototype = create(IteratorPrototype, { next: descriptor(1, next) }); + setToStringTag(Constructor, NAME + ' Iterator'); +}; - // set `diff` timestamp - var curr = +new Date(); - var ms = curr - (prevTime || curr); - self.diff = ms; - self.prev = prevTime; - self.curr = curr; - prevTime = curr; - // turn the `arguments` into a proper Array - var args = new Array(arguments.length); - for (var i = 0; i < args.length; i++) { - args[i] = arguments[i]; - } +/***/ }), +/* 189 */ +/***/ (function(module, exports, __webpack_require__) { - args[0] = exports.coerce(args[0]); +var ITERATOR = __webpack_require__(13)('iterator'); +var SAFE_CLOSING = false; - if ('string' !== typeof args[0]) { - // anything else let's inspect with %O - args.unshift('%O'); - } +try { + var riter = [7][ITERATOR](); + riter['return'] = function () { SAFE_CLOSING = true; }; + // eslint-disable-next-line no-throw-literal + Array.from(riter, function () { throw 2; }); +} catch (e) { /* empty */ } - // apply any `formatters` transformations - var index = 0; - args[0] = args[0].replace(/%([a-zA-Z%])/g, function(match, format) { - // if we encounter an escaped % then don't increase the array index - if (match === '%%') return match; - index++; - var formatter = exports.formatters[format]; - if ('function' === typeof formatter) { - var val = args[index]; - match = formatter.call(self, val); +module.exports = function (exec, skipClosing) { + if (!skipClosing && !SAFE_CLOSING) return false; + var safe = false; + try { + var arr = [7]; + var iter = arr[ITERATOR](); + iter.next = function () { return { done: safe = true }; }; + arr[ITERATOR] = function () { return iter; }; + exec(arr); + } catch (e) { /* empty */ } + return safe; +}; - // now we need to remove `args[index]` since it's inlined in the `format` - args.splice(index, 1); - index--; - } - return match; - }); - // apply env-specific formatting (colors, etc.) - exports.formatArgs.call(self, args); +/***/ }), +/* 190 */ +/***/ (function(module, exports) { - var logFn = debug.log || exports.log || console.log.bind(console); - logFn.apply(self, args); - } +module.exports = function (done, value) { + return { value: value, done: !!done }; +}; - debug.namespace = namespace; - debug.enabled = exports.enabled(namespace); - debug.useColors = exports.useColors(); - debug.color = selectColor(namespace); - debug.destroy = destroy; - // env-specific initialization logic for debug instances - if ('function' === typeof exports.init) { - exports.init(debug); - } +/***/ }), +/* 191 */ +/***/ (function(module, exports, __webpack_require__) { - exports.instances.push(debug); +var global = __webpack_require__(11); +var macrotask = __webpack_require__(109).set; +var Observer = global.MutationObserver || global.WebKitMutationObserver; +var process = global.process; +var Promise = global.Promise; +var isNode = __webpack_require__(47)(process) == 'process'; - return debug; -} +module.exports = function () { + var head, last, notify; -function destroy () { - var index = exports.instances.indexOf(this); - if (index !== -1) { - exports.instances.splice(index, 1); - return true; + var flush = function () { + var parent, fn; + if (isNode && (parent = process.domain)) parent.exit(); + while (head) { + fn = head.fn; + head = head.next; + try { + fn(); + } catch (e) { + if (head) notify(); + else last = undefined; + throw e; + } + } last = undefined; + if (parent) parent.enter(); + }; + + // Node.js + if (isNode) { + notify = function () { + process.nextTick(flush); + }; + // browsers with MutationObserver, except iOS Safari - https://github.com/zloirock/core-js/issues/339 + } else if (Observer && !(global.navigator && global.navigator.standalone)) { + var toggle = true; + var node = document.createTextNode(''); + new Observer(flush).observe(node, { characterData: true }); // eslint-disable-line no-new + notify = function () { + node.data = toggle = !toggle; + }; + // environments with maybe non-completely correct, but existent Promise + } else if (Promise && Promise.resolve) { + // Promise.resolve without an argument throws an error in LG WebOS 2 + var promise = Promise.resolve(undefined); + notify = function () { + promise.then(flush); + }; + // for other environments - macrotask based on: + // - setImmediate + // - MessageChannel + // - window.postMessag + // - onreadystatechange + // - setTimeout } else { - return false; + notify = function () { + // strange IE + webpack dev server bug - use .call(global) + macrotask.call(global, flush); + }; } -} -/** - * Enables a debug mode by namespaces. This can include modes - * separated by a colon and wildcards. - * - * @param {String} namespaces - * @api public - */ + return function (fn) { + var task = { fn: fn, next: undefined }; + if (last) last.next = task; + if (!head) { + head = task; + notify(); + } last = task; + }; +}; -function enable(namespaces) { - exports.save(namespaces); - exports.names = []; - exports.skips = []; +/***/ }), +/* 192 */ +/***/ (function(module, exports, __webpack_require__) { - var i; - var split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/); - var len = split.length; +// 19.1.2.2 / 15.2.3.5 Object.create(O [, Properties]) +var anObject = __webpack_require__(27); +var dPs = __webpack_require__(193); +var enumBugKeys = __webpack_require__(101); +var IE_PROTO = __webpack_require__(72)('IE_PROTO'); +var Empty = function () { /* empty */ }; +var PROTOTYPE = 'prototype'; - for (i = 0; i < len; i++) { - if (!split[i]) continue; // ignore empty strings - namespaces = split[i].replace(/\*/g, '.*?'); - if (namespaces[0] === '-') { - exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); - } else { - exports.names.push(new RegExp('^' + namespaces + '$')); - } - } +// Create object with fake `null` prototype: use iframe Object with cleared prototype +var createDict = function () { + // Thrash, waste and sodomy: IE GC bug + var iframe = __webpack_require__(68)('iframe'); + var i = enumBugKeys.length; + var lt = '<'; + var gt = '>'; + var iframeDocument; + iframe.style.display = 'none'; + __webpack_require__(102).appendChild(iframe); + iframe.src = 'javascript:'; // eslint-disable-line no-script-url + // createDict = iframe.contentWindow.Object; + // html.removeChild(iframe); + iframeDocument = iframe.contentWindow.document; + iframeDocument.open(); + iframeDocument.write(lt + 'script' + gt + 'document.F=Object' + lt + '/script' + gt); + iframeDocument.close(); + createDict = iframeDocument.F; + while (i--) delete createDict[PROTOTYPE][enumBugKeys[i]]; + return createDict(); +}; - for (i = 0; i < exports.instances.length; i++) { - var instance = exports.instances[i]; - instance.enabled = exports.enabled(instance.namespace); - } -} +module.exports = Object.create || function create(O, Properties) { + var result; + if (O !== null) { + Empty[PROTOTYPE] = anObject(O); + result = new Empty(); + Empty[PROTOTYPE] = null; + // add "__proto__" for Object.getPrototypeOf polyfill + result[IE_PROTO] = O; + } else result = createDict(); + return Properties === undefined ? result : dPs(result, Properties); +}; -/** - * Disable debug output. - * - * @api public - */ -function disable() { - exports.enable(''); -} +/***/ }), +/* 193 */ +/***/ (function(module, exports, __webpack_require__) { -/** - * Returns true if the given mode name is enabled, false otherwise. - * - * @param {String} name - * @return {Boolean} - * @api public - */ +var dP = __webpack_require__(50); +var anObject = __webpack_require__(27); +var getKeys = __webpack_require__(132); -function enabled(name) { - if (name[name.length - 1] === '*') { - return true; - } - var i, len; - for (i = 0, len = exports.skips.length; i < len; i++) { - if (exports.skips[i].test(name)) { - return false; - } - } - for (i = 0, len = exports.names.length; i < len; i++) { - if (exports.names[i].test(name)) { - return true; - } +module.exports = __webpack_require__(33) ? Object.defineProperties : function defineProperties(O, Properties) { + anObject(O); + var keys = getKeys(Properties); + var length = keys.length; + var i = 0; + var P; + while (length > i) dP.f(O, P = keys[i++], Properties[P]); + return O; +}; + + +/***/ }), +/* 194 */ +/***/ (function(module, exports, __webpack_require__) { + +// 19.1.2.9 / 15.2.3.2 Object.getPrototypeOf(O) +var has = __webpack_require__(49); +var toObject = __webpack_require__(133); +var IE_PROTO = __webpack_require__(72)('IE_PROTO'); +var ObjectProto = Object.prototype; + +module.exports = Object.getPrototypeOf || function (O) { + O = toObject(O); + if (has(O, IE_PROTO)) return O[IE_PROTO]; + if (typeof O.constructor == 'function' && O instanceof O.constructor) { + return O.constructor.prototype; + } return O instanceof Object ? ObjectProto : null; +}; + + +/***/ }), +/* 195 */ +/***/ (function(module, exports, __webpack_require__) { + +var has = __webpack_require__(49); +var toIObject = __webpack_require__(74); +var arrayIndexOf = __webpack_require__(182)(false); +var IE_PROTO = __webpack_require__(72)('IE_PROTO'); + +module.exports = function (object, names) { + var O = toIObject(object); + var i = 0; + var result = []; + var key; + for (key in O) if (key != IE_PROTO) has(O, key) && result.push(key); + // Don't enum bug & hidden keys + while (names.length > i) if (has(O, key = names[i++])) { + ~arrayIndexOf(result, key) || result.push(key); } - return false; -} + return result; +}; -/** - * Coerce `val`. - * - * @param {Mixed} val - * @return {Mixed} - * @api private - */ -function coerce(val) { - if (val instanceof Error) return val.stack || val.message; - return val; -} +/***/ }), +/* 196 */ +/***/ (function(module, exports, __webpack_require__) { + +var hide = __webpack_require__(31); +module.exports = function (target, src, safe) { + for (var key in src) { + if (safe && target[key]) target[key] = src[key]; + else hide(target, key, src[key]); + } return target; +}; /***/ }), -/* 113 */, -/* 114 */ +/* 197 */ /***/ (function(module, exports, __webpack_require__) { -module.exports = realpath -realpath.realpath = realpath -realpath.sync = realpathSync -realpath.realpathSync = realpathSync -realpath.monkeypatch = monkeypatch -realpath.unmonkeypatch = unmonkeypatch +module.exports = __webpack_require__(31); -var fs = __webpack_require__(3) -var origRealpath = fs.realpath -var origRealpathSync = fs.realpathSync -var version = process.version -var ok = /^v[0-5]\./.test(version) -var old = __webpack_require__(217) +/***/ }), +/* 198 */ +/***/ (function(module, exports, __webpack_require__) { -function newError (er) { - return er && er.syscall === 'realpath' && ( - er.code === 'ELOOP' || - er.code === 'ENOMEM' || - er.code === 'ENAMETOOLONG' - ) -} +"use strict"; -function realpath (p, cache, cb) { - if (ok) { - return origRealpath(p, cache, cb) - } +var global = __webpack_require__(11); +var core = __webpack_require__(23); +var dP = __webpack_require__(50); +var DESCRIPTORS = __webpack_require__(33); +var SPECIES = __webpack_require__(13)('species'); - if (typeof cache === 'function') { - cb = cache - cache = null - } - origRealpath(p, cache, function (er, result) { - if (newError(er)) { - old.realpath(p, cache, cb) - } else { - cb(er, result) - } - }) -} +module.exports = function (KEY) { + var C = typeof core[KEY] == 'function' ? core[KEY] : global[KEY]; + if (DESCRIPTORS && C && !C[SPECIES]) dP.f(C, SPECIES, { + configurable: true, + get: function () { return this; } + }); +}; -function realpathSync (p, cache) { - if (ok) { - return origRealpathSync(p, cache) - } - try { - return origRealpathSync(p, cache) - } catch (er) { - if (newError(er)) { - return old.realpathSync(p, cache) - } else { - throw er - } - } -} +/***/ }), +/* 199 */ +/***/ (function(module, exports, __webpack_require__) { -function monkeypatch () { - fs.realpath = realpath - fs.realpathSync = realpathSync -} +var toInteger = __webpack_require__(73); +var defined = __webpack_require__(67); +// true -> String#at +// false -> String#codePointAt +module.exports = function (TO_STRING) { + return function (that, pos) { + var s = String(defined(that)); + var i = toInteger(pos); + var l = s.length; + var a, b; + if (i < 0 || i >= l) return TO_STRING ? '' : undefined; + a = s.charCodeAt(i); + return a < 0xd800 || a > 0xdbff || i + 1 === l || (b = s.charCodeAt(i + 1)) < 0xdc00 || b > 0xdfff + ? TO_STRING ? s.charAt(i) : a + : TO_STRING ? s.slice(i, i + 2) : (a - 0xd800 << 10) + (b - 0xdc00) + 0x10000; + }; +}; -function unmonkeypatch () { - fs.realpath = origRealpath - fs.realpathSync = origRealpathSync -} + +/***/ }), +/* 200 */ +/***/ (function(module, exports, __webpack_require__) { + +var toInteger = __webpack_require__(73); +var max = Math.max; +var min = Math.min; +module.exports = function (index, length) { + index = toInteger(index); + return index < 0 ? max(index + length, 0) : min(index, length); +}; /***/ }), -/* 115 */ +/* 201 */ /***/ (function(module, exports, __webpack_require__) { -exports.alphasort = alphasort -exports.alphasorti = alphasorti -exports.setopts = setopts -exports.ownProp = ownProp -exports.makeAbs = makeAbs -exports.finish = finish -exports.mark = mark -exports.isIgnored = isIgnored -exports.childrenIgnored = childrenIgnored +// 7.1.1 ToPrimitive(input [, PreferredType]) +var isObject = __webpack_require__(34); +// instead of the ES6 spec version, we didn't implement @@toPrimitive case +// and the second argument - flag - preferred type is a string +module.exports = function (it, S) { + if (!isObject(it)) return it; + var fn, val; + if (S && typeof (fn = it.toString) == 'function' && !isObject(val = fn.call(it))) return val; + if (typeof (fn = it.valueOf) == 'function' && !isObject(val = fn.call(it))) return val; + if (!S && typeof (fn = it.toString) == 'function' && !isObject(val = fn.call(it))) return val; + throw TypeError("Can't convert object to primitive value"); +}; -function ownProp (obj, field) { - return Object.prototype.hasOwnProperty.call(obj, field) -} -var path = __webpack_require__(0) -var minimatch = __webpack_require__(60) -var isAbsolute = __webpack_require__(76) -var Minimatch = minimatch.Minimatch +/***/ }), +/* 202 */ +/***/ (function(module, exports, __webpack_require__) { -function alphasorti (a, b) { - return a.toLowerCase().localeCompare(b.toLowerCase()) -} +var global = __webpack_require__(11); +var navigator = global.navigator; -function alphasort (a, b) { - return a.localeCompare(b) -} +module.exports = navigator && navigator.userAgent || ''; -function setupIgnores (self, options) { - self.ignore = options.ignore || [] - if (!Array.isArray(self.ignore)) - self.ignore = [self.ignore] +/***/ }), +/* 203 */ +/***/ (function(module, exports, __webpack_require__) { - if (self.ignore.length) { - self.ignore = self.ignore.map(ignoreMap) - } -} +var classof = __webpack_require__(100); +var ITERATOR = __webpack_require__(13)('iterator'); +var Iterators = __webpack_require__(35); +module.exports = __webpack_require__(23).getIteratorMethod = function (it) { + if (it != undefined) return it[ITERATOR] + || it['@@iterator'] + || Iterators[classof(it)]; +}; -// ignore patterns are always in dot:true mode. -function ignoreMap (pattern) { - var gmatcher = null - if (pattern.slice(-3) === '/**') { - var gpattern = pattern.replace(/(\/\*\*)+$/, '') - gmatcher = new Minimatch(gpattern, { dot: true }) - } - return { - matcher: new Minimatch(pattern, { dot: true }), - gmatcher: gmatcher - } -} +/***/ }), +/* 204 */ +/***/ (function(module, exports, __webpack_require__) { -function setopts (self, pattern, options) { - if (!options) - options = {} +"use strict"; - // base-matching: just use globstar for that. - if (options.matchBase && -1 === pattern.indexOf("/")) { - if (options.noglobstar) { - throw new Error("base matching requires globstar") - } - pattern = "**/" + pattern +var addToUnscopables = __webpack_require__(180); +var step = __webpack_require__(190); +var Iterators = __webpack_require__(35); +var toIObject = __webpack_require__(74); + +// 22.1.3.4 Array.prototype.entries() +// 22.1.3.13 Array.prototype.keys() +// 22.1.3.29 Array.prototype.values() +// 22.1.3.30 Array.prototype[@@iterator]() +module.exports = __webpack_require__(103)(Array, 'Array', function (iterated, kind) { + this._t = toIObject(iterated); // target + this._i = 0; // next index + this._k = kind; // kind +// 22.1.5.2.1 %ArrayIteratorPrototype%.next() +}, function () { + var O = this._t; + var kind = this._k; + var index = this._i++; + if (!O || index >= O.length) { + this._t = undefined; + return step(1); } + if (kind == 'keys') return step(0, index); + if (kind == 'values') return step(0, O[index]); + return step(0, [index, O[index]]); +}, 'values'); - self.silent = !!options.silent - self.pattern = pattern - self.strict = options.strict !== false - self.realpath = !!options.realpath - self.realpathCache = options.realpathCache || Object.create(null) - self.follow = !!options.follow - self.dot = !!options.dot - self.mark = !!options.mark - self.nodir = !!options.nodir - if (self.nodir) - self.mark = true - self.sync = !!options.sync - self.nounique = !!options.nounique - self.nonull = !!options.nonull - self.nosort = !!options.nosort - self.nocase = !!options.nocase - self.stat = !!options.stat - self.noprocess = !!options.noprocess - self.absolute = !!options.absolute +// argumentsList[@@iterator] is %ArrayProto_values% (9.4.4.6, 9.4.4.7) +Iterators.Arguments = Iterators.Array; - self.maxLength = options.maxLength || Infinity - self.cache = options.cache || Object.create(null) - self.statCache = options.statCache || Object.create(null) - self.symlinks = options.symlinks || Object.create(null) +addToUnscopables('keys'); +addToUnscopables('values'); +addToUnscopables('entries'); - setupIgnores(self, options) - self.changedCwd = false - var cwd = process.cwd() - if (!ownProp(options, "cwd")) - self.cwd = cwd - else { - self.cwd = path.resolve(options.cwd) - self.changedCwd = self.cwd !== cwd - } +/***/ }), +/* 205 */ +/***/ (function(module, exports) { - self.root = options.root || path.resolve(self.cwd, "/") - self.root = path.resolve(self.root) - if (process.platform === "win32") - self.root = self.root.replace(/\\/g, "/") - // TODO: is an absolute `cwd` supposed to be resolved against `root`? - // e.g. { cwd: '/test', root: __dirname } === path.join(__dirname, '/test') - self.cwdAbs = isAbsolute(self.cwd) ? self.cwd : makeAbs(self, self.cwd) - if (process.platform === "win32") - self.cwdAbs = self.cwdAbs.replace(/\\/g, "/") - self.nomount = !!options.nomount - // disable comments and negation in Minimatch. - // Note that they are not supported in Glob itself anyway. - options.nonegate = true - options.nocomment = true +/***/ }), +/* 206 */ +/***/ (function(module, exports, __webpack_require__) { - self.minimatch = new Minimatch(pattern, options) - self.options = self.minimatch.options +"use strict"; + +var LIBRARY = __webpack_require__(69); +var global = __webpack_require__(11); +var ctx = __webpack_require__(48); +var classof = __webpack_require__(100); +var $export = __webpack_require__(41); +var isObject = __webpack_require__(34); +var aFunction = __webpack_require__(46); +var anInstance = __webpack_require__(181); +var forOf = __webpack_require__(183); +var speciesConstructor = __webpack_require__(108); +var task = __webpack_require__(109).set; +var microtask = __webpack_require__(191)(); +var newPromiseCapabilityModule = __webpack_require__(70); +var perform = __webpack_require__(104); +var userAgent = __webpack_require__(202); +var promiseResolve = __webpack_require__(105); +var PROMISE = 'Promise'; +var TypeError = global.TypeError; +var process = global.process; +var versions = process && process.versions; +var v8 = versions && versions.v8 || ''; +var $Promise = global[PROMISE]; +var isNode = classof(process) == 'process'; +var empty = function () { /* empty */ }; +var Internal, newGenericPromiseCapability, OwnPromiseCapability, Wrapper; +var newPromiseCapability = newGenericPromiseCapability = newPromiseCapabilityModule.f; + +var USE_NATIVE = !!function () { + try { + // correct subclassing with @@species support + var promise = $Promise.resolve(1); + var FakePromise = (promise.constructor = {})[__webpack_require__(13)('species')] = function (exec) { + exec(empty, empty); + }; + // unhandled rejections tracking support, NodeJS Promise without it fails @@species test + return (isNode || typeof PromiseRejectionEvent == 'function') + && promise.then(empty) instanceof FakePromise + // v8 6.6 (Node 10 and Chrome 66) have a bug with resolving custom thenables + // https://bugs.chromium.org/p/chromium/issues/detail?id=830565 + // we can't detect it synchronously, so just check versions + && v8.indexOf('6.6') !== 0 + && userAgent.indexOf('Chrome/66') === -1; + } catch (e) { /* empty */ } +}(); + +// helpers +var isThenable = function (it) { + var then; + return isObject(it) && typeof (then = it.then) == 'function' ? then : false; +}; +var notify = function (promise, isReject) { + if (promise._n) return; + promise._n = true; + var chain = promise._c; + microtask(function () { + var value = promise._v; + var ok = promise._s == 1; + var i = 0; + var run = function (reaction) { + var handler = ok ? reaction.ok : reaction.fail; + var resolve = reaction.resolve; + var reject = reaction.reject; + var domain = reaction.domain; + var result, then, exited; + try { + if (handler) { + if (!ok) { + if (promise._h == 2) onHandleUnhandled(promise); + promise._h = 1; + } + if (handler === true) result = value; + else { + if (domain) domain.enter(); + result = handler(value); // may throw + if (domain) { + domain.exit(); + exited = true; + } + } + if (result === reaction.promise) { + reject(TypeError('Promise-chain cycle')); + } else if (then = isThenable(result)) { + then.call(result, resolve, reject); + } else resolve(result); + } else reject(value); + } catch (e) { + if (domain && !exited) domain.exit(); + reject(e); + } + }; + while (chain.length > i) run(chain[i++]); // variable length - can't use forEach + promise._c = []; + promise._n = false; + if (isReject && !promise._h) onUnhandled(promise); + }); +}; +var onUnhandled = function (promise) { + task.call(global, function () { + var value = promise._v; + var unhandled = isUnhandled(promise); + var result, handler, console; + if (unhandled) { + result = perform(function () { + if (isNode) { + process.emit('unhandledRejection', value, promise); + } else if (handler = global.onunhandledrejection) { + handler({ promise: promise, reason: value }); + } else if ((console = global.console) && console.error) { + console.error('Unhandled promise rejection', value); + } + }); + // Browsers should not trigger `rejectionHandled` event if it was handled here, NodeJS - should + promise._h = isNode || isUnhandled(promise) ? 2 : 1; + } promise._a = undefined; + if (unhandled && result.e) throw result.v; + }); +}; +var isUnhandled = function (promise) { + return promise._h !== 1 && (promise._a || promise._c).length === 0; +}; +var onHandleUnhandled = function (promise) { + task.call(global, function () { + var handler; + if (isNode) { + process.emit('rejectionHandled', promise); + } else if (handler = global.onrejectionhandled) { + handler({ promise: promise, reason: promise._v }); + } + }); +}; +var $reject = function (value) { + var promise = this; + if (promise._d) return; + promise._d = true; + promise = promise._w || promise; // unwrap + promise._v = value; + promise._s = 2; + if (!promise._a) promise._a = promise._c.slice(); + notify(promise, true); +}; +var $resolve = function (value) { + var promise = this; + var then; + if (promise._d) return; + promise._d = true; + promise = promise._w || promise; // unwrap + try { + if (promise === value) throw TypeError("Promise can't be resolved itself"); + if (then = isThenable(value)) { + microtask(function () { + var wrapper = { _w: promise, _d: false }; // wrap + try { + then.call(value, ctx($resolve, wrapper, 1), ctx($reject, wrapper, 1)); + } catch (e) { + $reject.call(wrapper, e); + } + }); + } else { + promise._v = value; + promise._s = 1; + notify(promise, false); + } + } catch (e) { + $reject.call({ _w: promise, _d: false }, e); // wrap + } +}; + +// constructor polyfill +if (!USE_NATIVE) { + // 25.4.3.1 Promise(executor) + $Promise = function Promise(executor) { + anInstance(this, $Promise, PROMISE, '_h'); + aFunction(executor); + Internal.call(this); + try { + executor(ctx($resolve, this, 1), ctx($reject, this, 1)); + } catch (err) { + $reject.call(this, err); + } + }; + // eslint-disable-next-line no-unused-vars + Internal = function Promise(executor) { + this._c = []; // <- awaiting reactions + this._a = undefined; // <- checked in isUnhandled reactions + this._s = 0; // <- state + this._d = false; // <- done + this._v = undefined; // <- value + this._h = 0; // <- rejection state, 0 - default, 1 - handled, 2 - unhandled + this._n = false; // <- notify + }; + Internal.prototype = __webpack_require__(196)($Promise.prototype, { + // 25.4.5.3 Promise.prototype.then(onFulfilled, onRejected) + then: function then(onFulfilled, onRejected) { + var reaction = newPromiseCapability(speciesConstructor(this, $Promise)); + reaction.ok = typeof onFulfilled == 'function' ? onFulfilled : true; + reaction.fail = typeof onRejected == 'function' && onRejected; + reaction.domain = isNode ? process.domain : undefined; + this._c.push(reaction); + if (this._a) this._a.push(reaction); + if (this._s) notify(this, false); + return reaction.promise; + }, + // 25.4.5.1 Promise.prototype.catch(onRejected) + 'catch': function (onRejected) { + return this.then(undefined, onRejected); + } + }); + OwnPromiseCapability = function () { + var promise = new Internal(); + this.promise = promise; + this.resolve = ctx($resolve, promise, 1); + this.reject = ctx($reject, promise, 1); + }; + newPromiseCapabilityModule.f = newPromiseCapability = function (C) { + return C === $Promise || C === Wrapper + ? new OwnPromiseCapability(C) + : newGenericPromiseCapability(C); + }; } -function finish (self) { - var nou = self.nounique - var all = nou ? [] : Object.create(null) +$export($export.G + $export.W + $export.F * !USE_NATIVE, { Promise: $Promise }); +__webpack_require__(71)($Promise, PROMISE); +__webpack_require__(198)(PROMISE); +Wrapper = __webpack_require__(23)[PROMISE]; - for (var i = 0, l = self.matches.length; i < l; i ++) { - var matches = self.matches[i] - if (!matches || Object.keys(matches).length === 0) { - if (self.nonull) { - // do like the shell, and spit out the literal glob - var literal = self.minimatch.globSet[i] - if (nou) - all.push(literal) - else - all[literal] = true - } - } else { - // had matches - var m = Object.keys(matches) - if (nou) - all.push.apply(all, m) - else - m.forEach(function (m) { - all[m] = true - }) - } +// statics +$export($export.S + $export.F * !USE_NATIVE, PROMISE, { + // 25.4.4.5 Promise.reject(r) + reject: function reject(r) { + var capability = newPromiseCapability(this); + var $$reject = capability.reject; + $$reject(r); + return capability.promise; + } +}); +$export($export.S + $export.F * (LIBRARY || !USE_NATIVE), PROMISE, { + // 25.4.4.6 Promise.resolve(x) + resolve: function resolve(x) { + return promiseResolve(LIBRARY && this === Wrapper ? $Promise : this, x); + } +}); +$export($export.S + $export.F * !(USE_NATIVE && __webpack_require__(189)(function (iter) { + $Promise.all(iter)['catch'](empty); +})), PROMISE, { + // 25.4.4.1 Promise.all(iterable) + all: function all(iterable) { + var C = this; + var capability = newPromiseCapability(C); + var resolve = capability.resolve; + var reject = capability.reject; + var result = perform(function () { + var values = []; + var index = 0; + var remaining = 1; + forOf(iterable, false, function (promise) { + var $index = index++; + var alreadyCalled = false; + values.push(undefined); + remaining++; + C.resolve(promise).then(function (value) { + if (alreadyCalled) return; + alreadyCalled = true; + values[$index] = value; + --remaining || resolve(values); + }, reject); + }); + --remaining || resolve(values); + }); + if (result.e) reject(result.v); + return capability.promise; + }, + // 25.4.4.4 Promise.race(iterable) + race: function race(iterable) { + var C = this; + var capability = newPromiseCapability(C); + var reject = capability.reject; + var result = perform(function () { + forOf(iterable, false, function (promise) { + C.resolve(promise).then(capability.resolve, reject); + }); + }); + if (result.e) reject(result.v); + return capability.promise; } +}); - if (!nou) - all = Object.keys(all) - if (!self.nosort) - all = all.sort(self.nocase ? alphasorti : alphasort) +/***/ }), +/* 207 */ +/***/ (function(module, exports, __webpack_require__) { - // at *some* point we statted all of these - if (self.mark) { - for (var i = 0; i < all.length; i++) { - all[i] = self._mark(all[i]) - } - if (self.nodir) { - all = all.filter(function (e) { - var notDir = !(/\/$/.test(e)) - var c = self.cache[e] || self.cache[makeAbs(self, e)] - if (notDir && c) - notDir = c !== 'DIR' && !Array.isArray(c) - return notDir - }) - } - } +"use strict"; - if (self.ignore.length) - all = all.filter(function(m) { - return !isIgnored(self, m) - }) +var $at = __webpack_require__(199)(true); - self.found = all -} +// 21.1.3.27 String.prototype[@@iterator]() +__webpack_require__(103)(String, 'String', function (iterated) { + this._t = String(iterated); // target + this._i = 0; // next index +// 21.1.5.2.1 %StringIteratorPrototype%.next() +}, function () { + var O = this._t; + var index = this._i; + var point; + if (index >= O.length) return { value: undefined, done: true }; + point = $at(O, index); + this._i += point.length; + return { value: point, done: false }; +}); -function mark (self, p) { - var abs = makeAbs(self, p) - var c = self.cache[abs] - var m = p - if (c) { - var isDir = c === 'DIR' || Array.isArray(c) - var slash = p.slice(-1) === '/' - if (isDir && !slash) - m += '/' - else if (!isDir && slash) - m = m.slice(0, -1) +/***/ }), +/* 208 */ +/***/ (function(module, exports, __webpack_require__) { - if (m !== p) { - var mabs = makeAbs(self, m) - self.statCache[mabs] = self.statCache[abs] - self.cache[mabs] = self.cache[abs] - } - } +"use strict"; +// https://github.com/tc39/proposal-promise-finally - return m -} +var $export = __webpack_require__(41); +var core = __webpack_require__(23); +var global = __webpack_require__(11); +var speciesConstructor = __webpack_require__(108); +var promiseResolve = __webpack_require__(105); -// lotta situps... -function makeAbs (self, f) { - var abs = f - if (f.charAt(0) === '/') { - abs = path.join(self.root, f) - } else if (isAbsolute(f) || f === '') { - abs = f - } else if (self.changedCwd) { - abs = path.resolve(self.cwd, f) - } else { - abs = path.resolve(f) - } +$export($export.P + $export.R, 'Promise', { 'finally': function (onFinally) { + var C = speciesConstructor(this, core.Promise || global.Promise); + var isFunction = typeof onFinally == 'function'; + return this.then( + isFunction ? function (x) { + return promiseResolve(C, onFinally()).then(function () { return x; }); + } : onFinally, + isFunction ? function (e) { + return promiseResolve(C, onFinally()).then(function () { throw e; }); + } : onFinally + ); +} }); - if (process.platform === 'win32') - abs = abs.replace(/\\/g, '/') - return abs -} +/***/ }), +/* 209 */ +/***/ (function(module, exports, __webpack_require__) { +"use strict"; -// Return true, if pattern ends with globstar '**', for the accompanying parent directory. -// Ex:- If node_modules/** is the pattern, add 'node_modules' to ignore list along with it's contents -function isIgnored (self, path) { - if (!self.ignore.length) - return false +// https://github.com/tc39/proposal-promise-try +var $export = __webpack_require__(41); +var newPromiseCapability = __webpack_require__(70); +var perform = __webpack_require__(104); - return self.ignore.some(function(item) { - return item.matcher.match(path) || !!(item.gmatcher && item.gmatcher.match(path)) - }) -} +$export($export.S, 'Promise', { 'try': function (callbackfn) { + var promiseCapability = newPromiseCapability.f(this); + var result = perform(callbackfn); + (result.e ? promiseCapability.reject : promiseCapability.resolve)(result.v); + return promiseCapability.promise; +} }); -function childrenIgnored (self, path) { - if (!self.ignore.length) - return false - return self.ignore.some(function(item) { - return !!(item.gmatcher && item.gmatcher.match(path)) - }) +/***/ }), +/* 210 */ +/***/ (function(module, exports, __webpack_require__) { + +__webpack_require__(204); +var global = __webpack_require__(11); +var hide = __webpack_require__(31); +var Iterators = __webpack_require__(35); +var TO_STRING_TAG = __webpack_require__(13)('toStringTag'); + +var DOMIterables = ('CSSRuleList,CSSStyleDeclaration,CSSValueList,ClientRectList,DOMRectList,DOMStringList,' + + 'DOMTokenList,DataTransferItemList,FileList,HTMLAllCollection,HTMLCollection,HTMLFormElement,HTMLSelectElement,' + + 'MediaList,MimeTypeArray,NamedNodeMap,NodeList,PaintRequestList,Plugin,PluginArray,SVGLengthList,SVGNumberList,' + + 'SVGPathSegList,SVGPointList,SVGStringList,SVGTransformList,SourceBufferList,StyleSheetList,TextTrackCueList,' + + 'TextTrackList,TouchList').split(','); + +for (var i = 0; i < DOMIterables.length; i++) { + var NAME = DOMIterables[i]; + var Collection = global[NAME]; + var proto = Collection && Collection.prototype; + if (proto && !proto[TO_STRING_TAG]) hide(proto, TO_STRING_TAG, NAME); + Iterators[NAME] = Iterators.Array; } /***/ }), -/* 116 */ +/* 211 */ /***/ (function(module, exports, __webpack_require__) { -var path = __webpack_require__(0); -var fs = __webpack_require__(3); -var _0777 = parseInt('0777', 8); +/** + * This is the web browser implementation of `debug()`. + * + * Expose `debug()` as the module. + */ -module.exports = mkdirP.mkdirp = mkdirP.mkdirP = mkdirP; +exports = module.exports = __webpack_require__(112); +exports.log = log; +exports.formatArgs = formatArgs; +exports.save = save; +exports.load = load; +exports.useColors = useColors; +exports.storage = 'undefined' != typeof chrome + && 'undefined' != typeof chrome.storage + ? chrome.storage.local + : localstorage(); -function mkdirP (p, opts, f, made) { - if (typeof opts === 'function') { - f = opts; - opts = {}; - } - else if (!opts || typeof opts !== 'object') { - opts = { mode: opts }; - } - - var mode = opts.mode; - var xfs = opts.fs || fs; - - if (mode === undefined) { - mode = _0777 & (~process.umask()); - } - if (!made) made = null; - - var cb = f || function () {}; - p = path.resolve(p); - - xfs.mkdir(p, mode, function (er) { - if (!er) { - made = made || p; - return cb(null, made); - } - switch (er.code) { - case 'ENOENT': - mkdirP(path.dirname(p), opts, function (er, made) { - if (er) cb(er, made); - else mkdirP(p, opts, cb, made); - }); - break; +/** + * Colors. + */ - // In the case of any other error, just see if there's a dir - // there already. If so, then hooray! If not, then something - // is borked. - default: - xfs.stat(p, function (er2, stat) { - // if the stat fails, then that's super weird. - // let the original error be the failure reason. - if (er2 || !stat.isDirectory()) cb(er, made) - else cb(null, made); - }); - break; - } - }); -} +exports.colors = [ + '#0000CC', '#0000FF', '#0033CC', '#0033FF', '#0066CC', '#0066FF', '#0099CC', + '#0099FF', '#00CC00', '#00CC33', '#00CC66', '#00CC99', '#00CCCC', '#00CCFF', + '#3300CC', '#3300FF', '#3333CC', '#3333FF', '#3366CC', '#3366FF', '#3399CC', + '#3399FF', '#33CC00', '#33CC33', '#33CC66', '#33CC99', '#33CCCC', '#33CCFF', + '#6600CC', '#6600FF', '#6633CC', '#6633FF', '#66CC00', '#66CC33', '#9900CC', + '#9900FF', '#9933CC', '#9933FF', '#99CC00', '#99CC33', '#CC0000', '#CC0033', + '#CC0066', '#CC0099', '#CC00CC', '#CC00FF', '#CC3300', '#CC3333', '#CC3366', + '#CC3399', '#CC33CC', '#CC33FF', '#CC6600', '#CC6633', '#CC9900', '#CC9933', + '#CCCC00', '#CCCC33', '#FF0000', '#FF0033', '#FF0066', '#FF0099', '#FF00CC', + '#FF00FF', '#FF3300', '#FF3333', '#FF3366', '#FF3399', '#FF33CC', '#FF33FF', + '#FF6600', '#FF6633', '#FF9900', '#FF9933', '#FFCC00', '#FFCC33' +]; -mkdirP.sync = function sync (p, opts, made) { - if (!opts || typeof opts !== 'object') { - opts = { mode: opts }; - } - - var mode = opts.mode; - var xfs = opts.fs || fs; - - if (mode === undefined) { - mode = _0777 & (~process.umask()); - } - if (!made) made = null; +/** + * Currently only WebKit-based Web Inspectors, Firefox >= v31, + * and the Firebug extension (any Firefox version) are known + * to support "%c" CSS customizations. + * + * TODO: add a `localStorage` variable to explicitly enable/disable colors + */ - p = path.resolve(p); +function useColors() { + // NB: In an Electron preload script, document will be defined but not fully + // initialized. Since we know we're in Chrome, we'll just detect this case + // explicitly + if (typeof window !== 'undefined' && window.process && window.process.type === 'renderer') { + return true; + } - try { - xfs.mkdirSync(p, mode); - made = made || p; - } - catch (err0) { - switch (err0.code) { - case 'ENOENT' : - made = sync(path.dirname(p), opts, made); - sync(p, opts, made); - break; + // Internet Explorer and Edge do not support colors. + if (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) { + return false; + } - // In the case of any other error, just see if there's a dir - // there already. If so, then hooray! If not, then something - // is borked. - default: - var stat; - try { - stat = xfs.statSync(p); - } - catch (err1) { - throw err0; - } - if (!stat.isDirectory()) throw err0; - break; - } - } + // is webkit? http://stackoverflow.com/a/16459606/376773 + // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632 + return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) || + // is firebug? http://stackoverflow.com/a/398120/376773 + (typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) || + // is firefox >= v31? + // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages + (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) || + // double check webkit in userAgent just in case we are in a worker + (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)); +} - return made; -}; +/** + * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. + */ +exports.formatters.j = function(v) { + try { + return JSON.stringify(v); + } catch (err) { + return '[UnexpectedJSONParseError]: ' + err.message; + } +}; -/***/ }), -/* 117 */, -/* 118 */, -/* 119 */, -/* 120 */, -/* 121 */, -/* 122 */ -/***/ (function(module, exports, __webpack_require__) { -"use strict"; +/** + * Colorize log arguments if enabled. + * + * @api public + */ -module.exports = x => { - if (typeof x !== 'string') { - throw new TypeError('Expected a string, got ' + typeof x); - } +function formatArgs(args) { + var useColors = this.useColors; - // Catches EFBBBF (UTF-8 BOM) because the buffer-to-string - // conversion translates it to FEFF (UTF-16 BOM) - if (x.charCodeAt(0) === 0xFEFF) { - return x.slice(1); - } + args[0] = (useColors ? '%c' : '') + + this.namespace + + (useColors ? ' %c' : ' ') + + args[0] + + (useColors ? '%c ' : ' ') + + '+' + exports.humanize(this.diff); - return x; -}; + if (!useColors) return; + var c = 'color: ' + this.color; + args.splice(1, 0, c, 'color: inherit') -/***/ }), -/* 123 */ -/***/ (function(module, exports) { + // the final "%c" is somewhat tricky, because there could be other + // arguments passed either before or after the %c, so we need to + // figure out the correct index to insert the CSS into + var index = 0; + var lastC = 0; + args[0].replace(/%[a-zA-Z%]/g, function(match) { + if ('%%' === match) return; + index++; + if ('%c' === match) { + // we only are interested in the *last* %c + // (the user may have provided their own) + lastC = index; + } + }); -// Returns a wrapper function that returns a wrapped callback -// The wrapper function should do some stuff, and return a -// presumably different callback function. -// This makes sure that own properties are retained, so that -// decorations and such are not lost along the way. -module.exports = wrappy -function wrappy (fn, cb) { - if (fn && cb) return wrappy(fn)(cb) + args.splice(lastC, 0, c); +} - if (typeof fn !== 'function') - throw new TypeError('need wrapper function') +/** + * Invokes `console.log()` when available. + * No-op when `console.log` is not a "function". + * + * @api public + */ - Object.keys(fn).forEach(function (k) { - wrapper[k] = fn[k] - }) +function log() { + // this hackery is required for IE8/9, where + // the `console.log` function doesn't have 'apply' + return 'object' === typeof console + && console.log + && Function.prototype.apply.call(console.log, console, arguments); +} - return wrapper +/** + * Save `namespaces`. + * + * @param {String} namespaces + * @api private + */ - function wrapper() { - var args = new Array(arguments.length) - for (var i = 0; i < args.length; i++) { - args[i] = arguments[i] - } - var ret = fn.apply(this, args) - var cb = args[args.length-1] - if (typeof ret === 'function' && ret !== cb) { - Object.keys(cb).forEach(function (k) { - ret[k] = cb[k] - }) +function save(namespaces) { + try { + if (null == namespaces) { + exports.storage.removeItem('debug'); + } else { + exports.storage.debug = namespaces; } - return ret - } + } catch(e) {} } +/** + * Load `namespaces`. + * + * @return {String} returns the previously persisted debug modes + * @api private + */ -/***/ }), -/* 124 */, -/* 125 */, -/* 126 */, -/* 127 */, -/* 128 */, -/* 129 */, -/* 130 */, -/* 131 */ -/***/ (function(module, exports, __webpack_require__) { +function load() { + var r; + try { + r = exports.storage.debug; + } catch(e) {} -// fallback for non-array-like ES3 and non-enumerable old V8 strings -var cof = __webpack_require__(47); -// eslint-disable-next-line no-prototype-builtins -module.exports = Object('z').propertyIsEnumerable(0) ? Object : function (it) { - return cof(it) == 'String' ? it.split('') : Object(it); -}; + // If debug isn't set in LS, and we're in Electron, try to load $DEBUG + if (!r && typeof process !== 'undefined' && 'env' in process) { + r = process.env.DEBUG; + } + return r; +} -/***/ }), -/* 132 */ -/***/ (function(module, exports, __webpack_require__) { +/** + * Enable namespaces listed in `localStorage.debug` initially. + */ -// 19.1.2.14 / 15.2.3.14 Object.keys(O) -var $keys = __webpack_require__(195); -var enumBugKeys = __webpack_require__(101); +exports.enable(load()); -module.exports = Object.keys || function keys(O) { - return $keys(O, enumBugKeys); -}; +/** + * Localstorage attempts to return the localstorage. + * + * This is necessary because safari throws + * when a user disables cookies/localstorage + * and you attempt to access it. + * + * @return {LocalStorage} + * @api private + */ + +function localstorage() { + try { + return window.localStorage; + } catch (e) {} +} /***/ }), -/* 133 */ +/* 212 */ /***/ (function(module, exports, __webpack_require__) { -// 7.1.13 ToObject(argument) -var defined = __webpack_require__(67); -module.exports = function (it) { - return Object(defined(it)); -}; - +/** + * Detect Electron renderer process, which is node, but we should + * treat as a browser. + */ -/***/ }), -/* 134 */, -/* 135 */, -/* 136 */, -/* 137 */, -/* 138 */, -/* 139 */, -/* 140 */, -/* 141 */, -/* 142 */, -/* 143 */, -/* 144 */, -/* 145 */ -/***/ (function(module, exports) { +if (typeof process === 'undefined' || process.type === 'renderer') { + module.exports = __webpack_require__(211); +} else { + module.exports = __webpack_require__(213); +} -module.exports = {"name":"yarn","installationMethod":"unknown","version":"1.10.0-0","license":"BSD-2-Clause","preferGlobal":true,"description":"📦🐈 Fast, reliable, and secure dependency management.","dependencies":{"@zkochan/cmd-shim":"^2.2.4","babel-runtime":"^6.26.0","bytes":"^3.0.0","camelcase":"^4.0.0","chalk":"^2.1.0","commander":"^2.9.0","death":"^1.0.0","debug":"^3.0.0","deep-equal":"^1.0.1","detect-indent":"^5.0.0","dnscache":"^1.0.1","glob":"^7.1.1","gunzip-maybe":"^1.4.0","hash-for-dep":"^1.2.3","imports-loader":"^0.8.0","ini":"^1.3.4","inquirer":"^3.0.1","invariant":"^2.2.0","is-builtin-module":"^2.0.0","is-ci":"^1.0.10","is-webpack-bundle":"^1.0.0","leven":"^2.0.0","loud-rejection":"^1.2.0","micromatch":"^2.3.11","mkdirp":"^0.5.1","node-emoji":"^1.6.1","normalize-url":"^2.0.0","npm-logical-tree":"^1.2.1","object-path":"^0.11.2","proper-lockfile":"^2.0.0","puka":"^1.0.0","read":"^1.0.7","request":"^2.87.0","request-capture-har":"^1.2.2","rimraf":"^2.5.0","semver":"^5.1.0","ssri":"^5.3.0","strip-ansi":"^4.0.0","strip-bom":"^3.0.0","tar-fs":"^1.16.0","tar-stream":"^1.6.1","uuid":"^3.0.1","v8-compile-cache":"^2.0.0","validate-npm-package-license":"^3.0.3","yn":"^2.0.0"},"devDependencies":{"babel-core":"^6.26.0","babel-eslint":"^7.2.3","babel-loader":"^6.2.5","babel-plugin-array-includes":"^2.0.3","babel-plugin-transform-builtin-extend":"^1.1.2","babel-plugin-transform-inline-imports-commonjs":"^1.0.0","babel-plugin-transform-runtime":"^6.4.3","babel-preset-env":"^1.6.0","babel-preset-flow":"^6.23.0","babel-preset-stage-0":"^6.0.0","babylon":"^6.5.0","commitizen":"^2.9.6","cz-conventional-changelog":"^2.0.0","eslint":"^4.3.0","eslint-config-fb-strict":"^22.0.0","eslint-plugin-babel":"^5.0.0","eslint-plugin-flowtype":"^2.35.0","eslint-plugin-jasmine":"^2.6.2","eslint-plugin-jest":"^21.0.0","eslint-plugin-jsx-a11y":"^6.0.2","eslint-plugin-prefer-object-spread":"^1.2.1","eslint-plugin-prettier":"^2.1.2","eslint-plugin-react":"^7.1.0","eslint-plugin-relay":"^0.0.24","eslint-plugin-yarn-internal":"file:scripts/eslint-rules","execa":"^0.10.0","flow-bin":"^0.66.0","git-release-notes":"^3.0.0","gulp":"^3.9.0","gulp-babel":"^7.0.0","gulp-if":"^2.0.1","gulp-newer":"^1.0.0","gulp-plumber":"^1.0.1","gulp-sourcemaps":"^2.2.0","gulp-util":"^3.0.7","gulp-watch":"^5.0.0","jest":"^22.4.4","jsinspect":"^0.12.6","minimatch":"^3.0.4","mock-stdin":"^0.3.0","prettier":"^1.5.2","temp":"^0.8.3","webpack":"^2.1.0-beta.25","yargs":"^6.3.0"},"resolutions":{"sshpk":"^1.14.2"},"engines":{"node":">=4.0.0"},"repository":"yarnpkg/yarn","bin":{"yarn":"./bin/yarn.js","yarnpkg":"./bin/yarn.js"},"scripts":{"build":"gulp build","build-bundle":"node ./scripts/build-webpack.js","build-chocolatey":"powershell ./scripts/build-chocolatey.ps1","build-deb":"./scripts/build-deb.sh","build-dist":"bash ./scripts/build-dist.sh","build-win-installer":"scripts\\build-windows-installer.bat","changelog":"git-release-notes $(git describe --tags --abbrev=0 $(git describe --tags --abbrev=0)^)..$(git describe --tags --abbrev=0) scripts/changelog.md","dupe-check":"yarn jsinspect ./src","lint":"eslint . && flow check","pkg-tests":"yarn --cwd packages/pkg-tests jest yarn.test.js","prettier":"eslint src __tests__ --fix","release-branch":"./scripts/release-branch.sh","test":"yarn lint && yarn test-only","test-only":"node --max_old_space_size=4096 node_modules/jest/bin/jest.js --verbose","test-only-debug":"node --inspect-brk --max_old_space_size=4096 node_modules/jest/bin/jest.js --runInBand --verbose","test-coverage":"node --max_old_space_size=4096 node_modules/jest/bin/jest.js --coverage --verbose","watch":"gulp watch","commit":"git-cz"},"jest":{"collectCoverageFrom":["src/**/*.js"],"testEnvironment":"node","modulePathIgnorePatterns":["__tests__/fixtures/","packages/pkg-tests/pkg-tests-fixtures","dist/"],"testPathIgnorePatterns":["__tests__/(fixtures|__mocks__)/","updates/","_(temp|mock|install|init|helpers).js$","packages/pkg-tests"]},"config":{"commitizen":{"path":"./node_modules/cz-conventional-changelog"}}} /***/ }), -/* 146 */, -/* 147 */, -/* 148 */, -/* 149 */, -/* 150 */ +/* 213 */ /***/ (function(module, exports, __webpack_require__) { -"use strict"; +/** + * Module dependencies. + */ +var tty = __webpack_require__(79); +var util = __webpack_require__(2); -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.default = stringify; +/** + * This is the Node.js implementation of `debug()`. + * + * Expose `debug()` as the module. + */ -var _misc; +exports = module.exports = __webpack_require__(112); +exports.init = init; +exports.log = log; +exports.formatArgs = formatArgs; +exports.save = save; +exports.load = load; +exports.useColors = useColors; -function _load_misc() { - return _misc = __webpack_require__(12); -} +/** + * Colors. + */ -var _constants; +exports.colors = [ 6, 2, 3, 4, 5, 1 ]; -function _load_constants() { - return _constants = __webpack_require__(6); +try { + var supportsColor = __webpack_require__(239); + if (supportsColor && supportsColor.level >= 2) { + exports.colors = [ + 20, 21, 26, 27, 32, 33, 38, 39, 40, 41, 42, 43, 44, 45, 56, 57, 62, 63, 68, + 69, 74, 75, 76, 77, 78, 79, 80, 81, 92, 93, 98, 99, 112, 113, 128, 129, 134, + 135, 148, 149, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, + 172, 173, 178, 179, 184, 185, 196, 197, 198, 199, 200, 201, 202, 203, 204, + 205, 206, 207, 208, 209, 214, 215, 220, 221 + ]; + } +} catch (err) { + // swallow - we only care if `supports-color` is available; it doesn't have to be. } -var _package; - -function _load_package() { - return _package = __webpack_require__(145); -} +/** + * Build up the default `inspectOpts` object from the environment variables. + * + * $ DEBUG_COLORS=no DEBUG_DEPTH=10 DEBUG_SHOW_HIDDEN=enabled node script.js + */ -const NODE_VERSION = process.version; +exports.inspectOpts = Object.keys(process.env).filter(function (key) { + return /^debug_/i.test(key); +}).reduce(function (obj, key) { + // camel-case + var prop = key + .substring(6) + .toLowerCase() + .replace(/_([a-z])/g, function (_, k) { return k.toUpperCase() }); -function shouldWrapKey(str) { - return str.indexOf('true') === 0 || str.indexOf('false') === 0 || /[:\s\n\\",\[\]]/g.test(str) || /^[0-9]/g.test(str) || !/^[a-zA-Z]/g.test(str); -} + // coerce string value into JS value + var val = process.env[key]; + if (/^(yes|on|true|enabled)$/i.test(val)) val = true; + else if (/^(no|off|false|disabled)$/i.test(val)) val = false; + else if (val === 'null') val = null; + else val = Number(val); -function maybeWrap(str) { - if (typeof str === 'boolean' || typeof str === 'number' || shouldWrapKey(str)) { - return JSON.stringify(str); - } else { - return str; - } -} + obj[prop] = val; + return obj; +}, {}); -const priorities = { - name: 1, - version: 2, - uid: 3, - resolved: 4, - integrity: 5, - registry: 6, - dependencies: 7 -}; +/** + * Is stdout a TTY? Colored output is enabled when `true`. + */ -function priorityThenAlphaSort(a, b) { - if (priorities[a] || priorities[b]) { - return (priorities[a] || 100) > (priorities[b] || 100) ? 1 : -1; - } else { - return (0, (_misc || _load_misc()).sortAlpha)(a, b); - } +function useColors() { + return 'colors' in exports.inspectOpts + ? Boolean(exports.inspectOpts.colors) + : tty.isatty(process.stderr.fd); } -function _stringify(obj, options) { - if (typeof obj !== 'object') { - throw new TypeError(); - } - - const indent = options.indent; - const lines = []; - - // Sorting order needs to be consistent between runs, we run native sort by name because there are no - // problems with it being unstable because there are no to keys the same - // However priorities can be duplicated and native sort can shuffle things from run to run - const keys = Object.keys(obj).sort(priorityThenAlphaSort); +/** + * Map %o to `util.inspect()`, all on a single line. + */ - let addedKeys = []; +exports.formatters.o = function(v) { + this.inspectOpts.colors = this.useColors; + return util.inspect(v, this.inspectOpts) + .split('\n').map(function(str) { + return str.trim() + }).join(' '); +}; - for (let i = 0; i < keys.length; i++) { - const key = keys[i]; - const val = obj[key]; - if (val == null || addedKeys.indexOf(key) >= 0) { - continue; - } +/** + * Map %o to `util.inspect()`, allowing multiple lines if needed. + */ - const valKeys = [key]; +exports.formatters.O = function(v) { + this.inspectOpts.colors = this.useColors; + return util.inspect(v, this.inspectOpts); +}; - // get all keys that have the same value equality, we only want this for objects - if (typeof val === 'object') { - for (let j = i + 1; j < keys.length; j++) { - const key = keys[j]; - if (val === obj[key]) { - valKeys.push(key); - } - } - } +/** + * Adds ANSI color escape codes if enabled. + * + * @api public + */ - const keyLine = valKeys.sort((_misc || _load_misc()).sortAlpha).map(maybeWrap).join(', '); +function formatArgs(args) { + var name = this.namespace; + var useColors = this.useColors; - if (typeof val === 'string' || typeof val === 'boolean' || typeof val === 'number') { - lines.push(`${keyLine} ${maybeWrap(val)}`); - } else if (typeof val === 'object') { - lines.push(`${keyLine}:\n${_stringify(val, { indent: indent + ' ' })}` + (options.topLevel ? '\n' : '')); - } else { - throw new TypeError(); - } + if (useColors) { + var c = this.color; + var colorCode = '\u001b[3' + (c < 8 ? c : '8;5;' + c); + var prefix = ' ' + colorCode + ';1m' + name + ' ' + '\u001b[0m'; - addedKeys = addedKeys.concat(valKeys); + args[0] = prefix + args[0].split('\n').join('\n' + prefix); + args.push(colorCode + 'm+' + exports.humanize(this.diff) + '\u001b[0m'); + } else { + args[0] = getDate() + name + ' ' + args[0]; } - - return indent + lines.join(`\n${indent}`); } -function stringify(obj, noHeader, enableVersions) { - const val = _stringify(obj, { - indent: '', - topLevel: true - }); - if (noHeader) { - return val; - } - - const lines = []; - lines.push('# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.'); - lines.push(`# yarn lockfile v${(_constants || _load_constants()).LOCKFILE_VERSION}`); - if (enableVersions) { - lines.push(`# yarn v${(_package || _load_package()).version}`); - lines.push(`# node ${NODE_VERSION}`); +function getDate() { + if (exports.inspectOpts.hideDate) { + return ''; + } else { + return new Date().toISOString() + ' '; } - lines.push('\n'); - lines.push(val); - - return lines.join('\n'); } -/***/ }), -/* 151 */, -/* 152 */, -/* 153 */, -/* 154 */, -/* 155 */, -/* 156 */, -/* 157 */, -/* 158 */, -/* 159 */, -/* 160 */, -/* 161 */, -/* 162 */, -/* 163 */, -/* 164 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.fileDatesEqual = exports.copyFile = exports.unlink = undefined; - -var _asyncToGenerator2; +/** + * Invokes `util.format()` with the specified arguments and writes to stderr. + */ -function _load_asyncToGenerator() { - return _asyncToGenerator2 = _interopRequireDefault(__webpack_require__(1)); +function log() { + return process.stderr.write(util.format.apply(util, arguments) + '\n'); } -// We want to preserve file timestamps when copying a file, since yarn uses them to decide if a file has -// changed compared to the cache. -// There are some OS specific cases here: -// * On linux, fs.copyFile does not preserve timestamps, but does on OSX and Win. -// * On windows, you must open a file with write permissions to call `fs.futimes`. -// * On OSX you can open with read permissions and still call `fs.futimes`. -let fixTimes = (() => { - var _ref3 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (fd, dest, data) { - const doOpen = fd === undefined; - let openfd = fd ? fd : -1; - - if (disableTimestampCorrection === undefined) { - // if timestamps match already, no correction is needed. - // the need to correct timestamps varies based on OS and node versions. - const destStat = yield lstat(dest); - disableTimestampCorrection = fileDatesEqual(destStat.mtime, data.mtime); - } - - if (disableTimestampCorrection) { - return; - } - - if (doOpen) { - try { - openfd = yield open(dest, 'a', data.mode); - } catch (er) { - // file is likely read-only - try { - openfd = yield open(dest, 'r', data.mode); - } catch (err) { - // We can't even open this file for reading. - return; - } - } - } +/** + * Save `namespaces`. + * + * @param {String} namespaces + * @api private + */ - try { - if (openfd) { - yield futimes(openfd, data.atime, data.mtime); - } - } catch (er) { - // If `futimes` throws an exception, we probably have a case of a read-only file on Windows. - // In this case we can just return. The incorrect timestamp will just cause that file to be recopied - // on subsequent installs, which will effect yarn performance but not break anything. - } finally { - if (doOpen && openfd) { - yield close(openfd); - } - } - }); +function save(namespaces) { + if (null == namespaces) { + // If you set a process.env field to null or undefined, it gets cast to the + // string 'null' or 'undefined'. Just delete instead. + delete process.env.DEBUG; + } else { + process.env.DEBUG = namespaces; + } +} - return function fixTimes(_x7, _x8, _x9) { - return _ref3.apply(this, arguments); - }; -})(); +/** + * Load `namespaces`. + * + * @return {String} returns the previously persisted debug modes + * @api private + */ -// Compare file timestamps. -// Some versions of Node on windows zero the milliseconds when utime is used. +function load() { + return process.env.DEBUG; +} +/** + * Init logic for `debug` instances. + * + * Create a new `inspectOpts` object in case `useColors` is set + * differently for a particular `debug` instance. + */ -var _fs; +function init (debug) { + debug.inspectOpts = {}; -function _load_fs() { - return _fs = _interopRequireDefault(__webpack_require__(3)); + var keys = Object.keys(exports.inspectOpts); + for (var i = 0; i < keys.length; i++) { + debug.inspectOpts[keys[i]] = exports.inspectOpts[keys[i]]; + } } -var _promise; +/** + * Enable namespaces listed in `process.env.DEBUG` initially. + */ -function _load_promise() { - return _promise = __webpack_require__(40); -} +exports.enable(load()); -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } -// This module serves as a wrapper for file operations that are inconsistant across node and OS versions. +/***/ }), +/* 214 */, +/* 215 */, +/* 216 */, +/* 217 */ +/***/ (function(module, exports, __webpack_require__) { -let disableTimestampCorrection = undefined; // OS dependent. will be detected on first file copy. +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. -const readFileBuffer = (0, (_promise || _load_promise()).promisify)((_fs || _load_fs()).default.readFile); -const close = (0, (_promise || _load_promise()).promisify)((_fs || _load_fs()).default.close); -const lstat = (0, (_promise || _load_promise()).promisify)((_fs || _load_fs()).default.lstat); -const open = (0, (_promise || _load_promise()).promisify)((_fs || _load_fs()).default.open); -const futimes = (0, (_promise || _load_promise()).promisify)((_fs || _load_fs()).default.futimes); +var pathModule = __webpack_require__(0); +var isWindows = process.platform === 'win32'; +var fs = __webpack_require__(3); -const write = (0, (_promise || _load_promise()).promisify)((_fs || _load_fs()).default.write); +// JavaScript implementation of realpath, ported from node pre-v6 -const unlink = exports.unlink = (0, (_promise || _load_promise()).promisify)(__webpack_require__(233)); +var DEBUG = process.env.NODE_DEBUG && /fs/.test(process.env.NODE_DEBUG); -/** - * Unlinks the destination to force a recreation. This is needed on case-insensitive file systems - * to force the correct naming when the filename has changed only in character-casing. (Jest -> jest). - */ -const copyFile = exports.copyFile = (() => { - var _ref = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (data, cleanup) { - try { - yield unlink(data.dest); - yield copyFilePoly(data.src, data.dest, 0, data); - } finally { - if (cleanup) { - cleanup(); - } - } - }); +function rethrow() { + // Only enable in debug mode. A backtrace uses ~1000 bytes of heap space and + // is fairly slow to generate. + var callback; + if (DEBUG) { + var backtrace = new Error; + callback = debugCallback; + } else + callback = missingCallback; + + return callback; - return function copyFile(_x, _x2) { - return _ref.apply(this, arguments); - }; -})(); + function debugCallback(err) { + if (err) { + backtrace.message = err.message; + err = backtrace; + missingCallback(err); + } + } -// Node 8.5.0 introduced `fs.copyFile` which is much faster, so use that when available. -// Otherwise we fall back to reading and writing files as buffers. -const copyFilePoly = (src, dest, flags, data) => { - if ((_fs || _load_fs()).default.copyFile) { - return new Promise((resolve, reject) => (_fs || _load_fs()).default.copyFile(src, dest, flags, err => { - if (err) { - reject(err); - } else { - fixTimes(undefined, dest, data).then(() => resolve()).catch(ex => reject(ex)); + function missingCallback(err) { + if (err) { + if (process.throwDeprecation) + throw err; // Forgot a callback but don't know where? Use NODE_DEBUG=fs + else if (!process.noDeprecation) { + var msg = 'fs: missing callback ' + (err.stack || err.message); + if (process.traceDeprecation) + console.trace(msg); + else + console.error(msg); } - })); - } else { - return copyWithBuffer(src, dest, flags, data); + } } -}; +} -const copyWithBuffer = (() => { - var _ref2 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (src, dest, flags, data) { - // Use open -> write -> futimes -> close sequence to avoid opening the file twice: - // one with writeFile and one with utimes - const fd = yield open(dest, 'w', data.mode); - try { - const buffer = yield readFileBuffer(src); - yield write(fd, buffer, 0, buffer.length); - yield fixTimes(fd, dest, data); - } finally { - yield close(fd); - } - }); +function maybeCallback(cb) { + return typeof cb === 'function' ? cb : rethrow(); +} - return function copyWithBuffer(_x3, _x4, _x5, _x6) { - return _ref2.apply(this, arguments); - }; -})();const fileDatesEqual = exports.fileDatesEqual = (a, b) => { - const aTime = a.getTime(); - const bTime = b.getTime(); +var normalize = pathModule.normalize; - if (process.platform !== 'win32') { - return aTime === bTime; - } +// Regexp that finds the next partion of a (partial) path +// result is [base_with_slash, base], e.g. ['somedir/', 'somedir'] +if (isWindows) { + var nextPartRe = /(.*?)(?:[\/\\]+|$)/g; +} else { + var nextPartRe = /(.*?)(?:[\/]+|$)/g; +} - // See https://github.com/nodejs/node/pull/12607 - // Submillisecond times from stat and utimes are truncated on Windows, - // causing a file with mtime 8.0079998 and 8.0081144 to become 8.007 and 8.008 - // and making it impossible to update these files to their correct timestamps. - if (Math.abs(aTime - bTime) <= 1) { - return true; - } +// Regex to find the device root, including trailing slash. E.g. 'c:\\'. +if (isWindows) { + var splitRootRe = /^(?:[a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/][^\\\/]+)?[\\\/]*/; +} else { + var splitRootRe = /^[\/]*/; +} - const aTimeSec = Math.floor(aTime / 1000); - const bTimeSec = Math.floor(bTime / 1000); +exports.realpathSync = function realpathSync(p, cache) { + // make p is absolute + p = pathModule.resolve(p); - // See https://github.com/nodejs/node/issues/2069 - // Some versions of Node on windows zero the milliseconds when utime is used - // So if any of the time has a milliseconds part of zero we suspect that the - // bug is present and compare only seconds. - if (aTime - aTimeSec * 1000 === 0 || bTime - bTimeSec * 1000 === 0) { - return aTimeSec === bTimeSec; + if (cache && Object.prototype.hasOwnProperty.call(cache, p)) { + return cache[p]; } - return aTime === bTime; -}; + var original = p, + seenLinks = {}, + knownHard = {}; -/***/ }), -/* 165 */, -/* 166 */, -/* 167 */, -/* 168 */, -/* 169 */ -/***/ (function(module, exports, __webpack_require__) { + // current character position in p + var pos; + // the partial path so far, including a trailing slash if any + var current; + // the partial path without a trailing slash (except when pointing at a root) + var base; + // the partial path scanned in the previous round, with slash + var previous; -"use strict"; + start(); + function start() { + // Skip over roots + var m = splitRootRe.exec(p); + pos = m[0].length; + current = m[0]; + base = m[0]; + previous = ''; -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.isFakeRoot = isFakeRoot; -exports.isRootUser = isRootUser; -function getUid() { - if (process.platform !== 'win32' && process.getuid) { - return process.getuid(); + // On windows, check that the root exists. On unix there is no need. + if (isWindows && !knownHard[base]) { + fs.lstatSync(base); + knownHard[base] = true; + } } - return null; -} -exports.default = isRootUser(getUid()) && !isFakeRoot(); -function isFakeRoot() { - return Boolean(process.env.FAKEROOTKEY); -} + // walk down the path, swapping out linked pathparts for their real + // values + // NB: p.length changes. + while (pos < p.length) { + // find the next part + nextPartRe.lastIndex = pos; + var result = nextPartRe.exec(p); + previous = current; + current += result[0]; + base = previous + result[1]; + pos = nextPartRe.lastIndex; -function isRootUser(uid) { - return uid === 0; -} + // continue if not a symlink + if (knownHard[base] || (cache && cache[base] === base)) { + continue; + } -/***/ }), -/* 170 */, -/* 171 */ -/***/ (function(module, exports, __webpack_require__) { + var resolvedLink; + if (cache && Object.prototype.hasOwnProperty.call(cache, base)) { + // some known symbolic link. no need to stat again. + resolvedLink = cache[base]; + } else { + var stat = fs.lstatSync(base); + if (!stat.isSymbolicLink()) { + knownHard[base] = true; + if (cache) cache[base] = base; + continue; + } -"use strict"; + // read the link if it wasn't read before + // dev/ino always return 0 on windows, so skip the check. + var linkTarget = null; + if (!isWindows) { + var id = stat.dev.toString(32) + ':' + stat.ino.toString(32); + if (seenLinks.hasOwnProperty(id)) { + linkTarget = seenLinks[id]; + } + } + if (linkTarget === null) { + fs.statSync(base); + linkTarget = fs.readlinkSync(base); + } + resolvedLink = pathModule.resolve(previous, linkTarget); + // track this, if given a cache. + if (cache) cache[base] = resolvedLink; + if (!isWindows) seenLinks[id] = linkTarget; + } + // resolve the link, then start over + p = pathModule.resolve(resolvedLink, p.slice(pos)); + start(); + } -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.getDataDir = getDataDir; -exports.getCacheDir = getCacheDir; -exports.getConfigDir = getConfigDir; -const path = __webpack_require__(0); -const userHome = __webpack_require__(45).default; + if (cache) cache[original] = p; -const FALLBACK_CONFIG_DIR = path.join(userHome, '.config', 'yarn'); -const FALLBACK_CACHE_DIR = path.join(userHome, '.cache', 'yarn'); + return p; +}; -function getDataDir() { - if (process.platform === 'win32') { - const WIN32_APPDATA_DIR = getLocalAppDataDir(); - return WIN32_APPDATA_DIR == null ? FALLBACK_CONFIG_DIR : path.join(WIN32_APPDATA_DIR, 'Data'); - } else if (process.env.XDG_DATA_HOME) { - return path.join(process.env.XDG_DATA_HOME, 'yarn'); - } else { - // This could arguably be ~/Library/Application Support/Yarn on Macs, - // but that feels unintuitive for a cli tool - // Instead, use our prior fallback. Some day this could be - // path.join(userHome, '.local', 'share', 'yarn') - // or return path.join(WIN32_APPDATA_DIR, 'Data') on win32 - return FALLBACK_CONFIG_DIR; +exports.realpath = function realpath(p, cache, cb) { + if (typeof cb !== 'function') { + cb = maybeCallback(cache); + cache = null; } -} -function getCacheDir() { - if (process.platform === 'win32') { - // process.env.TEMP also exists, but most apps put caches here - return path.join(getLocalAppDataDir() || path.join(userHome, 'AppData', 'Local', 'Yarn'), 'Cache'); - } else if (process.env.XDG_CACHE_HOME) { - return path.join(process.env.XDG_CACHE_HOME, 'yarn'); - } else if (process.platform === 'darwin') { - return path.join(userHome, 'Library', 'Caches', 'Yarn'); - } else { - return FALLBACK_CACHE_DIR; - } -} + // make p is absolute + p = pathModule.resolve(p); -function getConfigDir() { - if (process.platform === 'win32') { - // Use our prior fallback. Some day this could be - // return path.join(WIN32_APPDATA_DIR, 'Config') - const WIN32_APPDATA_DIR = getLocalAppDataDir(); - return WIN32_APPDATA_DIR == null ? FALLBACK_CONFIG_DIR : path.join(WIN32_APPDATA_DIR, 'Config'); - } else if (process.env.XDG_CONFIG_HOME) { - return path.join(process.env.XDG_CONFIG_HOME, 'yarn'); - } else { - return FALLBACK_CONFIG_DIR; + if (cache && Object.prototype.hasOwnProperty.call(cache, p)) { + return process.nextTick(cb.bind(null, null, cache[p])); } -} -function getLocalAppDataDir() { - return process.env.LOCALAPPDATA ? path.join(process.env.LOCALAPPDATA, 'Yarn') : null; -} + var original = p, + seenLinks = {}, + knownHard = {}; -/***/ }), -/* 172 */, -/* 173 */ -/***/ (function(module, exports, __webpack_require__) { + // current character position in p + var pos; + // the partial path so far, including a trailing slash if any + var current; + // the partial path without a trailing slash (except when pointing at a root) + var base; + // the partial path scanned in the previous round, with slash + var previous; -module.exports = { "default": __webpack_require__(179), __esModule: true }; + start(); -/***/ }), -/* 174 */ -/***/ (function(module, exports, __webpack_require__) { + function start() { + // Skip over roots + var m = splitRootRe.exec(p); + pos = m[0].length; + current = m[0]; + base = m[0]; + previous = ''; -"use strict"; + // On windows, check that the root exists. On unix there is no need. + if (isWindows && !knownHard[base]) { + fs.lstat(base, function(err) { + if (err) return cb(err); + knownHard[base] = true; + LOOP(); + }); + } else { + process.nextTick(LOOP); + } + } -module.exports = balanced; -function balanced(a, b, str) { - if (a instanceof RegExp) a = maybeMatch(a, str); - if (b instanceof RegExp) b = maybeMatch(b, str); + // walk down the path, swapping out linked pathparts for their real + // values + function LOOP() { + // stop if scanned past end of path + if (pos >= p.length) { + if (cache) cache[original] = p; + return cb(null, p); + } - var r = range(a, b, str); + // find the next part + nextPartRe.lastIndex = pos; + var result = nextPartRe.exec(p); + previous = current; + current += result[0]; + base = previous + result[1]; + pos = nextPartRe.lastIndex; - return r && { - start: r[0], - end: r[1], - pre: str.slice(0, r[0]), - body: str.slice(r[0] + a.length, r[1]), - post: str.slice(r[1] + b.length) - }; -} + // continue if not a symlink + if (knownHard[base] || (cache && cache[base] === base)) { + return process.nextTick(LOOP); + } -function maybeMatch(reg, str) { - var m = str.match(reg); - return m ? m[0] : null; -} + if (cache && Object.prototype.hasOwnProperty.call(cache, base)) { + // known symbolic link. no need to stat again. + return gotResolvedLink(cache[base]); + } -balanced.range = range; -function range(a, b, str) { - var begs, beg, left, right, result; - var ai = str.indexOf(a); - var bi = str.indexOf(b, ai + 1); - var i = ai; + return fs.lstat(base, gotStat); + } - if (ai >= 0 && bi > 0) { - begs = []; - left = str.length; + function gotStat(err, stat) { + if (err) return cb(err); - while (i >= 0 && !result) { - if (i == ai) { - begs.push(i); - ai = str.indexOf(a, i + 1); - } else if (begs.length == 1) { - result = [ begs.pop(), bi ]; - } else { - beg = begs.pop(); - if (beg < left) { - left = beg; - right = bi; - } + // if not a symlink, skip to the next path part + if (!stat.isSymbolicLink()) { + knownHard[base] = true; + if (cache) cache[base] = base; + return process.nextTick(LOOP); + } - bi = str.indexOf(b, i + 1); + // stat & read the link if not read before + // call gotTarget as soon as the link target is known + // dev/ino always return 0 on windows, so skip the check. + if (!isWindows) { + var id = stat.dev.toString(32) + ':' + stat.ino.toString(32); + if (seenLinks.hasOwnProperty(id)) { + return gotTarget(null, seenLinks[id], base); } - - i = ai < bi && ai >= 0 ? ai : bi; } + fs.stat(base, function(err) { + if (err) return cb(err); - if (begs.length) { - result = [ left, right ]; - } + fs.readlink(base, function(err, target) { + if (!isWindows) seenLinks[id] = target; + gotTarget(err, target); + }); + }); } - return result; -} + function gotTarget(err, target, base) { + if (err) return cb(err); + + var resolvedLink = pathModule.resolve(previous, target); + if (cache) cache[base] = resolvedLink; + gotResolvedLink(resolvedLink); + } + + function gotResolvedLink(resolvedLink) { + // resolve the link, then start over + p = pathModule.resolve(resolvedLink, p.slice(pos)); + start(); + } +}; /***/ }), -/* 175 */ +/* 218 */ /***/ (function(module, exports, __webpack_require__) { -var concatMap = __webpack_require__(178); -var balanced = __webpack_require__(174); - -module.exports = expandTop; +module.exports = globSync +globSync.GlobSync = GlobSync -var escSlash = '\0SLASH'+Math.random()+'\0'; -var escOpen = '\0OPEN'+Math.random()+'\0'; -var escClose = '\0CLOSE'+Math.random()+'\0'; -var escComma = '\0COMMA'+Math.random()+'\0'; -var escPeriod = '\0PERIOD'+Math.random()+'\0'; +var fs = __webpack_require__(3) +var rp = __webpack_require__(114) +var minimatch = __webpack_require__(60) +var Minimatch = minimatch.Minimatch +var Glob = __webpack_require__(75).Glob +var util = __webpack_require__(2) +var path = __webpack_require__(0) +var assert = __webpack_require__(22) +var isAbsolute = __webpack_require__(76) +var common = __webpack_require__(115) +var alphasort = common.alphasort +var alphasorti = common.alphasorti +var setopts = common.setopts +var ownProp = common.ownProp +var childrenIgnored = common.childrenIgnored +var isIgnored = common.isIgnored -function numeric(str) { - return parseInt(str, 10) == str - ? parseInt(str, 10) - : str.charCodeAt(0); -} +function globSync (pattern, options) { + if (typeof options === 'function' || arguments.length === 3) + throw new TypeError('callback provided to sync glob\n'+ + 'See: https://github.com/isaacs/node-glob/issues/167') -function escapeBraces(str) { - return str.split('\\\\').join(escSlash) - .split('\\{').join(escOpen) - .split('\\}').join(escClose) - .split('\\,').join(escComma) - .split('\\.').join(escPeriod); + return new GlobSync(pattern, options).found } -function unescapeBraces(str) { - return str.split(escSlash).join('\\') - .split(escOpen).join('{') - .split(escClose).join('}') - .split(escComma).join(',') - .split(escPeriod).join('.'); -} +function GlobSync (pattern, options) { + if (!pattern) + throw new Error('must provide pattern') + if (typeof options === 'function' || arguments.length === 3) + throw new TypeError('callback provided to sync glob\n'+ + 'See: https://github.com/isaacs/node-glob/issues/167') -// Basically just str.split(","), but handling cases -// where we have nested braced sections, which should be -// treated as individual members, like {a,{b,c},d} -function parseCommaParts(str) { - if (!str) - return ['']; + if (!(this instanceof GlobSync)) + return new GlobSync(pattern, options) - var parts = []; - var m = balanced('{', '}', str); + setopts(this, pattern, options) - if (!m) - return str.split(','); + if (this.noprocess) + return this - var pre = m.pre; - var body = m.body; - var post = m.post; - var p = pre.split(','); + var n = this.minimatch.set.length + this.matches = new Array(n) + for (var i = 0; i < n; i ++) { + this._process(this.minimatch.set[i], i, false) + } + this._finish() +} - p[p.length-1] += '{' + body + '}'; - var postParts = parseCommaParts(post); - if (post.length) { - p[p.length-1] += postParts.shift(); - p.push.apply(p, postParts); +GlobSync.prototype._finish = function () { + assert(this instanceof GlobSync) + if (this.realpath) { + var self = this + this.matches.forEach(function (matchset, index) { + var set = self.matches[index] = Object.create(null) + for (var p in matchset) { + try { + p = self._makeAbs(p) + var real = rp.realpathSync(p, self.realpathCache) + set[real] = true + } catch (er) { + if (er.syscall === 'stat') + set[self._makeAbs(p)] = true + else + throw er + } + } + }) } + common.finish(this) +} - parts.push.apply(parts, p); - return parts; -} +GlobSync.prototype._process = function (pattern, index, inGlobStar) { + assert(this instanceof GlobSync) -function expandTop(str) { - if (!str) - return []; + // Get the first [n] parts of pattern that are all strings. + var n = 0 + while (typeof pattern[n] === 'string') { + n ++ + } + // now n is the index of the first one that is *not* a string. - // I don't know why Bash 4.3 does this, but it does. - // Anything starting with {} will have the first two bytes preserved - // but *only* at the top level, so {},a}b will not expand to anything, - // but a{},b}c will be expanded to [a}c,abc]. - // One could argue that this is a bug in Bash, but since the goal of - // this module is to match Bash's rules, we escape a leading {} - if (str.substr(0, 2) === '{}') { - str = '\\{\\}' + str.substr(2); + // See if there's anything else + var prefix + switch (n) { + // if not, then this is rather simple + case pattern.length: + this._processSimple(pattern.join('/'), index) + return + + case 0: + // pattern *starts* with some non-trivial item. + // going to readdir(cwd), but not include the prefix in matches. + prefix = null + break + + default: + // pattern has some string bits in the front. + // whatever it starts with, whether that's 'absolute' like /foo/bar, + // or 'relative' like '../baz' + prefix = pattern.slice(0, n).join('/') + break } - return expand(escapeBraces(str), true).map(unescapeBraces); -} + var remain = pattern.slice(n) -function identity(e) { - return e; -} + // get the list of entries. + var read + if (prefix === null) + read = '.' + else if (isAbsolute(prefix) || isAbsolute(pattern.join('/'))) { + if (!prefix || !isAbsolute(prefix)) + prefix = '/' + prefix + read = prefix + } else + read = prefix -function embrace(str) { - return '{' + str + '}'; -} -function isPadded(el) { - return /^-?0\d/.test(el); -} + var abs = this._makeAbs(read) -function lte(i, y) { - return i <= y; -} -function gte(i, y) { - return i >= y; + //if ignored, skip processing + if (childrenIgnored(this, read)) + return + + var isGlobStar = remain[0] === minimatch.GLOBSTAR + if (isGlobStar) + this._processGlobStar(prefix, read, abs, remain, index, inGlobStar) + else + this._processReaddir(prefix, read, abs, remain, index, inGlobStar) } -function expand(str, isTop) { - var expansions = []; - var m = balanced('{', '}', str); - if (!m || /\$$/.test(m.pre)) return [str]; +GlobSync.prototype._processReaddir = function (prefix, read, abs, remain, index, inGlobStar) { + var entries = this._readdir(abs, inGlobStar) - var isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body); - var isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body); - var isSequence = isNumericSequence || isAlphaSequence; - var isOptions = m.body.indexOf(',') >= 0; - if (!isSequence && !isOptions) { - // {a},b} - if (m.post.match(/,.*\}/)) { - str = m.pre + '{' + m.body + escClose + m.post; - return expand(str); - } - return [str]; - } + // if the abs isn't a dir, then nothing can match! + if (!entries) + return - var n; - if (isSequence) { - n = m.body.split(/\.\./); - } else { - n = parseCommaParts(m.body); - if (n.length === 1) { - // x{{a,b}}y ==> x{a}y x{b}y - n = expand(n[0], false).map(embrace); - if (n.length === 1) { - var post = m.post.length - ? expand(m.post, false) - : ['']; - return post.map(function(p) { - return m.pre + n[0] + p; - }); + // It will only match dot entries if it starts with a dot, or if + // dot is set. Stuff like @(.foo|.bar) isn't allowed. + var pn = remain[0] + var negate = !!this.minimatch.negate + var rawGlob = pn._glob + var dotOk = this.dot || rawGlob.charAt(0) === '.' + + var matchedEntries = [] + for (var i = 0; i < entries.length; i++) { + var e = entries[i] + if (e.charAt(0) !== '.' || dotOk) { + var m + if (negate && !prefix) { + m = !e.match(pn) + } else { + m = e.match(pn) } + if (m) + matchedEntries.push(e) } } - // at this point, n is the parts, and we know it's not a comma set - // with a single entry. - - // no need to expand pre, since it is guaranteed to be free of brace-sets - var pre = m.pre; - var post = m.post.length - ? expand(m.post, false) - : ['']; + var len = matchedEntries.length + // If there are no matched entries, then nothing matches. + if (len === 0) + return - var N; + // if this is the last remaining pattern bit, then no need for + // an additional stat *unless* the user has specified mark or + // stat explicitly. We know they exist, since readdir returned + // them. - if (isSequence) { - var x = numeric(n[0]); - var y = numeric(n[1]); - var width = Math.max(n[0].length, n[1].length) - var incr = n.length == 3 - ? Math.abs(numeric(n[2])) - : 1; - var test = lte; - var reverse = y < x; - if (reverse) { - incr *= -1; - test = gte; - } - var pad = n.some(isPadded); + if (remain.length === 1 && !this.mark && !this.stat) { + if (!this.matches[index]) + this.matches[index] = Object.create(null) - N = []; + for (var i = 0; i < len; i ++) { + var e = matchedEntries[i] + if (prefix) { + if (prefix.slice(-1) !== '/') + e = prefix + '/' + e + else + e = prefix + e + } - for (var i = x; test(i, y); i += incr) { - var c; - if (isAlphaSequence) { - c = String.fromCharCode(i); - if (c === '\\') - c = ''; - } else { - c = String(i); - if (pad) { - var need = width - c.length; - if (need > 0) { - var z = new Array(need + 1).join('0'); - if (i < 0) - c = '-' + z + c.slice(1); - else - c = z + c; - } - } + if (e.charAt(0) === '/' && !this.nomount) { + e = path.join(this.root, e) } - N.push(c); + this._emitMatch(index, e) } - } else { - N = concatMap(n, function(el) { return expand(el, false) }); + // This was the last one, and no stats were needed + return } - for (var j = 0; j < N.length; j++) { - for (var k = 0; k < post.length; k++) { - var expansion = pre + N[j] + post[k]; - if (!isTop || isSequence || expansion) - expansions.push(expansion); - } + // now test all matched entries as stand-ins for that part + // of the pattern. + remain.shift() + for (var i = 0; i < len; i ++) { + var e = matchedEntries[i] + var newPattern + if (prefix) + newPattern = [prefix, e] + else + newPattern = [e] + this._process(newPattern.concat(remain), index, inGlobStar) } - - return expansions; } +GlobSync.prototype._emitMatch = function (index, e) { + if (isIgnored(this, e)) + return -/***/ }), -/* 176 */ -/***/ (function(module, exports, __webpack_require__) { + var abs = this._makeAbs(e) -"use strict"; + if (this.mark) + e = this._mark(e) + if (this.absolute) { + e = abs + } -function preserveCamelCase(str) { - let isLastCharLower = false; - let isLastCharUpper = false; - let isLastLastCharUpper = false; + if (this.matches[index][e]) + return - for (let i = 0; i < str.length; i++) { - const c = str[i]; + if (this.nodir) { + var c = this.cache[abs] + if (c === 'DIR' || Array.isArray(c)) + return + } - if (isLastCharLower && /[a-zA-Z]/.test(c) && c.toUpperCase() === c) { - str = str.substr(0, i) + '-' + str.substr(i); - isLastCharLower = false; - isLastLastCharUpper = isLastCharUpper; - isLastCharUpper = true; - i++; - } else if (isLastCharUpper && isLastLastCharUpper && /[a-zA-Z]/.test(c) && c.toLowerCase() === c) { - str = str.substr(0, i - 1) + '-' + str.substr(i - 1); - isLastLastCharUpper = isLastCharUpper; - isLastCharUpper = false; - isLastCharLower = true; - } else { - isLastCharLower = c.toLowerCase() === c; - isLastLastCharUpper = isLastCharUpper; - isLastCharUpper = c.toUpperCase() === c; - } - } + this.matches[index][e] = true - return str; + if (this.stat) + this._stat(e) } -module.exports = function (str) { - if (arguments.length > 1) { - str = Array.from(arguments) - .map(x => x.trim()) - .filter(x => x.length) - .join('-'); - } else { - str = str.trim(); - } - if (str.length === 0) { - return ''; - } +GlobSync.prototype._readdirInGlobStar = function (abs) { + // follow all symlinked directories forever + // just proceed as if this is a non-globstar situation + if (this.follow) + return this._readdir(abs, false) - if (str.length === 1) { - return str.toLowerCase(); - } + var entries + var lstat + var stat + try { + lstat = fs.lstatSync(abs) + } catch (er) { + if (er.code === 'ENOENT') { + // lstat failed, doesn't exist + return null + } + } - if (/^[a-z0-9]+$/.test(str)) { - return str; - } + var isSym = lstat && lstat.isSymbolicLink() + this.symlinks[abs] = isSym - const hasUpperCase = str !== str.toLowerCase(); + // If it's not a symlink or a dir, then it's definitely a regular file. + // don't bother doing a readdir in that case. + if (!isSym && lstat && !lstat.isDirectory()) + this.cache[abs] = 'FILE' + else + entries = this._readdir(abs, false) - if (hasUpperCase) { - str = preserveCamelCase(str); - } + return entries +} - return str - .replace(/^[_.\- ]+/, '') - .toLowerCase() - .replace(/[_.\- ]+(\w|$)/g, (m, p1) => p1.toUpperCase()); -}; +GlobSync.prototype._readdir = function (abs, inGlobStar) { + var entries + if (inGlobStar && !ownProp(this.symlinks, abs)) + return this._readdirInGlobStar(abs) -/***/ }), -/* 177 */, -/* 178 */ -/***/ (function(module, exports) { + if (ownProp(this.cache, abs)) { + var c = this.cache[abs] + if (!c || c === 'FILE') + return null -module.exports = function (xs, fn) { - var res = []; - for (var i = 0; i < xs.length; i++) { - var x = fn(xs[i], i); - if (isArray(x)) res.push.apply(res, x); - else res.push(x); - } - return res; -}; + if (Array.isArray(c)) + return c + } -var isArray = Array.isArray || function (xs) { - return Object.prototype.toString.call(xs) === '[object Array]'; -}; + try { + return this._readdirEntries(abs, fs.readdirSync(abs)) + } catch (er) { + this._readdirError(abs, er) + return null + } +} +GlobSync.prototype._readdirEntries = function (abs, entries) { + // if we haven't asked to stat everything, then just + // assume that everything in there exists, so we can avoid + // having to stat it a second time. + if (!this.mark && !this.stat) { + for (var i = 0; i < entries.length; i ++) { + var e = entries[i] + if (abs === '/') + e = abs + e + else + e = abs + '/' + e + this.cache[e] = true + } + } -/***/ }), -/* 179 */ -/***/ (function(module, exports, __webpack_require__) { + this.cache[abs] = entries -__webpack_require__(205); -__webpack_require__(207); -__webpack_require__(210); -__webpack_require__(206); -__webpack_require__(208); -__webpack_require__(209); -module.exports = __webpack_require__(23).Promise; + // mark and cache dir-ness + return entries +} +GlobSync.prototype._readdirError = function (f, er) { + // handle errors, and cache the information + switch (er.code) { + case 'ENOTSUP': // https://github.com/isaacs/node-glob/issues/205 + case 'ENOTDIR': // totally normal. means it *does* exist. + var abs = this._makeAbs(f) + this.cache[abs] = 'FILE' + if (abs === this.cwdAbs) { + var error = new Error(er.code + ' invalid cwd ' + this.cwd) + error.path = this.cwd + error.code = er.code + throw error + } + break -/***/ }), -/* 180 */ -/***/ (function(module, exports) { + case 'ENOENT': // not terribly unusual + case 'ELOOP': + case 'ENAMETOOLONG': + case 'UNKNOWN': + this.cache[this._makeAbs(f)] = false + break -module.exports = function () { /* empty */ }; + default: // some unusual error. Treat as failure. + this.cache[this._makeAbs(f)] = false + if (this.strict) + throw er + if (!this.silent) + console.error('glob error', er) + break + } +} +GlobSync.prototype._processGlobStar = function (prefix, read, abs, remain, index, inGlobStar) { -/***/ }), -/* 181 */ -/***/ (function(module, exports) { + var entries = this._readdir(abs, inGlobStar) -module.exports = function (it, Constructor, name, forbiddenField) { - if (!(it instanceof Constructor) || (forbiddenField !== undefined && forbiddenField in it)) { - throw TypeError(name + ': incorrect invocation!'); - } return it; -}; + // no entries means not a dir, so it can never have matches + // foo.txt/** doesn't match foo.txt + if (!entries) + return + // test without the globstar, and with every child both below + // and replacing the globstar. + var remainWithoutGlobStar = remain.slice(1) + var gspref = prefix ? [ prefix ] : [] + var noGlobStar = gspref.concat(remainWithoutGlobStar) -/***/ }), -/* 182 */ -/***/ (function(module, exports, __webpack_require__) { + // the noGlobStar pattern exits the inGlobStar state + this._process(noGlobStar, index, false) -// false -> Array#indexOf -// true -> Array#includes -var toIObject = __webpack_require__(74); -var toLength = __webpack_require__(110); -var toAbsoluteIndex = __webpack_require__(200); -module.exports = function (IS_INCLUDES) { - return function ($this, el, fromIndex) { - var O = toIObject($this); - var length = toLength(O.length); - var index = toAbsoluteIndex(fromIndex, length); - var value; - // Array#includes uses SameValueZero equality algorithm - // eslint-disable-next-line no-self-compare - if (IS_INCLUDES && el != el) while (length > index) { - value = O[index++]; - // eslint-disable-next-line no-self-compare - if (value != value) return true; - // Array#indexOf ignores holes, Array#includes - not - } else for (;length > index; index++) if (IS_INCLUDES || index in O) { - if (O[index] === el) return IS_INCLUDES || index || 0; - } return !IS_INCLUDES && -1; - }; -}; + var len = entries.length + var isSym = this.symlinks[abs] + // If it's a symlink, and we're in a globstar, then stop + if (isSym && inGlobStar) + return -/***/ }), -/* 183 */ -/***/ (function(module, exports, __webpack_require__) { + for (var i = 0; i < len; i++) { + var e = entries[i] + if (e.charAt(0) === '.' && !this.dot) + continue -var ctx = __webpack_require__(48); -var call = __webpack_require__(187); -var isArrayIter = __webpack_require__(186); -var anObject = __webpack_require__(27); -var toLength = __webpack_require__(110); -var getIterFn = __webpack_require__(203); -var BREAK = {}; -var RETURN = {}; -var exports = module.exports = function (iterable, entries, fn, that, ITERATOR) { - var iterFn = ITERATOR ? function () { return iterable; } : getIterFn(iterable); - var f = ctx(fn, that, entries ? 2 : 1); - var index = 0; - var length, step, iterator, result; - if (typeof iterFn != 'function') throw TypeError(iterable + ' is not iterable!'); - // fast case for arrays with default iterator - if (isArrayIter(iterFn)) for (length = toLength(iterable.length); length > index; index++) { - result = entries ? f(anObject(step = iterable[index])[0], step[1]) : f(iterable[index]); - if (result === BREAK || result === RETURN) return result; - } else for (iterator = iterFn.call(iterable); !(step = iterator.next()).done;) { - result = call(iterator, f, step.value, entries); - if (result === BREAK || result === RETURN) return result; + // these two cases enter the inGlobStar state + var instead = gspref.concat(entries[i], remainWithoutGlobStar) + this._process(instead, index, true) + + var below = gspref.concat(entries[i], remain) + this._process(below, index, true) } -}; -exports.BREAK = BREAK; -exports.RETURN = RETURN; +} + +GlobSync.prototype._processSimple = function (prefix, index) { + // XXX review this. Shouldn't it be doing the mounting etc + // before doing stat? kinda weird? + var exists = this._stat(prefix) + if (!this.matches[index]) + this.matches[index] = Object.create(null) -/***/ }), -/* 184 */ -/***/ (function(module, exports, __webpack_require__) { + // If it doesn't exist, then just mark the lack of results + if (!exists) + return -module.exports = !__webpack_require__(33) && !__webpack_require__(85)(function () { - return Object.defineProperty(__webpack_require__(68)('div'), 'a', { get: function () { return 7; } }).a != 7; -}); + if (prefix && isAbsolute(prefix) && !this.nomount) { + var trail = /[\/\\]$/.test(prefix) + if (prefix.charAt(0) === '/') { + prefix = path.join(this.root, prefix) + } else { + prefix = path.resolve(this.root, prefix) + if (trail) + prefix += '/' + } + } + if (process.platform === 'win32') + prefix = prefix.replace(/\\/g, '/') -/***/ }), -/* 185 */ -/***/ (function(module, exports) { + // Mark this as a match + this._emitMatch(index, prefix) +} -// fast apply, http://jsperf.lnkit.com/fast-apply/5 -module.exports = function (fn, args, that) { - var un = that === undefined; - switch (args.length) { - case 0: return un ? fn() - : fn.call(that); - case 1: return un ? fn(args[0]) - : fn.call(that, args[0]); - case 2: return un ? fn(args[0], args[1]) - : fn.call(that, args[0], args[1]); - case 3: return un ? fn(args[0], args[1], args[2]) - : fn.call(that, args[0], args[1], args[2]); - case 4: return un ? fn(args[0], args[1], args[2], args[3]) - : fn.call(that, args[0], args[1], args[2], args[3]); - } return fn.apply(that, args); -}; +// Returns either 'DIR', 'FILE', or false +GlobSync.prototype._stat = function (f) { + var abs = this._makeAbs(f) + var needDir = f.slice(-1) === '/' + if (f.length > this.maxLength) + return false -/***/ }), -/* 186 */ -/***/ (function(module, exports, __webpack_require__) { + if (!this.stat && ownProp(this.cache, abs)) { + var c = this.cache[abs] -// check on default Array iterator -var Iterators = __webpack_require__(35); -var ITERATOR = __webpack_require__(13)('iterator'); -var ArrayProto = Array.prototype; + if (Array.isArray(c)) + c = 'DIR' -module.exports = function (it) { - return it !== undefined && (Iterators.Array === it || ArrayProto[ITERATOR] === it); -}; + // It exists, but maybe not how we need it + if (!needDir || c === 'DIR') + return c + if (needDir && c === 'FILE') + return false -/***/ }), -/* 187 */ -/***/ (function(module, exports, __webpack_require__) { + // otherwise we have to stat, because maybe c=true + // if we know it exists, but not what it is. + } -// call something on iterator step with safe closing on error -var anObject = __webpack_require__(27); -module.exports = function (iterator, fn, value, entries) { - try { - return entries ? fn(anObject(value)[0], value[1]) : fn(value); - // 7.4.6 IteratorClose(iterator, completion) - } catch (e) { - var ret = iterator['return']; - if (ret !== undefined) anObject(ret.call(iterator)); - throw e; + var exists + var stat = this.statCache[abs] + if (!stat) { + var lstat + try { + lstat = fs.lstatSync(abs) + } catch (er) { + if (er && (er.code === 'ENOENT' || er.code === 'ENOTDIR')) { + this.statCache[abs] = false + return false + } + } + + if (lstat && lstat.isSymbolicLink()) { + try { + stat = fs.statSync(abs) + } catch (er) { + stat = lstat + } + } else { + stat = lstat + } } -}; + this.statCache[abs] = stat -/***/ }), -/* 188 */ -/***/ (function(module, exports, __webpack_require__) { + var c = true + if (stat) + c = stat.isDirectory() ? 'DIR' : 'FILE' -"use strict"; + this.cache[abs] = this.cache[abs] || c -var create = __webpack_require__(192); -var descriptor = __webpack_require__(106); -var setToStringTag = __webpack_require__(71); -var IteratorPrototype = {}; + if (needDir && c === 'FILE') + return false -// 25.1.2.1.1 %IteratorPrototype%[@@iterator]() -__webpack_require__(31)(IteratorPrototype, __webpack_require__(13)('iterator'), function () { return this; }); + return c +} -module.exports = function (Constructor, NAME, next) { - Constructor.prototype = create(IteratorPrototype, { next: descriptor(1, next) }); - setToStringTag(Constructor, NAME + ' Iterator'); -}; +GlobSync.prototype._mark = function (p) { + return common.mark(this, p) +} + +GlobSync.prototype._makeAbs = function (f) { + return common.makeAbs(this, f) +} /***/ }), -/* 189 */ +/* 219 */, +/* 220 */, +/* 221 */ /***/ (function(module, exports, __webpack_require__) { -var ITERATOR = __webpack_require__(13)('iterator'); -var SAFE_CLOSING = false; - -try { - var riter = [7][ITERATOR](); - riter['return'] = function () { SAFE_CLOSING = true; }; - // eslint-disable-next-line no-throw-literal - Array.from(riter, function () { throw 2; }); -} catch (e) { /* empty */ } - -module.exports = function (exec, skipClosing) { - if (!skipClosing && !SAFE_CLOSING) return false; - var safe = false; - try { - var arr = [7]; - var iter = arr[ITERATOR](); - iter.next = function () { return { done: safe = true }; }; - arr[ITERATOR] = function () { return iter; }; - exec(arr); - } catch (e) { /* empty */ } - return safe; -}; +"use strict"; +module.exports = function (flag, argv) { + argv = argv || process.argv; -/***/ }), -/* 190 */ -/***/ (function(module, exports) { + var terminatorPos = argv.indexOf('--'); + var prefix = /^--/.test(flag) ? '' : '--'; + var pos = argv.indexOf(prefix + flag); -module.exports = function (done, value) { - return { value: value, done: !!done }; + return pos !== -1 && (terminatorPos !== -1 ? pos < terminatorPos : true); }; /***/ }), -/* 191 */ +/* 222 */, +/* 223 */ /***/ (function(module, exports, __webpack_require__) { -var global = __webpack_require__(11); -var macrotask = __webpack_require__(109).set; -var Observer = global.MutationObserver || global.WebKitMutationObserver; -var process = global.process; -var Promise = global.Promise; -var isNode = __webpack_require__(47)(process) == 'process'; - -module.exports = function () { - var head, last, notify; +var wrappy = __webpack_require__(123) +var reqs = Object.create(null) +var once = __webpack_require__(61) - var flush = function () { - var parent, fn; - if (isNode && (parent = process.domain)) parent.exit(); - while (head) { - fn = head.fn; - head = head.next; - try { - fn(); - } catch (e) { - if (head) notify(); - else last = undefined; - throw e; - } - } last = undefined; - if (parent) parent.enter(); - }; +module.exports = wrappy(inflight) - // Node.js - if (isNode) { - notify = function () { - process.nextTick(flush); - }; - // browsers with MutationObserver, except iOS Safari - https://github.com/zloirock/core-js/issues/339 - } else if (Observer && !(global.navigator && global.navigator.standalone)) { - var toggle = true; - var node = document.createTextNode(''); - new Observer(flush).observe(node, { characterData: true }); // eslint-disable-line no-new - notify = function () { - node.data = toggle = !toggle; - }; - // environments with maybe non-completely correct, but existent Promise - } else if (Promise && Promise.resolve) { - // Promise.resolve without an argument throws an error in LG WebOS 2 - var promise = Promise.resolve(undefined); - notify = function () { - promise.then(flush); - }; - // for other environments - macrotask based on: - // - setImmediate - // - MessageChannel - // - window.postMessag - // - onreadystatechange - // - setTimeout +function inflight (key, cb) { + if (reqs[key]) { + reqs[key].push(cb) + return null } else { - notify = function () { - // strange IE + webpack dev server bug - use .call(global) - macrotask.call(global, flush); - }; + reqs[key] = [cb] + return makeres(key) } +} - return function (fn) { - var task = { fn: fn, next: undefined }; - if (last) last.next = task; - if (!head) { - head = task; - notify(); - } last = task; - }; -}; +function makeres (key) { + return once(function RES () { + var cbs = reqs[key] + var len = cbs.length + var args = slice(arguments) + // XXX It's somewhat ambiguous whether a new callback added in this + // pass should be queued for later execution if something in the + // list of callbacks throws, or if it should just be discarded. + // However, it's such an edge case that it hardly matters, and either + // choice is likely as surprising as the other. + // As it happens, we do go ahead and schedule it for later execution. + try { + for (var i = 0; i < len; i++) { + cbs[i].apply(null, args) + } + } finally { + if (cbs.length > len) { + // added more in the interim. + // de-zalgo, just in case, but don't call again. + cbs.splice(0, len) + process.nextTick(function () { + RES.apply(null, args) + }) + } else { + delete reqs[key] + } + } + }) +} -/***/ }), -/* 192 */ -/***/ (function(module, exports, __webpack_require__) { +function slice (args) { + var length = args.length + var array = [] -// 19.1.2.2 / 15.2.3.5 Object.create(O [, Properties]) -var anObject = __webpack_require__(27); -var dPs = __webpack_require__(193); -var enumBugKeys = __webpack_require__(101); -var IE_PROTO = __webpack_require__(72)('IE_PROTO'); -var Empty = function () { /* empty */ }; -var PROTOTYPE = 'prototype'; + for (var i = 0; i < length; i++) array[i] = args[i] + return array +} -// Create object with fake `null` prototype: use iframe Object with cleared prototype -var createDict = function () { - // Thrash, waste and sodomy: IE GC bug - var iframe = __webpack_require__(68)('iframe'); - var i = enumBugKeys.length; - var lt = '<'; - var gt = '>'; - var iframeDocument; - iframe.style.display = 'none'; - __webpack_require__(102).appendChild(iframe); - iframe.src = 'javascript:'; // eslint-disable-line no-script-url - // createDict = iframe.contentWindow.Object; - // html.removeChild(iframe); - iframeDocument = iframe.contentWindow.document; - iframeDocument.open(); - iframeDocument.write(lt + 'script' + gt + 'document.F=Object' + lt + '/script' + gt); - iframeDocument.close(); - createDict = iframeDocument.F; - while (i--) delete createDict[PROTOTYPE][enumBugKeys[i]]; - return createDict(); -}; -module.exports = Object.create || function create(O, Properties) { - var result; - if (O !== null) { - Empty[PROTOTYPE] = anObject(O); - result = new Empty(); - Empty[PROTOTYPE] = null; - // add "__proto__" for Object.getPrototypeOf polyfill - result[IE_PROTO] = O; - } else result = createDict(); - return Properties === undefined ? result : dPs(result, Properties); -}; +/***/ }), +/* 224 */ +/***/ (function(module, exports) { + +if (typeof Object.create === 'function') { + // implementation from standard node.js 'util' module + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }); + }; +} else { + // old school shim for old browsers + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor + var TempCtor = function () {} + TempCtor.prototype = superCtor.prototype + ctor.prototype = new TempCtor() + ctor.prototype.constructor = ctor + } +} /***/ }), -/* 193 */ +/* 225 */, +/* 226 */, +/* 227 */ /***/ (function(module, exports, __webpack_require__) { -var dP = __webpack_require__(50); -var anObject = __webpack_require__(27); -var getKeys = __webpack_require__(132); +// @flow -module.exports = __webpack_require__(33) ? Object.defineProperties : function defineProperties(O, Properties) { - anObject(O); - var keys = getKeys(Properties); - var length = keys.length; - var i = 0; - var P; - while (length > i) dP.f(O, P = keys[i++], Properties[P]); - return O; -}; +/*:: +declare var __webpack_require__: mixed; +*/ + +module.exports = typeof __webpack_require__ !== "undefined"; /***/ }), -/* 194 */ -/***/ (function(module, exports, __webpack_require__) { +/* 228 */, +/* 229 */ +/***/ (function(module, exports) { -// 19.1.2.9 / 15.2.3.2 Object.getPrototypeOf(O) -var has = __webpack_require__(49); -var toObject = __webpack_require__(133); -var IE_PROTO = __webpack_require__(72)('IE_PROTO'); -var ObjectProto = Object.prototype; +/** + * Helpers. + */ -module.exports = Object.getPrototypeOf || function (O) { - O = toObject(O); - if (has(O, IE_PROTO)) return O[IE_PROTO]; - if (typeof O.constructor == 'function' && O instanceof O.constructor) { - return O.constructor.prototype; - } return O instanceof Object ? ObjectProto : null; -}; +var s = 1000; +var m = s * 60; +var h = m * 60; +var d = h * 24; +var y = d * 365.25; +/** + * Parse or format the given `val`. + * + * Options: + * + * - `long` verbose formatting [false] + * + * @param {String|Number} val + * @param {Object} [options] + * @throws {Error} throw an error if val is not a non-empty string or a number + * @return {String|Number} + * @api public + */ -/***/ }), -/* 195 */ -/***/ (function(module, exports, __webpack_require__) { +module.exports = function(val, options) { + options = options || {}; + var type = typeof val; + if (type === 'string' && val.length > 0) { + return parse(val); + } else if (type === 'number' && isNaN(val) === false) { + return options.long ? fmtLong(val) : fmtShort(val); + } + throw new Error( + 'val is not a non-empty string or a valid number. val=' + + JSON.stringify(val) + ); +}; -var has = __webpack_require__(49); -var toIObject = __webpack_require__(74); -var arrayIndexOf = __webpack_require__(182)(false); -var IE_PROTO = __webpack_require__(72)('IE_PROTO'); +/** + * Parse the given `str` and return milliseconds. + * + * @param {String} str + * @return {Number} + * @api private + */ -module.exports = function (object, names) { - var O = toIObject(object); - var i = 0; - var result = []; - var key; - for (key in O) if (key != IE_PROTO) has(O, key) && result.push(key); - // Don't enum bug & hidden keys - while (names.length > i) if (has(O, key = names[i++])) { - ~arrayIndexOf(result, key) || result.push(key); +function parse(str) { + str = String(str); + if (str.length > 100) { + return; } - return result; -}; + var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec( + str + ); + if (!match) { + return; + } + var n = parseFloat(match[1]); + var type = (match[2] || 'ms').toLowerCase(); + switch (type) { + case 'years': + case 'year': + case 'yrs': + case 'yr': + case 'y': + return n * y; + case 'days': + case 'day': + case 'd': + return n * d; + case 'hours': + case 'hour': + case 'hrs': + case 'hr': + case 'h': + return n * h; + case 'minutes': + case 'minute': + case 'mins': + case 'min': + case 'm': + return n * m; + case 'seconds': + case 'second': + case 'secs': + case 'sec': + case 's': + return n * s; + case 'milliseconds': + case 'millisecond': + case 'msecs': + case 'msec': + case 'ms': + return n; + default: + return undefined; + } +} +/** + * Short format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ -/***/ }), -/* 196 */ -/***/ (function(module, exports, __webpack_require__) { +function fmtShort(ms) { + if (ms >= d) { + return Math.round(ms / d) + 'd'; + } + if (ms >= h) { + return Math.round(ms / h) + 'h'; + } + if (ms >= m) { + return Math.round(ms / m) + 'm'; + } + if (ms >= s) { + return Math.round(ms / s) + 's'; + } + return ms + 'ms'; +} -var hide = __webpack_require__(31); -module.exports = function (target, src, safe) { - for (var key in src) { - if (safe && target[key]) target[key] = src[key]; - else hide(target, key, src[key]); - } return target; -}; +/** + * Long format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private + */ +function fmtLong(ms) { + return plural(ms, d, 'day') || + plural(ms, h, 'hour') || + plural(ms, m, 'minute') || + plural(ms, s, 'second') || + ms + ' ms'; +} -/***/ }), -/* 197 */ -/***/ (function(module, exports, __webpack_require__) { +/** + * Pluralization helper. + */ -module.exports = __webpack_require__(31); +function plural(ms, n, name) { + if (ms < n) { + return; + } + if (ms < n * 1.5) { + return Math.floor(ms / n) + ' ' + name; + } + return Math.ceil(ms / n) + ' ' + name + 's'; +} /***/ }), -/* 198 */ +/* 230 */, +/* 231 */, +/* 232 */, +/* 233 */ /***/ (function(module, exports, __webpack_require__) { -"use strict"; - -var global = __webpack_require__(11); -var core = __webpack_require__(23); -var dP = __webpack_require__(50); -var DESCRIPTORS = __webpack_require__(33); -var SPECIES = __webpack_require__(13)('species'); - -module.exports = function (KEY) { - var C = typeof core[KEY] == 'function' ? core[KEY] : global[KEY]; - if (DESCRIPTORS && C && !C[SPECIES]) dP.f(C, SPECIES, { - configurable: true, - get: function () { return this; } - }); -}; - +module.exports = rimraf +rimraf.sync = rimrafSync -/***/ }), -/* 199 */ -/***/ (function(module, exports, __webpack_require__) { +var assert = __webpack_require__(22) +var path = __webpack_require__(0) +var fs = __webpack_require__(3) +var glob = __webpack_require__(75) +var _0666 = parseInt('666', 8) -var toInteger = __webpack_require__(73); -var defined = __webpack_require__(67); -// true -> String#at -// false -> String#codePointAt -module.exports = function (TO_STRING) { - return function (that, pos) { - var s = String(defined(that)); - var i = toInteger(pos); - var l = s.length; - var a, b; - if (i < 0 || i >= l) return TO_STRING ? '' : undefined; - a = s.charCodeAt(i); - return a < 0xd800 || a > 0xdbff || i + 1 === l || (b = s.charCodeAt(i + 1)) < 0xdc00 || b > 0xdfff - ? TO_STRING ? s.charAt(i) : a - : TO_STRING ? s.slice(i, i + 2) : (a - 0xd800 << 10) + (b - 0xdc00) + 0x10000; - }; -}; +var defaultGlobOpts = { + nosort: true, + silent: true +} +// for EMFILE handling +var timeout = 0 -/***/ }), -/* 200 */ -/***/ (function(module, exports, __webpack_require__) { +var isWindows = (process.platform === "win32") -var toInteger = __webpack_require__(73); -var max = Math.max; -var min = Math.min; -module.exports = function (index, length) { - index = toInteger(index); - return index < 0 ? max(index + length, 0) : min(index, length); -}; +function defaults (options) { + var methods = [ + 'unlink', + 'chmod', + 'stat', + 'lstat', + 'rmdir', + 'readdir' + ] + methods.forEach(function(m) { + options[m] = options[m] || fs[m] + m = m + 'Sync' + options[m] = options[m] || fs[m] + }) + options.maxBusyTries = options.maxBusyTries || 3 + options.emfileWait = options.emfileWait || 1000 + if (options.glob === false) { + options.disableGlob = true + } + options.disableGlob = options.disableGlob || false + options.glob = options.glob || defaultGlobOpts +} -/***/ }), -/* 201 */ -/***/ (function(module, exports, __webpack_require__) { +function rimraf (p, options, cb) { + if (typeof options === 'function') { + cb = options + options = {} + } -// 7.1.1 ToPrimitive(input [, PreferredType]) -var isObject = __webpack_require__(34); -// instead of the ES6 spec version, we didn't implement @@toPrimitive case -// and the second argument - flag - preferred type is a string -module.exports = function (it, S) { - if (!isObject(it)) return it; - var fn, val; - if (S && typeof (fn = it.toString) == 'function' && !isObject(val = fn.call(it))) return val; - if (typeof (fn = it.valueOf) == 'function' && !isObject(val = fn.call(it))) return val; - if (!S && typeof (fn = it.toString) == 'function' && !isObject(val = fn.call(it))) return val; - throw TypeError("Can't convert object to primitive value"); -}; + assert(p, 'rimraf: missing path') + assert.equal(typeof p, 'string', 'rimraf: path should be a string') + assert.equal(typeof cb, 'function', 'rimraf: callback function required') + assert(options, 'rimraf: invalid options argument provided') + assert.equal(typeof options, 'object', 'rimraf: options should be object') + defaults(options) -/***/ }), -/* 202 */ -/***/ (function(module, exports, __webpack_require__) { + var busyTries = 0 + var errState = null + var n = 0 -var global = __webpack_require__(11); -var navigator = global.navigator; + if (options.disableGlob || !glob.hasMagic(p)) + return afterGlob(null, [p]) -module.exports = navigator && navigator.userAgent || ''; + options.lstat(p, function (er, stat) { + if (!er) + return afterGlob(null, [p]) + glob(p, options.glob, afterGlob) + }) -/***/ }), -/* 203 */ -/***/ (function(module, exports, __webpack_require__) { + function next (er) { + errState = errState || er + if (--n === 0) + cb(errState) + } -var classof = __webpack_require__(100); -var ITERATOR = __webpack_require__(13)('iterator'); -var Iterators = __webpack_require__(35); -module.exports = __webpack_require__(23).getIteratorMethod = function (it) { - if (it != undefined) return it[ITERATOR] - || it['@@iterator'] - || Iterators[classof(it)]; -}; + function afterGlob (er, results) { + if (er) + return cb(er) + n = results.length + if (n === 0) + return cb() -/***/ }), -/* 204 */ -/***/ (function(module, exports, __webpack_require__) { + results.forEach(function (p) { + rimraf_(p, options, function CB (er) { + if (er) { + if ((er.code === "EBUSY" || er.code === "ENOTEMPTY" || er.code === "EPERM") && + busyTries < options.maxBusyTries) { + busyTries ++ + var time = busyTries * 100 + // try again, with the same exact callback as this one. + return setTimeout(function () { + rimraf_(p, options, CB) + }, time) + } -"use strict"; + // this one won't happen if graceful-fs is used. + if (er.code === "EMFILE" && timeout < options.emfileWait) { + return setTimeout(function () { + rimraf_(p, options, CB) + }, timeout ++) + } -var addToUnscopables = __webpack_require__(180); -var step = __webpack_require__(190); -var Iterators = __webpack_require__(35); -var toIObject = __webpack_require__(74); + // already gone + if (er.code === "ENOENT") er = null + } -// 22.1.3.4 Array.prototype.entries() -// 22.1.3.13 Array.prototype.keys() -// 22.1.3.29 Array.prototype.values() -// 22.1.3.30 Array.prototype[@@iterator]() -module.exports = __webpack_require__(103)(Array, 'Array', function (iterated, kind) { - this._t = toIObject(iterated); // target - this._i = 0; // next index - this._k = kind; // kind -// 22.1.5.2.1 %ArrayIteratorPrototype%.next() -}, function () { - var O = this._t; - var kind = this._k; - var index = this._i++; - if (!O || index >= O.length) { - this._t = undefined; - return step(1); + timeout = 0 + next(er) + }) + }) } - if (kind == 'keys') return step(0, index); - if (kind == 'values') return step(0, O[index]); - return step(0, [index, O[index]]); -}, 'values'); - -// argumentsList[@@iterator] is %ArrayProto_values% (9.4.4.6, 9.4.4.7) -Iterators.Arguments = Iterators.Array; +} -addToUnscopables('keys'); -addToUnscopables('values'); -addToUnscopables('entries'); +// Two possible strategies. +// 1. Assume it's a file. unlink it, then do the dir stuff on EPERM or EISDIR +// 2. Assume it's a directory. readdir, then do the file stuff on ENOTDIR +// +// Both result in an extra syscall when you guess wrong. However, there +// are likely far more normal files in the world than directories. This +// is based on the assumption that a the average number of files per +// directory is >= 1. +// +// If anyone ever complains about this, then I guess the strategy could +// be made configurable somehow. But until then, YAGNI. +function rimraf_ (p, options, cb) { + assert(p) + assert(options) + assert(typeof cb === 'function') + // sunos lets the root user unlink directories, which is... weird. + // so we have to lstat here and make sure it's not a dir. + options.lstat(p, function (er, st) { + if (er && er.code === "ENOENT") + return cb(null) -/***/ }), -/* 205 */ -/***/ (function(module, exports) { + // Windows can EPERM on stat. Life is suffering. + if (er && er.code === "EPERM" && isWindows) + fixWinEPERM(p, options, er, cb) + if (st && st.isDirectory()) + return rmdir(p, options, er, cb) + options.unlink(p, function (er) { + if (er) { + if (er.code === "ENOENT") + return cb(null) + if (er.code === "EPERM") + return (isWindows) + ? fixWinEPERM(p, options, er, cb) + : rmdir(p, options, er, cb) + if (er.code === "EISDIR") + return rmdir(p, options, er, cb) + } + return cb(er) + }) + }) +} -/***/ }), -/* 206 */ -/***/ (function(module, exports, __webpack_require__) { +function fixWinEPERM (p, options, er, cb) { + assert(p) + assert(options) + assert(typeof cb === 'function') + if (er) + assert(er instanceof Error) -"use strict"; + options.chmod(p, _0666, function (er2) { + if (er2) + cb(er2.code === "ENOENT" ? null : er) + else + options.stat(p, function(er3, stats) { + if (er3) + cb(er3.code === "ENOENT" ? null : er) + else if (stats.isDirectory()) + rmdir(p, options, er, cb) + else + options.unlink(p, cb) + }) + }) +} -var LIBRARY = __webpack_require__(69); -var global = __webpack_require__(11); -var ctx = __webpack_require__(48); -var classof = __webpack_require__(100); -var $export = __webpack_require__(41); -var isObject = __webpack_require__(34); -var aFunction = __webpack_require__(46); -var anInstance = __webpack_require__(181); -var forOf = __webpack_require__(183); -var speciesConstructor = __webpack_require__(108); -var task = __webpack_require__(109).set; -var microtask = __webpack_require__(191)(); -var newPromiseCapabilityModule = __webpack_require__(70); -var perform = __webpack_require__(104); -var userAgent = __webpack_require__(202); -var promiseResolve = __webpack_require__(105); -var PROMISE = 'Promise'; -var TypeError = global.TypeError; -var process = global.process; -var versions = process && process.versions; -var v8 = versions && versions.v8 || ''; -var $Promise = global[PROMISE]; -var isNode = classof(process) == 'process'; -var empty = function () { /* empty */ }; -var Internal, newGenericPromiseCapability, OwnPromiseCapability, Wrapper; -var newPromiseCapability = newGenericPromiseCapability = newPromiseCapabilityModule.f; +function fixWinEPERMSync (p, options, er) { + assert(p) + assert(options) + if (er) + assert(er instanceof Error) -var USE_NATIVE = !!function () { try { - // correct subclassing with @@species support - var promise = $Promise.resolve(1); - var FakePromise = (promise.constructor = {})[__webpack_require__(13)('species')] = function (exec) { - exec(empty, empty); - }; - // unhandled rejections tracking support, NodeJS Promise without it fails @@species test - return (isNode || typeof PromiseRejectionEvent == 'function') - && promise.then(empty) instanceof FakePromise - // v8 6.6 (Node 10 and Chrome 66) have a bug with resolving custom thenables - // https://bugs.chromium.org/p/chromium/issues/detail?id=830565 - // we can't detect it synchronously, so just check versions - && v8.indexOf('6.6') !== 0 - && userAgent.indexOf('Chrome/66') === -1; - } catch (e) { /* empty */ } -}(); + options.chmodSync(p, _0666) + } catch (er2) { + if (er2.code === "ENOENT") + return + else + throw er + } -// helpers -var isThenable = function (it) { - var then; - return isObject(it) && typeof (then = it.then) == 'function' ? then : false; -}; -var notify = function (promise, isReject) { - if (promise._n) return; - promise._n = true; - var chain = promise._c; - microtask(function () { - var value = promise._v; - var ok = promise._s == 1; - var i = 0; - var run = function (reaction) { - var handler = ok ? reaction.ok : reaction.fail; - var resolve = reaction.resolve; - var reject = reaction.reject; - var domain = reaction.domain; - var result, then, exited; - try { - if (handler) { - if (!ok) { - if (promise._h == 2) onHandleUnhandled(promise); - promise._h = 1; - } - if (handler === true) result = value; - else { - if (domain) domain.enter(); - result = handler(value); // may throw - if (domain) { - domain.exit(); - exited = true; - } - } - if (result === reaction.promise) { - reject(TypeError('Promise-chain cycle')); - } else if (then = isThenable(result)) { - then.call(result, resolve, reject); - } else resolve(result); - } else reject(value); - } catch (e) { - if (domain && !exited) domain.exit(); - reject(e); - } - }; - while (chain.length > i) run(chain[i++]); // variable length - can't use forEach - promise._c = []; - promise._n = false; - if (isReject && !promise._h) onUnhandled(promise); - }); -}; -var onUnhandled = function (promise) { - task.call(global, function () { - var value = promise._v; - var unhandled = isUnhandled(promise); - var result, handler, console; - if (unhandled) { - result = perform(function () { - if (isNode) { - process.emit('unhandledRejection', value, promise); - } else if (handler = global.onunhandledrejection) { - handler({ promise: promise, reason: value }); - } else if ((console = global.console) && console.error) { - console.error('Unhandled promise rejection', value); - } - }); - // Browsers should not trigger `rejectionHandled` event if it was handled here, NodeJS - should - promise._h = isNode || isUnhandled(promise) ? 2 : 1; - } promise._a = undefined; - if (unhandled && result.e) throw result.v; - }); -}; -var isUnhandled = function (promise) { - return promise._h !== 1 && (promise._a || promise._c).length === 0; -}; -var onHandleUnhandled = function (promise) { - task.call(global, function () { - var handler; - if (isNode) { - process.emit('rejectionHandled', promise); - } else if (handler = global.onrejectionhandled) { - handler({ promise: promise, reason: promise._v }); - } - }); -}; -var $reject = function (value) { - var promise = this; - if (promise._d) return; - promise._d = true; - promise = promise._w || promise; // unwrap - promise._v = value; - promise._s = 2; - if (!promise._a) promise._a = promise._c.slice(); - notify(promise, true); -}; -var $resolve = function (value) { - var promise = this; - var then; - if (promise._d) return; - promise._d = true; - promise = promise._w || promise; // unwrap try { - if (promise === value) throw TypeError("Promise can't be resolved itself"); - if (then = isThenable(value)) { - microtask(function () { - var wrapper = { _w: promise, _d: false }; // wrap - try { - then.call(value, ctx($resolve, wrapper, 1), ctx($reject, wrapper, 1)); - } catch (e) { - $reject.call(wrapper, e); - } - }); - } else { - promise._v = value; - promise._s = 1; - notify(promise, false); + var stats = options.statSync(p) + } catch (er3) { + if (er3.code === "ENOENT") + return + else + throw er + } + + if (stats.isDirectory()) + rmdirSync(p, options, er) + else + options.unlinkSync(p) +} + +function rmdir (p, options, originalEr, cb) { + assert(p) + assert(options) + if (originalEr) + assert(originalEr instanceof Error) + assert(typeof cb === 'function') + + // try to rmdir first, and only readdir on ENOTEMPTY or EEXIST (SunOS) + // if we guessed wrong, and it's not a directory, then + // raise the original error. + options.rmdir(p, function (er) { + if (er && (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM")) + rmkids(p, options, cb) + else if (er && er.code === "ENOTDIR") + cb(originalEr) + else + cb(er) + }) +} + +function rmkids(p, options, cb) { + assert(p) + assert(options) + assert(typeof cb === 'function') + + options.readdir(p, function (er, files) { + if (er) + return cb(er) + var n = files.length + if (n === 0) + return options.rmdir(p, cb) + var errState + files.forEach(function (f) { + rimraf(path.join(p, f), options, function (er) { + if (errState) + return + if (er) + return cb(errState = er) + if (--n === 0) + options.rmdir(p, cb) + }) + }) + }) +} + +// this looks simpler, and is strictly *faster*, but will +// tie up the JavaScript thread and fail on excessively +// deep directory trees. +function rimrafSync (p, options) { + options = options || {} + defaults(options) + + assert(p, 'rimraf: missing path') + assert.equal(typeof p, 'string', 'rimraf: path should be a string') + assert(options, 'rimraf: missing options') + assert.equal(typeof options, 'object', 'rimraf: options should be object') + + var results + + if (options.disableGlob || !glob.hasMagic(p)) { + results = [p] + } else { + try { + options.lstatSync(p) + results = [p] + } catch (er) { + results = glob.sync(p, options.glob) } - } catch (e) { - $reject.call({ _w: promise, _d: false }, e); // wrap } -}; -// constructor polyfill -if (!USE_NATIVE) { - // 25.4.3.1 Promise(executor) - $Promise = function Promise(executor) { - anInstance(this, $Promise, PROMISE, '_h'); - aFunction(executor); - Internal.call(this); + if (!results.length) + return + + for (var i = 0; i < results.length; i++) { + var p = results[i] + try { - executor(ctx($resolve, this, 1), ctx($reject, this, 1)); - } catch (err) { - $reject.call(this, err); + var st = options.lstatSync(p) + } catch (er) { + if (er.code === "ENOENT") + return + + // Windows can EPERM on stat. Life is suffering. + if (er.code === "EPERM" && isWindows) + fixWinEPERMSync(p, options, er) } - }; - // eslint-disable-next-line no-unused-vars - Internal = function Promise(executor) { - this._c = []; // <- awaiting reactions - this._a = undefined; // <- checked in isUnhandled reactions - this._s = 0; // <- state - this._d = false; // <- done - this._v = undefined; // <- value - this._h = 0; // <- rejection state, 0 - default, 1 - handled, 2 - unhandled - this._n = false; // <- notify - }; - Internal.prototype = __webpack_require__(196)($Promise.prototype, { - // 25.4.5.3 Promise.prototype.then(onFulfilled, onRejected) - then: function then(onFulfilled, onRejected) { - var reaction = newPromiseCapability(speciesConstructor(this, $Promise)); - reaction.ok = typeof onFulfilled == 'function' ? onFulfilled : true; - reaction.fail = typeof onRejected == 'function' && onRejected; - reaction.domain = isNode ? process.domain : undefined; - this._c.push(reaction); - if (this._a) this._a.push(reaction); - if (this._s) notify(this, false); - return reaction.promise; - }, - // 25.4.5.1 Promise.prototype.catch(onRejected) - 'catch': function (onRejected) { - return this.then(undefined, onRejected); + + try { + // sunos lets the root user unlink directories, which is... weird. + if (st && st.isDirectory()) + rmdirSync(p, options, null) + else + options.unlinkSync(p) + } catch (er) { + if (er.code === "ENOENT") + return + if (er.code === "EPERM") + return isWindows ? fixWinEPERMSync(p, options, er) : rmdirSync(p, options, er) + if (er.code !== "EISDIR") + throw er + + rmdirSync(p, options, er) } - }); - OwnPromiseCapability = function () { - var promise = new Internal(); - this.promise = promise; - this.resolve = ctx($resolve, promise, 1); - this.reject = ctx($reject, promise, 1); - }; - newPromiseCapabilityModule.f = newPromiseCapability = function (C) { - return C === $Promise || C === Wrapper - ? new OwnPromiseCapability(C) - : newGenericPromiseCapability(C); - }; + } } -$export($export.G + $export.W + $export.F * !USE_NATIVE, { Promise: $Promise }); -__webpack_require__(71)($Promise, PROMISE); -__webpack_require__(198)(PROMISE); -Wrapper = __webpack_require__(23)[PROMISE]; +function rmdirSync (p, options, originalEr) { + assert(p) + assert(options) + if (originalEr) + assert(originalEr instanceof Error) -// statics -$export($export.S + $export.F * !USE_NATIVE, PROMISE, { - // 25.4.4.5 Promise.reject(r) - reject: function reject(r) { - var capability = newPromiseCapability(this); - var $$reject = capability.reject; - $$reject(r); - return capability.promise; - } -}); -$export($export.S + $export.F * (LIBRARY || !USE_NATIVE), PROMISE, { - // 25.4.4.6 Promise.resolve(x) - resolve: function resolve(x) { - return promiseResolve(LIBRARY && this === Wrapper ? $Promise : this, x); - } -}); -$export($export.S + $export.F * !(USE_NATIVE && __webpack_require__(189)(function (iter) { - $Promise.all(iter)['catch'](empty); -})), PROMISE, { - // 25.4.4.1 Promise.all(iterable) - all: function all(iterable) { - var C = this; - var capability = newPromiseCapability(C); - var resolve = capability.resolve; - var reject = capability.reject; - var result = perform(function () { - var values = []; - var index = 0; - var remaining = 1; - forOf(iterable, false, function (promise) { - var $index = index++; - var alreadyCalled = false; - values.push(undefined); - remaining++; - C.resolve(promise).then(function (value) { - if (alreadyCalled) return; - alreadyCalled = true; - values[$index] = value; - --remaining || resolve(values); - }, reject); - }); - --remaining || resolve(values); - }); - if (result.e) reject(result.v); - return capability.promise; - }, - // 25.4.4.4 Promise.race(iterable) - race: function race(iterable) { - var C = this; - var capability = newPromiseCapability(C); - var reject = capability.reject; - var result = perform(function () { - forOf(iterable, false, function (promise) { - C.resolve(promise).then(capability.resolve, reject); - }); - }); - if (result.e) reject(result.v); - return capability.promise; + try { + options.rmdirSync(p) + } catch (er) { + if (er.code === "ENOENT") + return + if (er.code === "ENOTDIR") + throw originalEr + if (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM") + rmkidsSync(p, options) } -}); +} + +function rmkidsSync (p, options) { + assert(p) + assert(options) + options.readdirSync(p).forEach(function (f) { + rimrafSync(path.join(p, f), options) + }) + + // We only end up here once we got ENOTEMPTY at least once, and + // at this point, we are guaranteed to have removed all the kids. + // So, we know that it won't be ENOENT or ENOTDIR or anything else. + // try really hard to delete stuff on windows, because it has a + // PROFOUNDLY annoying habit of not closing handles promptly when + // files are deleted, resulting in spurious ENOTEMPTY errors. + var retries = isWindows ? 100 : 1 + var i = 0 + do { + var threw = true + try { + var ret = options.rmdirSync(p, options) + threw = false + return ret + } finally { + if (++i < retries && threw) + continue + } + } while (true) +} /***/ }), -/* 207 */ +/* 234 */, +/* 235 */, +/* 236 */, +/* 237 */, +/* 238 */, +/* 239 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var $at = __webpack_require__(199)(true); +var hasFlag = __webpack_require__(221); -// 21.1.3.27 String.prototype[@@iterator]() -__webpack_require__(103)(String, 'String', function (iterated) { - this._t = String(iterated); // target - this._i = 0; // next index -// 21.1.5.2.1 %StringIteratorPrototype%.next() -}, function () { - var O = this._t; - var index = this._i; - var point; - if (index >= O.length) return { value: undefined, done: true }; - point = $at(O, index); - this._i += point.length; - return { value: point, done: false }; -}); +var support = function (level) { + if (level === 0) { + return false; + } + return { + level: level, + hasBasic: true, + has256: level >= 2, + has16m: level >= 3 + }; +}; -/***/ }), -/* 208 */ -/***/ (function(module, exports, __webpack_require__) { +var supportLevel = (function () { + if (hasFlag('no-color') || + hasFlag('no-colors') || + hasFlag('color=false')) { + return 0; + } -"use strict"; -// https://github.com/tc39/proposal-promise-finally + if (hasFlag('color=16m') || + hasFlag('color=full') || + hasFlag('color=truecolor')) { + return 3; + } -var $export = __webpack_require__(41); -var core = __webpack_require__(23); -var global = __webpack_require__(11); -var speciesConstructor = __webpack_require__(108); -var promiseResolve = __webpack_require__(105); + if (hasFlag('color=256')) { + return 2; + } -$export($export.P + $export.R, 'Promise', { 'finally': function (onFinally) { - var C = speciesConstructor(this, core.Promise || global.Promise); - var isFunction = typeof onFinally == 'function'; - return this.then( - isFunction ? function (x) { - return promiseResolve(C, onFinally()).then(function () { return x; }); - } : onFinally, - isFunction ? function (e) { - return promiseResolve(C, onFinally()).then(function () { throw e; }); - } : onFinally - ); -} }); + if (hasFlag('color') || + hasFlag('colors') || + hasFlag('color=true') || + hasFlag('color=always')) { + return 1; + } + if (process.stdout && !process.stdout.isTTY) { + return 0; + } -/***/ }), -/* 209 */ -/***/ (function(module, exports, __webpack_require__) { + if (process.platform === 'win32') { + return 1; + } -"use strict"; + if ('CI' in process.env) { + if ('TRAVIS' in process.env || process.env.CI === 'Travis') { + return 1; + } -// https://github.com/tc39/proposal-promise-try -var $export = __webpack_require__(41); -var newPromiseCapability = __webpack_require__(70); -var perform = __webpack_require__(104); + return 0; + } -$export($export.S, 'Promise', { 'try': function (callbackfn) { - var promiseCapability = newPromiseCapability.f(this); - var result = perform(callbackfn); - (result.e ? promiseCapability.reject : promiseCapability.resolve)(result.v); - return promiseCapability.promise; -} }); + if ('TEAMCITY_VERSION' in process.env) { + return process.env.TEAMCITY_VERSION.match(/^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/) === null ? 0 : 1; + } + if (/^(screen|xterm)-256(?:color)?/.test(process.env.TERM)) { + return 2; + } -/***/ }), -/* 210 */ -/***/ (function(module, exports, __webpack_require__) { + if (/^screen|^xterm|^vt100|color|ansi|cygwin|linux/i.test(process.env.TERM)) { + return 1; + } -__webpack_require__(204); -var global = __webpack_require__(11); -var hide = __webpack_require__(31); -var Iterators = __webpack_require__(35); -var TO_STRING_TAG = __webpack_require__(13)('toStringTag'); + if ('COLORTERM' in process.env) { + return 1; + } -var DOMIterables = ('CSSRuleList,CSSStyleDeclaration,CSSValueList,ClientRectList,DOMRectList,DOMStringList,' + - 'DOMTokenList,DataTransferItemList,FileList,HTMLAllCollection,HTMLCollection,HTMLFormElement,HTMLSelectElement,' + - 'MediaList,MimeTypeArray,NamedNodeMap,NodeList,PaintRequestList,Plugin,PluginArray,SVGLengthList,SVGNumberList,' + - 'SVGPathSegList,SVGPointList,SVGStringList,SVGTransformList,SourceBufferList,StyleSheetList,TextTrackCueList,' + - 'TextTrackList,TouchList').split(','); + if (process.env.TERM === 'dumb') { + return 0; + } -for (var i = 0; i < DOMIterables.length; i++) { - var NAME = DOMIterables[i]; - var Collection = global[NAME]; - var proto = Collection && Collection.prototype; - if (proto && !proto[TO_STRING_TAG]) hide(proto, TO_STRING_TAG, NAME); - Iterators[NAME] = Iterators.Array; + return 0; +})(); + +if (supportLevel === 0 && 'FORCE_COLOR' in process.env) { + supportLevel = 1; } +module.exports = process && support(supportLevel); + + +/***/ }) +/******/ ]); /***/ }), -/* 211 */ -/***/ (function(module, exports, __webpack_require__) { +/* 416 */ +/***/ (function(module, exports) { -/** - * This is the web browser implementation of `debug()`. - * - * Expose `debug()` as the module. - */ +module.exports = require("buffer"); -exports = module.exports = __webpack_require__(112); -exports.log = log; -exports.formatArgs = formatArgs; -exports.save = save; -exports.load = load; -exports.useColors = useColors; -exports.storage = 'undefined' != typeof chrome - && 'undefined' != typeof chrome.storage - ? chrome.storage.local - : localstorage(); +/***/ }), +/* 417 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { -/** - * Colors. +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "validateDependencies", function() { return validateDependencies; }); +/* harmony import */ var _yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(415); +/* harmony import */ var _yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var dedent__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2); +/* harmony import */ var dedent__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(dedent__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(114); +/* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(chalk__WEBPACK_IMPORTED_MODULE_2__); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_3__); +/* harmony import */ var _fs__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(231); +/* harmony import */ var _log__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(220); +/* harmony import */ var _package_json__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(349); +/* harmony import */ var _projects_tree__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(418); +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. */ +// @ts-expect-error published types are useless -exports.colors = [ - '#0000CC', '#0000FF', '#0033CC', '#0033FF', '#0066CC', '#0066FF', '#0099CC', - '#0099FF', '#00CC00', '#00CC33', '#00CC66', '#00CC99', '#00CCCC', '#00CCFF', - '#3300CC', '#3300FF', '#3333CC', '#3333FF', '#3366CC', '#3366FF', '#3399CC', - '#3399FF', '#33CC00', '#33CC33', '#33CC66', '#33CC99', '#33CCCC', '#33CCFF', - '#6600CC', '#6600FF', '#6633CC', '#6633FF', '#66CC00', '#66CC33', '#9900CC', - '#9900FF', '#9933CC', '#9933FF', '#99CC00', '#99CC33', '#CC0000', '#CC0033', - '#CC0066', '#CC0099', '#CC00CC', '#CC00FF', '#CC3300', '#CC3333', '#CC3366', - '#CC3399', '#CC33CC', '#CC33FF', '#CC6600', '#CC6633', '#CC9900', '#CC9933', - '#CCCC00', '#CCCC33', '#FF0000', '#FF0033', '#FF0066', '#FF0099', '#FF00CC', - '#FF00FF', '#FF3300', '#FF3333', '#FF3366', '#FF3399', '#FF33CC', '#FF33FF', - '#FF6600', '#FF6633', '#FF9900', '#FF9933', '#FFCC00', '#FFCC33' -]; -/** - * Currently only WebKit-based Web Inspectors, Firefox >= v31, - * and the Firebug extension (any Firefox version) are known - * to support "%c" CSS customizations. - * - * TODO: add a `localStorage` variable to explicitly enable/disable colors - */ -function useColors() { - // NB: In an Electron preload script, document will be defined but not fully - // initialized. Since we know we're in Chrome, we'll just detect this case - // explicitly - if (typeof window !== 'undefined' && window.process && window.process.type === 'renderer') { - return true; - } - // Internet Explorer and Edge do not support colors. - if (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) { - return false; - } - // is webkit? http://stackoverflow.com/a/16459606/376773 - // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632 - return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) || - // is firebug? http://stackoverflow.com/a/398120/376773 - (typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) || - // is firefox >= v31? - // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages - (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) || - // double check webkit in userAgent just in case we are in a worker - (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)); -} -/** - * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. - */ -exports.formatters.j = function(v) { - try { - return JSON.stringify(v); - } catch (err) { - return '[UnexpectedJSONParseError]: ' + err.message; - } -}; +async function validateDependencies(kbn, yarnLock) { + // look through all of the packages in the yarn.lock file to see if + // we have accidentally installed multiple lodash v4 versions + const lodash4Versions = new Set(); + const lodash4Reqs = new Set(); + + for (const [req, dep] of Object.entries(yarnLock)) { + if (req.startsWith('lodash@') && dep.version.startsWith('4.')) { + lodash4Reqs.add(req); + lodash4Versions.add(dep.version); + } + } // if we find more than one lodash v4 version installed then delete + // lodash v4 requests from the yarn.lock file and prompt the user to + // retry bootstrap so that a single v4 version will be installed + + + if (lodash4Versions.size > 1) { + for (const req of lodash4Reqs) { + delete yarnLock[req]; + } + + await Object(_fs__WEBPACK_IMPORTED_MODULE_4__["writeFile"])(kbn.getAbsolute('yarn.lock'), Object(_yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__["stringify"])(yarnLock), 'utf8'); + _log__WEBPACK_IMPORTED_MODULE_5__["log"].error(dedent__WEBPACK_IMPORTED_MODULE_1___default.a` + + Multiple version of lodash v4 were detected, so they have been removed + from the yarn.lock file. Please rerun yarn kbn bootstrap to coalese the + lodash versions installed. + + If you still see this error when you re-bootstrap then you might need + to force a new dependency to use the latest version of lodash via the + "resolutions" field in package.json. + + If you have questions about this please reach out to the operations team. + + `); + process.exit(1); + } // look through all the dependencies of production packages and production + // dependencies of those packages to determine if we're shipping any versions + // of lodash v3 in the distributable + + + const prodDependencies = kbn.resolveAllProductionDependencies(yarnLock, _log__WEBPACK_IMPORTED_MODULE_5__["log"]); + const lodash3Versions = new Set(); + + for (const dep of prodDependencies.values()) { + if (dep.name === 'lodash' && dep.version.startsWith('3.')) { + lodash3Versions.add(dep.version); + } + } // if any lodash v3 packages were found we abort and tell the user to fix things + + + if (lodash3Versions.size) { + _log__WEBPACK_IMPORTED_MODULE_5__["log"].error(dedent__WEBPACK_IMPORTED_MODULE_1___default.a` + + Due to changes in the yarn.lock file and/or package.json files a version of + lodash 3 is now included in the production dependencies. To reduce the size of + our distributable and especially our front-end bundles we have decided to + prevent adding any new instances of lodash 3. + + Please inspect the changes to yarn.lock or package.json files to identify where + the lodash 3 version is coming from and remove it. -/** - * Colorize log arguments if enabled. - * - * @api public - */ + If you have questions about this please reack out to the operations team. -function formatArgs(args) { - var useColors = this.useColors; + `); + process.exit(1); + } // TODO: remove this once we move into a single package.json + // look through all the package.json files to find packages which have mismatched version ranges - args[0] = (useColors ? '%c' : '') - + this.namespace - + (useColors ? ' %c' : ' ') - + args[0] - + (useColors ? '%c ' : ' ') - + '+' + exports.humanize(this.diff); - if (!useColors) return; + const depRanges = new Map(); - var c = 'color: ' + this.color; - args.splice(1, 0, c, 'color: inherit') + for (const project of kbn.getAllProjects().values()) { + var _kbn$kibanaProject; - // the final "%c" is somewhat tricky, because there could be other - // arguments passed either before or after the %c, so we need to - // figure out the correct index to insert the CSS into - var index = 0; - var lastC = 0; - args[0].replace(/%[a-zA-Z%]/g, function(match) { - if ('%%' === match) return; - index++; - if ('%c' === match) { - // we only are interested in the *last* %c - // (the user may have provided their own) - lastC = index; + // Skip if this is an external plugin + if (project.path.includes(`${(_kbn$kibanaProject = kbn.kibanaProject) === null || _kbn$kibanaProject === void 0 ? void 0 : _kbn$kibanaProject.path}${path__WEBPACK_IMPORTED_MODULE_3__["sep"]}plugins`)) { + continue; } - }); - args.splice(lastC, 0, c); -} + for (const [dep, range] of Object.entries(project.allDependencies)) { + const existingDep = depRanges.get(dep); -/** - * Invokes `console.log()` when available. - * No-op when `console.log` is not a "function". - * - * @api public - */ + if (!existingDep) { + depRanges.set(dep, [{ + range, + projects: [project] + }]); + continue; + } -function log() { - // this hackery is required for IE8/9, where - // the `console.log` function doesn't have 'apply' - return 'object' === typeof console - && console.log - && Function.prototype.apply.call(console.log, console, arguments); -} + const existingRange = existingDep.find(existing => existing.range === range); -/** - * Save `namespaces`. - * - * @param {String} namespaces - * @api private - */ + if (!existingRange) { + existingDep.push({ + range, + projects: [project] + }); + continue; + } -function save(namespaces) { - try { - if (null == namespaces) { - exports.storage.removeItem('debug'); - } else { - exports.storage.debug = namespaces; + existingRange.projects.push(project); } - } catch(e) {} -} + } -/** - * Load `namespaces`. - * - * @return {String} returns the previously persisted debug modes - * @api private - */ + const duplicateRanges = Array.from(depRanges.entries()).filter(([, ranges]) => ranges.length > 1 && !ranges.every(rng => Object(_package_json__WEBPACK_IMPORTED_MODULE_6__["isLinkDependency"])(rng.range))).reduce((acc, [dep, ranges]) => [...acc, dep, ...ranges.map(({ + range, + projects + }) => ` ${range} => ${projects.map(p => p.name).join(', ')}`)], []).join('\n '); -function load() { - var r; - try { - r = exports.storage.debug; - } catch(e) {} + if (duplicateRanges) { + _log__WEBPACK_IMPORTED_MODULE_5__["log"].error(dedent__WEBPACK_IMPORTED_MODULE_1___default.a` - // If debug isn't set in LS, and we're in Electron, try to load $DEBUG - if (!r && typeof process !== 'undefined' && 'env' in process) { - r = process.env.DEBUG; - } + [single_version_dependencies] Multiple version ranges for the same dependency + were found declared across different package.json files. Please consolidate + those to match across all package.json files. Different versions for the + same dependency is not supported. - return r; -} + If you have questions about this please reach out to the operations team. -/** - * Enable namespaces listed in `localStorage.debug` initially. - */ + The conflicting dependencies are: -exports.enable(load()); + ${duplicateRanges} + `); + process.exit(1); + } // look for packages that have the the `kibana.devOnly` flag in their package.json + // and make sure they aren't included in the production dependencies of Kibana -/** - * Localstorage attempts to return the localstorage. - * - * This is necessary because safari throws - * when a user disables cookies/localstorage - * and you attempt to access it. - * - * @return {LocalStorage} - * @api private - */ -function localstorage() { - try { - return window.localStorage; - } catch (e) {} -} + const devOnlyProjectsInProduction = getDevOnlyProductionDepsTree(kbn, 'kibana'); + if (devOnlyProjectsInProduction) { + _log__WEBPACK_IMPORTED_MODULE_5__["log"].error(dedent__WEBPACK_IMPORTED_MODULE_1___default.a` + Some of the packages in the production dependency chain for Kibana and X-Pack are + flagged with "kibana.devOnly" in their package.json. Please check changes made to + packages and their dependencies to ensure they don't end up in production. -/***/ }), -/* 212 */ -/***/ (function(module, exports, __webpack_require__) { + The devOnly dependencies that are being dependend on in production are: -/** - * Detect Electron renderer process, which is node, but we should - * treat as a browser. - */ + ${Object(_projects_tree__WEBPACK_IMPORTED_MODULE_7__["treeToString"])(devOnlyProjectsInProduction).split('\n').join('\n ')} + `); + process.exit(1); + } -if (typeof process === 'undefined' || process.type === 'renderer') { - module.exports = __webpack_require__(211); -} else { - module.exports = __webpack_require__(213); + _log__WEBPACK_IMPORTED_MODULE_5__["log"].success('yarn.lock analysis completed without any issues'); } +function getDevOnlyProductionDepsTree(kbn, projectName) { + const project = kbn.getProject(projectName); + const childProjectNames = [...Object.keys(project.productionDependencies).filter(name => kbn.hasProject(name)), ...(projectName === 'kibana' ? ['x-pack'] : [])]; + const children = childProjectNames.map(n => getDevOnlyProductionDepsTree(kbn, n)).filter(t => !!t); -/***/ }), -/* 213 */ -/***/ (function(module, exports, __webpack_require__) { + if (!children.length && !project.isFlaggedAsDevOnly()) { + return; + } -/** - * Module dependencies. - */ + const tree = { + name: project.isFlaggedAsDevOnly() ? chalk__WEBPACK_IMPORTED_MODULE_2___default.a.red.bold(projectName) : projectName, + children + }; + return tree; +} -var tty = __webpack_require__(79); -var util = __webpack_require__(2); +/***/ }), +/* 418 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { -/** - * This is the Node.js implementation of `debug()`. - * - * Expose `debug()` as the module. +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "renderProjectsTree", function() { return renderProjectsTree; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "treeToString", function() { return treeToString; }); +/* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(114); +/* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(chalk__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_1__); +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. */ -exports = module.exports = __webpack_require__(112); -exports.init = init; -exports.log = log; -exports.formatArgs = formatArgs; -exports.save = save; -exports.load = load; -exports.useColors = useColors; - -/** - * Colors. - */ -exports.colors = [ 6, 2, 3, 4, 5, 1 ]; +const projectKey = Symbol('__project'); +function renderProjectsTree(rootPath, projects) { + const projectsTree = buildProjectsTree(rootPath, projects); + return treeToString(createTreeStructure(projectsTree)); +} +function treeToString(tree) { + return [tree.name].concat(childrenToStrings(tree.children, '')).join('\n'); +} -try { - var supportsColor = __webpack_require__(239); - if (supportsColor && supportsColor.level >= 2) { - exports.colors = [ - 20, 21, 26, 27, 32, 33, 38, 39, 40, 41, 42, 43, 44, 45, 56, 57, 62, 63, 68, - 69, 74, 75, 76, 77, 78, 79, 80, 81, 92, 93, 98, 99, 112, 113, 128, 129, 134, - 135, 148, 149, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, - 172, 173, 178, 179, 184, 185, 196, 197, 198, 199, 200, 201, 202, 203, 204, - 205, 206, 207, 208, 209, 214, 215, 220, 221 - ]; +function childrenToStrings(tree, treePrefix) { + if (tree === undefined) { + return []; } -} catch (err) { - // swallow - we only care if `supports-color` is available; it doesn't have to be. -} -/** - * Build up the default `inspectOpts` object from the environment variables. - * - * $ DEBUG_COLORS=no DEBUG_DEPTH=10 DEBUG_SHOW_HIDDEN=enabled node script.js - */ + let strings = []; + tree.forEach((node, index) => { + const isLastNode = tree.length - 1 === index; + const nodePrefix = isLastNode ? '└── ' : '├── '; + const childPrefix = isLastNode ? ' ' : '│ '; + const childrenPrefix = treePrefix + childPrefix; + strings.push(`${treePrefix}${nodePrefix}${node.name}`); + strings = strings.concat(childrenToStrings(node.children, childrenPrefix)); + }); + return strings; +} -exports.inspectOpts = Object.keys(process.env).filter(function (key) { - return /^debug_/i.test(key); -}).reduce(function (obj, key) { - // camel-case - var prop = key - .substring(6) - .toLowerCase() - .replace(/_([a-z])/g, function (_, k) { return k.toUpperCase() }); +function createTreeStructure(tree) { + let name; + const children = []; - // coerce string value into JS value - var val = process.env[key]; - if (/^(yes|on|true|enabled)$/i.test(val)) val = true; - else if (/^(no|off|false|disabled)$/i.test(val)) val = false; - else if (val === 'null') val = null; - else val = Number(val); + for (const [dir, project] of tree.entries()) { + // This is a leaf node (aka a project) + if (typeof project === 'string') { + name = chalk__WEBPACK_IMPORTED_MODULE_0___default.a.green(project); + continue; + } // If there's only one project and the key indicates it's a leaf node, we + // know that we're at a package folder that contains a package.json, so we + // "inline it" so we don't get unnecessary levels, i.e. we'll just see + // `foo` instead of `foo -> foo`. - obj[prop] = val; - return obj; -}, {}); -/** - * Is stdout a TTY? Colored output is enabled when `true`. - */ + if (project.size === 1 && project.has(projectKey)) { + const projectName = project.get(projectKey); + children.push({ + children: [], + name: dirOrProjectName(dir, projectName) + }); + continue; + } -function useColors() { - return 'colors' in exports.inspectOpts - ? Boolean(exports.inspectOpts.colors) - : tty.isatty(process.stderr.fd); -} + const subtree = createTreeStructure(project); // If the name is specified, we know there's a package at the "root" of the + // subtree itself. -/** - * Map %o to `util.inspect()`, all on a single line. - */ + if (subtree.name !== undefined) { + const projectName = subtree.name; + children.push({ + children: subtree.children, + name: dirOrProjectName(dir, projectName) + }); + continue; + } // Special-case whenever we have one child, so we don't get unnecessary + // folders in the output. E.g. instead of `foo -> bar -> baz` we get + // `foo/bar/baz` instead. -exports.formatters.o = function(v) { - this.inspectOpts.colors = this.useColors; - return util.inspect(v, this.inspectOpts) - .split('\n').map(function(str) { - return str.trim() - }).join(' '); -}; -/** - * Map %o to `util.inspect()`, allowing multiple lines if needed. - */ + if (subtree.children && subtree.children.length === 1) { + const child = subtree.children[0]; + const newName = chalk__WEBPACK_IMPORTED_MODULE_0___default.a.dim(path__WEBPACK_IMPORTED_MODULE_1___default.a.join(dir.toString(), child.name)); + children.push({ + children: child.children, + name: newName + }); + continue; + } -exports.formatters.O = function(v) { - this.inspectOpts.colors = this.useColors; - return util.inspect(v, this.inspectOpts); -}; + children.push({ + children: subtree.children, + name: chalk__WEBPACK_IMPORTED_MODULE_0___default.a.dim(dir.toString()) + }); + } -/** - * Adds ANSI color escape codes if enabled. - * - * @api public - */ + return { + name, + children + }; +} -function formatArgs(args) { - var name = this.namespace; - var useColors = this.useColors; +function dirOrProjectName(dir, projectName) { + return dir === projectName ? chalk__WEBPACK_IMPORTED_MODULE_0___default.a.green(dir) : chalk__WEBPACK_IMPORTED_MODULE_0___default.a`{dim ${dir.toString()} ({reset.green ${projectName}})}`; +} - if (useColors) { - var c = this.color; - var colorCode = '\u001b[3' + (c < 8 ? c : '8;5;' + c); - var prefix = ' ' + colorCode + ';1m' + name + ' ' + '\u001b[0m'; +function buildProjectsTree(rootPath, projects) { + const tree = new Map(); - args[0] = prefix + args[0].split('\n').join('\n' + prefix); - args.push(colorCode + 'm+' + exports.humanize(this.diff) + '\u001b[0m'); - } else { - args[0] = getDate() + name + ' ' + args[0]; + for (const project of projects.values()) { + if (rootPath === project.path) { + tree.set(projectKey, project.name); + } else { + const relativeProjectPath = path__WEBPACK_IMPORTED_MODULE_1___default.a.relative(rootPath, project.path); + addProjectToTree(tree, relativeProjectPath.split(path__WEBPACK_IMPORTED_MODULE_1___default.a.sep), project); + } } + + return tree; } -function getDate() { - if (exports.inspectOpts.hideDate) { - return ''; +function addProjectToTree(tree, pathParts, project) { + if (pathParts.length === 0) { + tree.set(projectKey, project.name); } else { - return new Date().toISOString() + ' '; - } -} + const [currentDir, ...rest] = pathParts; -/** - * Invokes `util.format()` with the specified arguments and writes to stderr. - */ + if (!tree.has(currentDir)) { + tree.set(currentDir, new Map()); + } -function log() { - return process.stderr.write(util.format.apply(util, arguments) + '\n'); + const subtree = tree.get(currentDir); + addProjectToTree(subtree, rest, project); + } } -/** - * Save `namespaces`. - * - * @param {String} namespaces - * @api private - */ +/***/ }), +/* 419 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { -function save(namespaces) { - if (null == namespaces) { - // If you set a process.env field to null or undefined, it gets cast to the - // string 'null' or 'undefined'. Just delete instead. - delete process.env.DEBUG; - } else { - process.env.DEBUG = namespaces; - } -} +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony import */ var _yarn_integrity__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(420); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "yarnIntegrityFileExists", function() { return _yarn_integrity__WEBPACK_IMPORTED_MODULE_0__["yarnIntegrityFileExists"]; }); -/** - * Load `namespaces`. - * - * @return {String} returns the previously persisted debug modes - * @api private - */ +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ensureYarnIntegrityFileExists", function() { return _yarn_integrity__WEBPACK_IMPORTED_MODULE_0__["ensureYarnIntegrityFileExists"]; }); -function load() { - return process.env.DEBUG; -} +/* harmony import */ var _get_cache_folders__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(421); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "getBazelDiskCacheFolder", function() { return _get_cache_folders__WEBPACK_IMPORTED_MODULE_1__["getBazelDiskCacheFolder"]; }); -/** - * Init logic for `debug` instances. - * - * Create a new `inspectOpts` object in case `useColors` is set - * differently for a particular `debug` instance. - */ +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "getBazelRepositoryCacheFolder", function() { return _get_cache_folders__WEBPACK_IMPORTED_MODULE_1__["getBazelRepositoryCacheFolder"]; }); -function init (debug) { - debug.inspectOpts = {}; +/* harmony import */ var _install_tools__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(422); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "isBazelBinAvailable", function() { return _install_tools__WEBPACK_IMPORTED_MODULE_2__["isBazelBinAvailable"]; }); - var keys = Object.keys(exports.inspectOpts); - for (var i = 0; i < keys.length; i++) { - debug.inspectOpts[keys[i]] = exports.inspectOpts[keys[i]]; - } -} +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "installBazelTools", function() { return _install_tools__WEBPACK_IMPORTED_MODULE_2__["installBazelTools"]; }); -/** - * Enable namespaces listed in `process.env.DEBUG` initially. - */ +/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(423); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "runBazel", function() { return _run__WEBPACK_IMPORTED_MODULE_3__["runBazel"]; }); + +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "runIBazel", function() { return _run__WEBPACK_IMPORTED_MODULE_3__["runIBazel"]; }); -exports.enable(load()); +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ -/***/ }), -/* 214 */, -/* 215 */, -/* 216 */, -/* 217 */ -/***/ (function(module, exports, __webpack_require__) { -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. -var pathModule = __webpack_require__(0); -var isWindows = process.platform === 'win32'; -var fs = __webpack_require__(3); -// JavaScript implementation of realpath, ported from node pre-v6 +/***/ }), +/* 420 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { -var DEBUG = process.env.NODE_DEBUG && /fs/.test(process.env.NODE_DEBUG); +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "yarnIntegrityFileExists", function() { return yarnIntegrityFileExists; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ensureYarnIntegrityFileExists", function() { return ensureYarnIntegrityFileExists; }); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _fs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(231); +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ -function rethrow() { - // Only enable in debug mode. A backtrace uses ~1000 bytes of heap space and - // is fairly slow to generate. - var callback; - if (DEBUG) { - var backtrace = new Error; - callback = debugCallback; - } else - callback = missingCallback; - return callback; +async function yarnIntegrityFileExists(nodeModulesPath) { + try { + const nodeModulesRealPath = await Object(_fs__WEBPACK_IMPORTED_MODULE_1__["tryRealpath"])(nodeModulesPath); + const yarnIntegrityFilePath = Object(path__WEBPACK_IMPORTED_MODULE_0__["join"])(nodeModulesRealPath, '.yarn-integrity'); // check if the file already exists - function debugCallback(err) { - if (err) { - backtrace.message = err.message; - err = backtrace; - missingCallback(err); + if (await Object(_fs__WEBPACK_IMPORTED_MODULE_1__["isFile"])(yarnIntegrityFilePath)) { + return true; } + } catch {// no-op } - function missingCallback(err) { - if (err) { - if (process.throwDeprecation) - throw err; // Forgot a callback but don't know where? Use NODE_DEBUG=fs - else if (!process.noDeprecation) { - var msg = 'fs: missing callback ' + (err.stack || err.message); - if (process.traceDeprecation) - console.trace(msg); - else - console.error(msg); - } - } - } + return false; } +async function ensureYarnIntegrityFileExists(nodeModulesPath) { + try { + const nodeModulesRealPath = await Object(_fs__WEBPACK_IMPORTED_MODULE_1__["tryRealpath"])(nodeModulesPath); + const yarnIntegrityFilePath = Object(path__WEBPACK_IMPORTED_MODULE_0__["join"])(nodeModulesRealPath, '.yarn-integrity'); // ensure node_modules folder is created -function maybeCallback(cb) { - return typeof cb === 'function' ? cb : rethrow(); + await Object(_fs__WEBPACK_IMPORTED_MODULE_1__["mkdirp"])(nodeModulesRealPath); // write a blank file in case it doesn't exists + + await Object(_fs__WEBPACK_IMPORTED_MODULE_1__["writeFile"])(yarnIntegrityFilePath, '', { + flag: 'wx' + }); + } catch {// no-op + } } -var normalize = pathModule.normalize; +/***/ }), +/* 421 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { -// Regexp that finds the next partion of a (partial) path -// result is [base_with_slash, base], e.g. ['somedir/', 'somedir'] -if (isWindows) { - var nextPartRe = /(.*?)(?:[\/\\]+|$)/g; -} else { - var nextPartRe = /(.*?)(?:[\/]+|$)/g; -} +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getBazelDiskCacheFolder", function() { return getBazelDiskCacheFolder; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getBazelRepositoryCacheFolder", function() { return getBazelRepositoryCacheFolder; }); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _child_process__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(221); +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ -// Regex to find the device root, including trailing slash. E.g. 'c:\\'. -if (isWindows) { - var splitRootRe = /^(?:[a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/][^\\\/]+)?[\\\/]*/; -} else { - var splitRootRe = /^[\/]*/; + + +async function rawRunBazelInfoRepoCache() { + const { + stdout: bazelRepositoryCachePath + } = await Object(_child_process__WEBPACK_IMPORTED_MODULE_1__["spawn"])('bazel', ['info', 'repository_cache'], { + stdio: 'pipe' + }); + return bazelRepositoryCachePath; } -exports.realpathSync = function realpathSync(p, cache) { - // make p is absolute - p = pathModule.resolve(p); +async function getBazelDiskCacheFolder() { + return Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(Object(path__WEBPACK_IMPORTED_MODULE_0__["dirname"])(await rawRunBazelInfoRepoCache()), 'disk-cache'); +} +async function getBazelRepositoryCacheFolder() { + return await rawRunBazelInfoRepoCache(); +} - if (cache && Object.prototype.hasOwnProperty.call(cache, p)) { - return cache[p]; - } +/***/ }), +/* 422 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { - var original = p, - seenLinks = {}, - knownHard = {}; +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isBazelBinAvailable", function() { return isBazelBinAvailable; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "installBazelTools", function() { return installBazelTools; }); +/* harmony import */ var dedent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2); +/* harmony import */ var dedent__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(dedent__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var _child_process__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(221); +/* harmony import */ var _fs__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(231); +/* harmony import */ var _log__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(220); +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ - // current character position in p - var pos; - // the partial path so far, including a trailing slash if any - var current; - // the partial path without a trailing slash (except when pointing at a root) - var base; - // the partial path scanned in the previous round, with slash - var previous; - start(); - function start() { - // Skip over roots - var m = splitRootRe.exec(p); - pos = m[0].length; - current = m[0]; - base = m[0]; - previous = ''; - // On windows, check that the root exists. On unix there is no need. - if (isWindows && !knownHard[base]) { - fs.lstatSync(base); - knownHard[base] = true; - } - } - // walk down the path, swapping out linked pathparts for their real - // values - // NB: p.length changes. - while (pos < p.length) { - // find the next part - nextPartRe.lastIndex = pos; - var result = nextPartRe.exec(p); - previous = current; - current += result[0]; - base = previous + result[1]; - pos = nextPartRe.lastIndex; - // continue if not a symlink - if (knownHard[base] || (cache && cache[base] === base)) { - continue; - } +async function readBazelToolsVersionFile(repoRootPath, versionFilename) { + const version = (await Object(_fs__WEBPACK_IMPORTED_MODULE_3__["readFile"])(Object(path__WEBPACK_IMPORTED_MODULE_1__["resolve"])(repoRootPath, versionFilename))).toString().split('\n')[0]; - var resolvedLink; - if (cache && Object.prototype.hasOwnProperty.call(cache, base)) { - // some known symbolic link. no need to stat again. - resolvedLink = cache[base]; - } else { - var stat = fs.lstatSync(base); - if (!stat.isSymbolicLink()) { - knownHard[base] = true; - if (cache) cache[base] = base; - continue; - } + if (!version) { + throw new Error(`[bazel_tools] Failed on reading bazel tools versions\n ${versionFilename} file do not contain any version set`); + } - // read the link if it wasn't read before - // dev/ino always return 0 on windows, so skip the check. - var linkTarget = null; - if (!isWindows) { - var id = stat.dev.toString(32) + ':' + stat.ino.toString(32); - if (seenLinks.hasOwnProperty(id)) { - linkTarget = seenLinks[id]; - } - } - if (linkTarget === null) { - fs.statSync(base); - linkTarget = fs.readlinkSync(base); - } - resolvedLink = pathModule.resolve(previous, linkTarget); - // track this, if given a cache. - if (cache) cache[base] = resolvedLink; - if (!isWindows) seenLinks[id] = linkTarget; - } + return version; +} - // resolve the link, then start over - p = pathModule.resolve(resolvedLink, p.slice(pos)); - start(); +async function isBazelBinAvailable() { + try { + await Object(_child_process__WEBPACK_IMPORTED_MODULE_2__["spawn"])('bazel', ['--version'], { + stdio: 'pipe' + }); + return true; + } catch { + return false; } +} - if (cache) cache[original] = p; +async function isBazeliskInstalled(bazeliskVersion) { + try { + const { + stdout: bazeliskPkgInstallStdout + } = await Object(_child_process__WEBPACK_IMPORTED_MODULE_2__["spawn"])('npm', ['ls', '--global', '--parseable', '--long', `@bazel/bazelisk@${bazeliskVersion}`], { + stdio: 'pipe' + }); + return bazeliskPkgInstallStdout.includes(`@bazel/bazelisk@${bazeliskVersion}`); + } catch { + return false; + } +} - return p; -}; +async function tryRemoveBazeliskFromYarnGlobal() { + try { + // Check if Bazelisk is installed on the yarn global scope + const { + stdout: bazeliskPkgInstallStdout + } = await Object(_child_process__WEBPACK_IMPORTED_MODULE_2__["spawn"])('yarn', ['global', 'list'], { + stdio: 'pipe' + }); // Bazelisk was found on yarn global scope so lets remove it + if (bazeliskPkgInstallStdout.includes(`@bazel/bazelisk@`)) { + await Object(_child_process__WEBPACK_IMPORTED_MODULE_2__["spawn"])('yarn', ['global', 'remove', `@bazel/bazelisk`], { + stdio: 'pipe' + }); + _log__WEBPACK_IMPORTED_MODULE_4__["log"].info(`[bazel_tools] bazelisk was installed on Yarn global packages and is now removed`); + return true; + } -exports.realpath = function realpath(p, cache, cb) { - if (typeof cb !== 'function') { - cb = maybeCallback(cache); - cache = null; + return false; + } catch { + return false; } +} - // make p is absolute - p = pathModule.resolve(p); +async function installBazelTools(repoRootPath) { + _log__WEBPACK_IMPORTED_MODULE_4__["log"].debug(`[bazel_tools] reading bazel tools versions from version files`); + const bazeliskVersion = await readBazelToolsVersionFile(repoRootPath, '.bazeliskversion'); + const bazelVersion = await readBazelToolsVersionFile(repoRootPath, '.bazelversion'); // Check what globals are installed - if (cache && Object.prototype.hasOwnProperty.call(cache, p)) { - return process.nextTick(cb.bind(null, null, cache[p])); - } + _log__WEBPACK_IMPORTED_MODULE_4__["log"].debug(`[bazel_tools] verify if bazelisk is installed`); // Check if we need to remove bazelisk from yarn - var original = p, - seenLinks = {}, - knownHard = {}; + await tryRemoveBazeliskFromYarnGlobal(); // Test if bazelisk is already installed in the correct version - // current character position in p - var pos; - // the partial path so far, including a trailing slash if any - var current; - // the partial path without a trailing slash (except when pointing at a root) - var base; - // the partial path scanned in the previous round, with slash - var previous; + const isBazeliskPkgInstalled = await isBazeliskInstalled(bazeliskVersion); // Test if bazel bin is available - start(); + const isBazelBinAlreadyAvailable = await isBazelBinAvailable(); // Install bazelisk if not installed - function start() { - // Skip over roots - var m = splitRootRe.exec(p); - pos = m[0].length; - current = m[0]; - base = m[0]; - previous = ''; + if (!isBazeliskPkgInstalled || !isBazelBinAlreadyAvailable) { + _log__WEBPACK_IMPORTED_MODULE_4__["log"].info(`[bazel_tools] installing Bazel tools`); + _log__WEBPACK_IMPORTED_MODULE_4__["log"].debug(`[bazel_tools] bazelisk is not installed. Installing @bazel/bazelisk@${bazeliskVersion} and bazel@${bazelVersion}`); + await Object(_child_process__WEBPACK_IMPORTED_MODULE_2__["spawn"])('npm', ['install', '--global', `@bazel/bazelisk@${bazeliskVersion}`], { + env: { + USE_BAZEL_VERSION: bazelVersion + }, + stdio: 'pipe' + }); + const isBazelBinAvailableAfterInstall = await isBazelBinAvailable(); - // On windows, check that the root exists. On unix there is no need. - if (isWindows && !knownHard[base]) { - fs.lstat(base, function(err) { - if (err) return cb(err); - knownHard[base] = true; - LOOP(); - }); - } else { - process.nextTick(LOOP); + if (!isBazelBinAvailableAfterInstall) { + throw new Error(dedent__WEBPACK_IMPORTED_MODULE_0___default.a` + [bazel_tools] an error occurred when installing the Bazel tools. Please make sure you have access to npm globally installed modules on your $PATH + `); } } - // walk down the path, swapping out linked pathparts for their real - // values - function LOOP() { - // stop if scanned past end of path - if (pos >= p.length) { - if (cache) cache[original] = p; - return cb(null, p); - } + _log__WEBPACK_IMPORTED_MODULE_4__["log"].success(`[bazel_tools] all bazel tools are correctly installed`); +} - // find the next part - nextPartRe.lastIndex = pos; - var result = nextPartRe.exec(p); - previous = current; - current += result[0]; - base = previous + result[1]; - pos = nextPartRe.lastIndex; +/***/ }), +/* 423 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { - // continue if not a symlink - if (knownHard[base] || (cache && cache[base] === base)) { - return process.nextTick(LOOP); - } +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "runBazel", function() { return runBazel; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "runIBazel", function() { return runIBazel; }); +/* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(114); +/* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(chalk__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var rxjs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(9); +/* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(424); +/* harmony import */ var _kbn_dev_utils_stdio__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(522); +/* harmony import */ var _kbn_dev_utils_stdio__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_kbn_dev_utils_stdio__WEBPACK_IMPORTED_MODULE_3__); +/* harmony import */ var _child_process__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(221); +/* harmony import */ var _log__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(220); +/* harmony import */ var _errors__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(347); +function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } - if (cache && Object.prototype.hasOwnProperty.call(cache, base)) { - // known symbolic link. no need to stat again. - return gotResolvedLink(cache[base]); - } +function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } - return fs.lstat(base, gotStat); - } +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } - function gotStat(err, stat) { - if (err) return cb(err); +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ - // if not a symlink, skip to the next path part - if (!stat.isSymbolicLink()) { - knownHard[base] = true; - if (cache) cache[base] = base; - return process.nextTick(LOOP); - } - // stat & read the link if not read before - // call gotTarget as soon as the link target is known - // dev/ino always return 0 on windows, so skip the check. - if (!isWindows) { - var id = stat.dev.toString(32) + ':' + stat.ino.toString(32); - if (seenLinks.hasOwnProperty(id)) { - return gotTarget(null, seenLinks[id], base); - } - } - fs.stat(base, function(err) { - if (err) return cb(err); - fs.readlink(base, function(err, target) { - if (!isWindows) seenLinks[id] = target; - gotTarget(err, target); - }); - }); - } - function gotTarget(err, target, base) { - if (err) return cb(err); - var resolvedLink = pathModule.resolve(previous, target); - if (cache) cache[base] = resolvedLink; - gotResolvedLink(resolvedLink); - } - function gotResolvedLink(resolvedLink) { - // resolve the link, then start over - p = pathModule.resolve(resolvedLink, p.slice(pos)); - start(); + + +async function runBazelCommandWithRunner(bazelCommandRunner, bazelArgs, offline = false, runOpts = {}) { + // Force logs to pipe in order to control the output of them + const bazelOpts = _objectSpread(_objectSpread({}, runOpts), {}, { + stdio: 'pipe' + }); + + if (offline) { + bazelArgs = [...bazelArgs, '--config=offline']; } -}; + const bazelProc = Object(_child_process__WEBPACK_IMPORTED_MODULE_4__["spawn"])(bazelCommandRunner, bazelArgs, bazelOpts); + const bazelLogs$ = new rxjs__WEBPACK_IMPORTED_MODULE_1__["Subject"](); // Bazel outputs machine readable output into stdout and human readable output goes to stderr. + // Therefore we need to get both. In order to get errors we need to parse the actual text line -/***/ }), -/* 218 */ -/***/ (function(module, exports, __webpack_require__) { + const bazelLogSubscription = rxjs__WEBPACK_IMPORTED_MODULE_1__["merge"](Object(_kbn_dev_utils_stdio__WEBPACK_IMPORTED_MODULE_3__["observeLines"])(bazelProc.stdout).pipe(Object(rxjs_operators__WEBPACK_IMPORTED_MODULE_2__["tap"])(line => _log__WEBPACK_IMPORTED_MODULE_5__["log"].info(`${chalk__WEBPACK_IMPORTED_MODULE_0___default.a.cyan(`[${bazelCommandRunner}]`)} ${line}`))), Object(_kbn_dev_utils_stdio__WEBPACK_IMPORTED_MODULE_3__["observeLines"])(bazelProc.stderr).pipe(Object(rxjs_operators__WEBPACK_IMPORTED_MODULE_2__["tap"])(line => _log__WEBPACK_IMPORTED_MODULE_5__["log"].info(`${chalk__WEBPACK_IMPORTED_MODULE_0___default.a.cyan(`[${bazelCommandRunner}]`)} ${line}`)))).subscribe(bazelLogs$); // Wait for process and logs to finish, unsubscribing in the end -module.exports = globSync -globSync.GlobSync = GlobSync + try { + await bazelProc; + } catch { + throw new _errors__WEBPACK_IMPORTED_MODULE_6__["CliError"](`The bazel command that was running failed to complete.`); + } -var fs = __webpack_require__(3) -var rp = __webpack_require__(114) -var minimatch = __webpack_require__(60) -var Minimatch = minimatch.Minimatch -var Glob = __webpack_require__(75).Glob -var util = __webpack_require__(2) -var path = __webpack_require__(0) -var assert = __webpack_require__(22) -var isAbsolute = __webpack_require__(76) -var common = __webpack_require__(115) -var alphasort = common.alphasort -var alphasorti = common.alphasorti -var setopts = common.setopts -var ownProp = common.ownProp -var childrenIgnored = common.childrenIgnored -var isIgnored = common.isIgnored + await bazelLogs$.toPromise(); + await bazelLogSubscription.unsubscribe(); +} -function globSync (pattern, options) { - if (typeof options === 'function' || arguments.length === 3) - throw new TypeError('callback provided to sync glob\n'+ - 'See: https://github.com/isaacs/node-glob/issues/167') +async function runBazel(bazelArgs, offline = false, runOpts = {}) { + await runBazelCommandWithRunner('bazel', bazelArgs, offline, runOpts); +} +async function runIBazel(bazelArgs, offline = false, runOpts = {}) { + const extendedEnv = _objectSpread({ + IBAZEL_USE_LEGACY_WATCHER: '0' + }, runOpts === null || runOpts === void 0 ? void 0 : runOpts.env); - return new GlobSync(pattern, options).found + await runBazelCommandWithRunner('ibazel', bazelArgs, offline, _objectSpread(_objectSpread({}, runOpts), {}, { + env: extendedEnv + })); } -function GlobSync (pattern, options) { - if (!pattern) - throw new Error('must provide pattern') +/***/ }), +/* 424 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony import */ var _internal_operators_audit__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(425); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "audit", function() { return _internal_operators_audit__WEBPACK_IMPORTED_MODULE_0__["audit"]; }); - if (typeof options === 'function' || arguments.length === 3) - throw new TypeError('callback provided to sync glob\n'+ - 'See: https://github.com/isaacs/node-glob/issues/167') +/* harmony import */ var _internal_operators_auditTime__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(426); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "auditTime", function() { return _internal_operators_auditTime__WEBPACK_IMPORTED_MODULE_1__["auditTime"]; }); - if (!(this instanceof GlobSync)) - return new GlobSync(pattern, options) +/* harmony import */ var _internal_operators_buffer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(427); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buffer", function() { return _internal_operators_buffer__WEBPACK_IMPORTED_MODULE_2__["buffer"]; }); - setopts(this, pattern, options) +/* harmony import */ var _internal_operators_bufferCount__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(428); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "bufferCount", function() { return _internal_operators_bufferCount__WEBPACK_IMPORTED_MODULE_3__["bufferCount"]; }); - if (this.noprocess) - return this +/* harmony import */ var _internal_operators_bufferTime__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(429); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "bufferTime", function() { return _internal_operators_bufferTime__WEBPACK_IMPORTED_MODULE_4__["bufferTime"]; }); - var n = this.minimatch.set.length - this.matches = new Array(n) - for (var i = 0; i < n; i ++) { - this._process(this.minimatch.set[i], i, false) - } - this._finish() -} +/* harmony import */ var _internal_operators_bufferToggle__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(430); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "bufferToggle", function() { return _internal_operators_bufferToggle__WEBPACK_IMPORTED_MODULE_5__["bufferToggle"]; }); -GlobSync.prototype._finish = function () { - assert(this instanceof GlobSync) - if (this.realpath) { - var self = this - this.matches.forEach(function (matchset, index) { - var set = self.matches[index] = Object.create(null) - for (var p in matchset) { - try { - p = self._makeAbs(p) - var real = rp.realpathSync(p, self.realpathCache) - set[real] = true - } catch (er) { - if (er.syscall === 'stat') - set[self._makeAbs(p)] = true - else - throw er - } - } - }) - } - common.finish(this) -} +/* harmony import */ var _internal_operators_bufferWhen__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(431); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "bufferWhen", function() { return _internal_operators_bufferWhen__WEBPACK_IMPORTED_MODULE_6__["bufferWhen"]; }); +/* harmony import */ var _internal_operators_catchError__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(432); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "catchError", function() { return _internal_operators_catchError__WEBPACK_IMPORTED_MODULE_7__["catchError"]; }); -GlobSync.prototype._process = function (pattern, index, inGlobStar) { - assert(this instanceof GlobSync) +/* harmony import */ var _internal_operators_combineAll__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(433); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "combineAll", function() { return _internal_operators_combineAll__WEBPACK_IMPORTED_MODULE_8__["combineAll"]; }); - // Get the first [n] parts of pattern that are all strings. - var n = 0 - while (typeof pattern[n] === 'string') { - n ++ - } - // now n is the index of the first one that is *not* a string. +/* harmony import */ var _internal_operators_combineLatest__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(434); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "combineLatest", function() { return _internal_operators_combineLatest__WEBPACK_IMPORTED_MODULE_9__["combineLatest"]; }); - // See if there's anything else - var prefix - switch (n) { - // if not, then this is rather simple - case pattern.length: - this._processSimple(pattern.join('/'), index) - return +/* harmony import */ var _internal_operators_concat__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(435); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "concat", function() { return _internal_operators_concat__WEBPACK_IMPORTED_MODULE_10__["concat"]; }); - case 0: - // pattern *starts* with some non-trivial item. - // going to readdir(cwd), but not include the prefix in matches. - prefix = null - break +/* harmony import */ var _internal_operators_concatAll__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(81); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "concatAll", function() { return _internal_operators_concatAll__WEBPACK_IMPORTED_MODULE_11__["concatAll"]; }); - default: - // pattern has some string bits in the front. - // whatever it starts with, whether that's 'absolute' like /foo/bar, - // or 'relative' like '../baz' - prefix = pattern.slice(0, n).join('/') - break - } +/* harmony import */ var _internal_operators_concatMap__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(436); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "concatMap", function() { return _internal_operators_concatMap__WEBPACK_IMPORTED_MODULE_12__["concatMap"]; }); - var remain = pattern.slice(n) +/* harmony import */ var _internal_operators_concatMapTo__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(437); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "concatMapTo", function() { return _internal_operators_concatMapTo__WEBPACK_IMPORTED_MODULE_13__["concatMapTo"]; }); - // get the list of entries. - var read - if (prefix === null) - read = '.' - else if (isAbsolute(prefix) || isAbsolute(pattern.join('/'))) { - if (!prefix || !isAbsolute(prefix)) - prefix = '/' + prefix - read = prefix - } else - read = prefix +/* harmony import */ var _internal_operators_count__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(438); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "count", function() { return _internal_operators_count__WEBPACK_IMPORTED_MODULE_14__["count"]; }); - var abs = this._makeAbs(read) +/* harmony import */ var _internal_operators_debounce__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(439); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "debounce", function() { return _internal_operators_debounce__WEBPACK_IMPORTED_MODULE_15__["debounce"]; }); - //if ignored, skip processing - if (childrenIgnored(this, read)) - return +/* harmony import */ var _internal_operators_debounceTime__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(440); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "debounceTime", function() { return _internal_operators_debounceTime__WEBPACK_IMPORTED_MODULE_16__["debounceTime"]; }); - var isGlobStar = remain[0] === minimatch.GLOBSTAR - if (isGlobStar) - this._processGlobStar(prefix, read, abs, remain, index, inGlobStar) - else - this._processReaddir(prefix, read, abs, remain, index, inGlobStar) -} +/* harmony import */ var _internal_operators_defaultIfEmpty__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(441); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "defaultIfEmpty", function() { return _internal_operators_defaultIfEmpty__WEBPACK_IMPORTED_MODULE_17__["defaultIfEmpty"]; }); +/* harmony import */ var _internal_operators_delay__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(442); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "delay", function() { return _internal_operators_delay__WEBPACK_IMPORTED_MODULE_18__["delay"]; }); -GlobSync.prototype._processReaddir = function (prefix, read, abs, remain, index, inGlobStar) { - var entries = this._readdir(abs, inGlobStar) +/* harmony import */ var _internal_operators_delayWhen__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(444); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "delayWhen", function() { return _internal_operators_delayWhen__WEBPACK_IMPORTED_MODULE_19__["delayWhen"]; }); - // if the abs isn't a dir, then nothing can match! - if (!entries) - return +/* harmony import */ var _internal_operators_dematerialize__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(445); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "dematerialize", function() { return _internal_operators_dematerialize__WEBPACK_IMPORTED_MODULE_20__["dematerialize"]; }); - // It will only match dot entries if it starts with a dot, or if - // dot is set. Stuff like @(.foo|.bar) isn't allowed. - var pn = remain[0] - var negate = !!this.minimatch.negate - var rawGlob = pn._glob - var dotOk = this.dot || rawGlob.charAt(0) === '.' +/* harmony import */ var _internal_operators_distinct__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(446); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "distinct", function() { return _internal_operators_distinct__WEBPACK_IMPORTED_MODULE_21__["distinct"]; }); - var matchedEntries = [] - for (var i = 0; i < entries.length; i++) { - var e = entries[i] - if (e.charAt(0) !== '.' || dotOk) { - var m - if (negate && !prefix) { - m = !e.match(pn) - } else { - m = e.match(pn) - } - if (m) - matchedEntries.push(e) - } - } +/* harmony import */ var _internal_operators_distinctUntilChanged__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(447); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "distinctUntilChanged", function() { return _internal_operators_distinctUntilChanged__WEBPACK_IMPORTED_MODULE_22__["distinctUntilChanged"]; }); - var len = matchedEntries.length - // If there are no matched entries, then nothing matches. - if (len === 0) - return +/* harmony import */ var _internal_operators_distinctUntilKeyChanged__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(448); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "distinctUntilKeyChanged", function() { return _internal_operators_distinctUntilKeyChanged__WEBPACK_IMPORTED_MODULE_23__["distinctUntilKeyChanged"]; }); - // if this is the last remaining pattern bit, then no need for - // an additional stat *unless* the user has specified mark or - // stat explicitly. We know they exist, since readdir returned - // them. +/* harmony import */ var _internal_operators_elementAt__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(449); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "elementAt", function() { return _internal_operators_elementAt__WEBPACK_IMPORTED_MODULE_24__["elementAt"]; }); - if (remain.length === 1 && !this.mark && !this.stat) { - if (!this.matches[index]) - this.matches[index] = Object.create(null) +/* harmony import */ var _internal_operators_endWith__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(452); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "endWith", function() { return _internal_operators_endWith__WEBPACK_IMPORTED_MODULE_25__["endWith"]; }); - for (var i = 0; i < len; i ++) { - var e = matchedEntries[i] - if (prefix) { - if (prefix.slice(-1) !== '/') - e = prefix + '/' + e - else - e = prefix + e - } +/* harmony import */ var _internal_operators_every__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(453); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "every", function() { return _internal_operators_every__WEBPACK_IMPORTED_MODULE_26__["every"]; }); - if (e.charAt(0) === '/' && !this.nomount) { - e = path.join(this.root, e) - } - this._emitMatch(index, e) - } - // This was the last one, and no stats were needed - return - } +/* harmony import */ var _internal_operators_exhaust__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(454); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "exhaust", function() { return _internal_operators_exhaust__WEBPACK_IMPORTED_MODULE_27__["exhaust"]; }); - // now test all matched entries as stand-ins for that part - // of the pattern. - remain.shift() - for (var i = 0; i < len; i ++) { - var e = matchedEntries[i] - var newPattern - if (prefix) - newPattern = [prefix, e] - else - newPattern = [e] - this._process(newPattern.concat(remain), index, inGlobStar) - } -} +/* harmony import */ var _internal_operators_exhaustMap__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(455); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "exhaustMap", function() { return _internal_operators_exhaustMap__WEBPACK_IMPORTED_MODULE_28__["exhaustMap"]; }); +/* harmony import */ var _internal_operators_expand__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(456); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "expand", function() { return _internal_operators_expand__WEBPACK_IMPORTED_MODULE_29__["expand"]; }); -GlobSync.prototype._emitMatch = function (index, e) { - if (isIgnored(this, e)) - return +/* harmony import */ var _internal_operators_filter__WEBPACK_IMPORTED_MODULE_30__ = __webpack_require__(106); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "filter", function() { return _internal_operators_filter__WEBPACK_IMPORTED_MODULE_30__["filter"]; }); - var abs = this._makeAbs(e) +/* harmony import */ var _internal_operators_finalize__WEBPACK_IMPORTED_MODULE_31__ = __webpack_require__(457); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "finalize", function() { return _internal_operators_finalize__WEBPACK_IMPORTED_MODULE_31__["finalize"]; }); - if (this.mark) - e = this._mark(e) +/* harmony import */ var _internal_operators_find__WEBPACK_IMPORTED_MODULE_32__ = __webpack_require__(458); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "find", function() { return _internal_operators_find__WEBPACK_IMPORTED_MODULE_32__["find"]; }); - if (this.absolute) { - e = abs - } +/* harmony import */ var _internal_operators_findIndex__WEBPACK_IMPORTED_MODULE_33__ = __webpack_require__(459); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "findIndex", function() { return _internal_operators_findIndex__WEBPACK_IMPORTED_MODULE_33__["findIndex"]; }); - if (this.matches[index][e]) - return +/* harmony import */ var _internal_operators_first__WEBPACK_IMPORTED_MODULE_34__ = __webpack_require__(460); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "first", function() { return _internal_operators_first__WEBPACK_IMPORTED_MODULE_34__["first"]; }); - if (this.nodir) { - var c = this.cache[abs] - if (c === 'DIR' || Array.isArray(c)) - return - } +/* harmony import */ var _internal_operators_groupBy__WEBPACK_IMPORTED_MODULE_35__ = __webpack_require__(32); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "groupBy", function() { return _internal_operators_groupBy__WEBPACK_IMPORTED_MODULE_35__["groupBy"]; }); - this.matches[index][e] = true +/* harmony import */ var _internal_operators_ignoreElements__WEBPACK_IMPORTED_MODULE_36__ = __webpack_require__(461); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ignoreElements", function() { return _internal_operators_ignoreElements__WEBPACK_IMPORTED_MODULE_36__["ignoreElements"]; }); - if (this.stat) - this._stat(e) -} +/* harmony import */ var _internal_operators_isEmpty__WEBPACK_IMPORTED_MODULE_37__ = __webpack_require__(462); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "isEmpty", function() { return _internal_operators_isEmpty__WEBPACK_IMPORTED_MODULE_37__["isEmpty"]; }); +/* harmony import */ var _internal_operators_last__WEBPACK_IMPORTED_MODULE_38__ = __webpack_require__(463); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "last", function() { return _internal_operators_last__WEBPACK_IMPORTED_MODULE_38__["last"]; }); -GlobSync.prototype._readdirInGlobStar = function (abs) { - // follow all symlinked directories forever - // just proceed as if this is a non-globstar situation - if (this.follow) - return this._readdir(abs, false) +/* harmony import */ var _internal_operators_map__WEBPACK_IMPORTED_MODULE_39__ = __webpack_require__(67); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "map", function() { return _internal_operators_map__WEBPACK_IMPORTED_MODULE_39__["map"]; }); - var entries - var lstat - var stat - try { - lstat = fs.lstatSync(abs) - } catch (er) { - if (er.code === 'ENOENT') { - // lstat failed, doesn't exist - return null - } - } +/* harmony import */ var _internal_operators_mapTo__WEBPACK_IMPORTED_MODULE_40__ = __webpack_require__(465); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "mapTo", function() { return _internal_operators_mapTo__WEBPACK_IMPORTED_MODULE_40__["mapTo"]; }); - var isSym = lstat && lstat.isSymbolicLink() - this.symlinks[abs] = isSym +/* harmony import */ var _internal_operators_materialize__WEBPACK_IMPORTED_MODULE_41__ = __webpack_require__(466); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "materialize", function() { return _internal_operators_materialize__WEBPACK_IMPORTED_MODULE_41__["materialize"]; }); - // If it's not a symlink or a dir, then it's definitely a regular file. - // don't bother doing a readdir in that case. - if (!isSym && lstat && !lstat.isDirectory()) - this.cache[abs] = 'FILE' - else - entries = this._readdir(abs, false) +/* harmony import */ var _internal_operators_max__WEBPACK_IMPORTED_MODULE_42__ = __webpack_require__(467); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "max", function() { return _internal_operators_max__WEBPACK_IMPORTED_MODULE_42__["max"]; }); - return entries -} +/* harmony import */ var _internal_operators_merge__WEBPACK_IMPORTED_MODULE_43__ = __webpack_require__(470); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "merge", function() { return _internal_operators_merge__WEBPACK_IMPORTED_MODULE_43__["merge"]; }); -GlobSync.prototype._readdir = function (abs, inGlobStar) { - var entries +/* harmony import */ var _internal_operators_mergeAll__WEBPACK_IMPORTED_MODULE_44__ = __webpack_require__(82); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "mergeAll", function() { return _internal_operators_mergeAll__WEBPACK_IMPORTED_MODULE_44__["mergeAll"]; }); - if (inGlobStar && !ownProp(this.symlinks, abs)) - return this._readdirInGlobStar(abs) +/* harmony import */ var _internal_operators_mergeMap__WEBPACK_IMPORTED_MODULE_45__ = __webpack_require__(83); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "mergeMap", function() { return _internal_operators_mergeMap__WEBPACK_IMPORTED_MODULE_45__["mergeMap"]; }); - if (ownProp(this.cache, abs)) { - var c = this.cache[abs] - if (!c || c === 'FILE') - return null +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "flatMap", function() { return _internal_operators_mergeMap__WEBPACK_IMPORTED_MODULE_45__["flatMap"]; }); - if (Array.isArray(c)) - return c - } +/* harmony import */ var _internal_operators_mergeMapTo__WEBPACK_IMPORTED_MODULE_46__ = __webpack_require__(471); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "mergeMapTo", function() { return _internal_operators_mergeMapTo__WEBPACK_IMPORTED_MODULE_46__["mergeMapTo"]; }); - try { - return this._readdirEntries(abs, fs.readdirSync(abs)) - } catch (er) { - this._readdirError(abs, er) - return null - } -} +/* harmony import */ var _internal_operators_mergeScan__WEBPACK_IMPORTED_MODULE_47__ = __webpack_require__(472); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "mergeScan", function() { return _internal_operators_mergeScan__WEBPACK_IMPORTED_MODULE_47__["mergeScan"]; }); -GlobSync.prototype._readdirEntries = function (abs, entries) { - // if we haven't asked to stat everything, then just - // assume that everything in there exists, so we can avoid - // having to stat it a second time. - if (!this.mark && !this.stat) { - for (var i = 0; i < entries.length; i ++) { - var e = entries[i] - if (abs === '/') - e = abs + e - else - e = abs + '/' + e - this.cache[e] = true - } - } +/* harmony import */ var _internal_operators_min__WEBPACK_IMPORTED_MODULE_48__ = __webpack_require__(473); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "min", function() { return _internal_operators_min__WEBPACK_IMPORTED_MODULE_48__["min"]; }); - this.cache[abs] = entries +/* harmony import */ var _internal_operators_multicast__WEBPACK_IMPORTED_MODULE_49__ = __webpack_require__(474); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "multicast", function() { return _internal_operators_multicast__WEBPACK_IMPORTED_MODULE_49__["multicast"]; }); - // mark and cache dir-ness - return entries -} +/* harmony import */ var _internal_operators_observeOn__WEBPACK_IMPORTED_MODULE_50__ = __webpack_require__(42); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "observeOn", function() { return _internal_operators_observeOn__WEBPACK_IMPORTED_MODULE_50__["observeOn"]; }); -GlobSync.prototype._readdirError = function (f, er) { - // handle errors, and cache the information - switch (er.code) { - case 'ENOTSUP': // https://github.com/isaacs/node-glob/issues/205 - case 'ENOTDIR': // totally normal. means it *does* exist. - var abs = this._makeAbs(f) - this.cache[abs] = 'FILE' - if (abs === this.cwdAbs) { - var error = new Error(er.code + ' invalid cwd ' + this.cwd) - error.path = this.cwd - error.code = er.code - throw error - } - break +/* harmony import */ var _internal_operators_onErrorResumeNext__WEBPACK_IMPORTED_MODULE_51__ = __webpack_require__(475); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "onErrorResumeNext", function() { return _internal_operators_onErrorResumeNext__WEBPACK_IMPORTED_MODULE_51__["onErrorResumeNext"]; }); - case 'ENOENT': // not terribly unusual - case 'ELOOP': - case 'ENAMETOOLONG': - case 'UNKNOWN': - this.cache[this._makeAbs(f)] = false - break +/* harmony import */ var _internal_operators_pairwise__WEBPACK_IMPORTED_MODULE_52__ = __webpack_require__(476); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "pairwise", function() { return _internal_operators_pairwise__WEBPACK_IMPORTED_MODULE_52__["pairwise"]; }); - default: // some unusual error. Treat as failure. - this.cache[this._makeAbs(f)] = false - if (this.strict) - throw er - if (!this.silent) - console.error('glob error', er) - break - } -} +/* harmony import */ var _internal_operators_partition__WEBPACK_IMPORTED_MODULE_53__ = __webpack_require__(477); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "partition", function() { return _internal_operators_partition__WEBPACK_IMPORTED_MODULE_53__["partition"]; }); -GlobSync.prototype._processGlobStar = function (prefix, read, abs, remain, index, inGlobStar) { +/* harmony import */ var _internal_operators_pluck__WEBPACK_IMPORTED_MODULE_54__ = __webpack_require__(478); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "pluck", function() { return _internal_operators_pluck__WEBPACK_IMPORTED_MODULE_54__["pluck"]; }); - var entries = this._readdir(abs, inGlobStar) +/* harmony import */ var _internal_operators_publish__WEBPACK_IMPORTED_MODULE_55__ = __webpack_require__(479); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "publish", function() { return _internal_operators_publish__WEBPACK_IMPORTED_MODULE_55__["publish"]; }); - // no entries means not a dir, so it can never have matches - // foo.txt/** doesn't match foo.txt - if (!entries) - return +/* harmony import */ var _internal_operators_publishBehavior__WEBPACK_IMPORTED_MODULE_56__ = __webpack_require__(480); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "publishBehavior", function() { return _internal_operators_publishBehavior__WEBPACK_IMPORTED_MODULE_56__["publishBehavior"]; }); - // test without the globstar, and with every child both below - // and replacing the globstar. - var remainWithoutGlobStar = remain.slice(1) - var gspref = prefix ? [ prefix ] : [] - var noGlobStar = gspref.concat(remainWithoutGlobStar) +/* harmony import */ var _internal_operators_publishLast__WEBPACK_IMPORTED_MODULE_57__ = __webpack_require__(481); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "publishLast", function() { return _internal_operators_publishLast__WEBPACK_IMPORTED_MODULE_57__["publishLast"]; }); - // the noGlobStar pattern exits the inGlobStar state - this._process(noGlobStar, index, false) +/* harmony import */ var _internal_operators_publishReplay__WEBPACK_IMPORTED_MODULE_58__ = __webpack_require__(482); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "publishReplay", function() { return _internal_operators_publishReplay__WEBPACK_IMPORTED_MODULE_58__["publishReplay"]; }); - var len = entries.length - var isSym = this.symlinks[abs] +/* harmony import */ var _internal_operators_race__WEBPACK_IMPORTED_MODULE_59__ = __webpack_require__(483); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "race", function() { return _internal_operators_race__WEBPACK_IMPORTED_MODULE_59__["race"]; }); - // If it's a symlink, and we're in a globstar, then stop - if (isSym && inGlobStar) - return +/* harmony import */ var _internal_operators_reduce__WEBPACK_IMPORTED_MODULE_60__ = __webpack_require__(468); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "reduce", function() { return _internal_operators_reduce__WEBPACK_IMPORTED_MODULE_60__["reduce"]; }); - for (var i = 0; i < len; i++) { - var e = entries[i] - if (e.charAt(0) === '.' && !this.dot) - continue +/* harmony import */ var _internal_operators_repeat__WEBPACK_IMPORTED_MODULE_61__ = __webpack_require__(484); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "repeat", function() { return _internal_operators_repeat__WEBPACK_IMPORTED_MODULE_61__["repeat"]; }); - // these two cases enter the inGlobStar state - var instead = gspref.concat(entries[i], remainWithoutGlobStar) - this._process(instead, index, true) +/* harmony import */ var _internal_operators_repeatWhen__WEBPACK_IMPORTED_MODULE_62__ = __webpack_require__(485); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "repeatWhen", function() { return _internal_operators_repeatWhen__WEBPACK_IMPORTED_MODULE_62__["repeatWhen"]; }); - var below = gspref.concat(entries[i], remain) - this._process(below, index, true) - } -} +/* harmony import */ var _internal_operators_retry__WEBPACK_IMPORTED_MODULE_63__ = __webpack_require__(486); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "retry", function() { return _internal_operators_retry__WEBPACK_IMPORTED_MODULE_63__["retry"]; }); -GlobSync.prototype._processSimple = function (prefix, index) { - // XXX review this. Shouldn't it be doing the mounting etc - // before doing stat? kinda weird? - var exists = this._stat(prefix) +/* harmony import */ var _internal_operators_retryWhen__WEBPACK_IMPORTED_MODULE_64__ = __webpack_require__(487); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "retryWhen", function() { return _internal_operators_retryWhen__WEBPACK_IMPORTED_MODULE_64__["retryWhen"]; }); - if (!this.matches[index]) - this.matches[index] = Object.create(null) +/* harmony import */ var _internal_operators_refCount__WEBPACK_IMPORTED_MODULE_65__ = __webpack_require__(31); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "refCount", function() { return _internal_operators_refCount__WEBPACK_IMPORTED_MODULE_65__["refCount"]; }); - // If it doesn't exist, then just mark the lack of results - if (!exists) - return +/* harmony import */ var _internal_operators_sample__WEBPACK_IMPORTED_MODULE_66__ = __webpack_require__(488); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "sample", function() { return _internal_operators_sample__WEBPACK_IMPORTED_MODULE_66__["sample"]; }); - if (prefix && isAbsolute(prefix) && !this.nomount) { - var trail = /[\/\\]$/.test(prefix) - if (prefix.charAt(0) === '/') { - prefix = path.join(this.root, prefix) - } else { - prefix = path.resolve(this.root, prefix) - if (trail) - prefix += '/' - } - } +/* harmony import */ var _internal_operators_sampleTime__WEBPACK_IMPORTED_MODULE_67__ = __webpack_require__(489); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "sampleTime", function() { return _internal_operators_sampleTime__WEBPACK_IMPORTED_MODULE_67__["sampleTime"]; }); - if (process.platform === 'win32') - prefix = prefix.replace(/\\/g, '/') +/* harmony import */ var _internal_operators_scan__WEBPACK_IMPORTED_MODULE_68__ = __webpack_require__(469); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "scan", function() { return _internal_operators_scan__WEBPACK_IMPORTED_MODULE_68__["scan"]; }); - // Mark this as a match - this._emitMatch(index, prefix) -} +/* harmony import */ var _internal_operators_sequenceEqual__WEBPACK_IMPORTED_MODULE_69__ = __webpack_require__(490); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "sequenceEqual", function() { return _internal_operators_sequenceEqual__WEBPACK_IMPORTED_MODULE_69__["sequenceEqual"]; }); -// Returns either 'DIR', 'FILE', or false -GlobSync.prototype._stat = function (f) { - var abs = this._makeAbs(f) - var needDir = f.slice(-1) === '/' +/* harmony import */ var _internal_operators_share__WEBPACK_IMPORTED_MODULE_70__ = __webpack_require__(491); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "share", function() { return _internal_operators_share__WEBPACK_IMPORTED_MODULE_70__["share"]; }); - if (f.length > this.maxLength) - return false +/* harmony import */ var _internal_operators_shareReplay__WEBPACK_IMPORTED_MODULE_71__ = __webpack_require__(492); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "shareReplay", function() { return _internal_operators_shareReplay__WEBPACK_IMPORTED_MODULE_71__["shareReplay"]; }); - if (!this.stat && ownProp(this.cache, abs)) { - var c = this.cache[abs] +/* harmony import */ var _internal_operators_single__WEBPACK_IMPORTED_MODULE_72__ = __webpack_require__(493); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "single", function() { return _internal_operators_single__WEBPACK_IMPORTED_MODULE_72__["single"]; }); - if (Array.isArray(c)) - c = 'DIR' +/* harmony import */ var _internal_operators_skip__WEBPACK_IMPORTED_MODULE_73__ = __webpack_require__(494); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "skip", function() { return _internal_operators_skip__WEBPACK_IMPORTED_MODULE_73__["skip"]; }); - // It exists, but maybe not how we need it - if (!needDir || c === 'DIR') - return c +/* harmony import */ var _internal_operators_skipLast__WEBPACK_IMPORTED_MODULE_74__ = __webpack_require__(495); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "skipLast", function() { return _internal_operators_skipLast__WEBPACK_IMPORTED_MODULE_74__["skipLast"]; }); - if (needDir && c === 'FILE') - return false +/* harmony import */ var _internal_operators_skipUntil__WEBPACK_IMPORTED_MODULE_75__ = __webpack_require__(496); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "skipUntil", function() { return _internal_operators_skipUntil__WEBPACK_IMPORTED_MODULE_75__["skipUntil"]; }); - // otherwise we have to stat, because maybe c=true - // if we know it exists, but not what it is. - } +/* harmony import */ var _internal_operators_skipWhile__WEBPACK_IMPORTED_MODULE_76__ = __webpack_require__(497); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "skipWhile", function() { return _internal_operators_skipWhile__WEBPACK_IMPORTED_MODULE_76__["skipWhile"]; }); - var exists - var stat = this.statCache[abs] - if (!stat) { - var lstat - try { - lstat = fs.lstatSync(abs) - } catch (er) { - if (er && (er.code === 'ENOENT' || er.code === 'ENOTDIR')) { - this.statCache[abs] = false - return false - } - } +/* harmony import */ var _internal_operators_startWith__WEBPACK_IMPORTED_MODULE_77__ = __webpack_require__(498); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "startWith", function() { return _internal_operators_startWith__WEBPACK_IMPORTED_MODULE_77__["startWith"]; }); - if (lstat && lstat.isSymbolicLink()) { - try { - stat = fs.statSync(abs) - } catch (er) { - stat = lstat - } - } else { - stat = lstat - } - } +/* harmony import */ var _internal_operators_subscribeOn__WEBPACK_IMPORTED_MODULE_78__ = __webpack_require__(499); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "subscribeOn", function() { return _internal_operators_subscribeOn__WEBPACK_IMPORTED_MODULE_78__["subscribeOn"]; }); - this.statCache[abs] = stat +/* harmony import */ var _internal_operators_switchAll__WEBPACK_IMPORTED_MODULE_79__ = __webpack_require__(501); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "switchAll", function() { return _internal_operators_switchAll__WEBPACK_IMPORTED_MODULE_79__["switchAll"]; }); - var c = true - if (stat) - c = stat.isDirectory() ? 'DIR' : 'FILE' +/* harmony import */ var _internal_operators_switchMap__WEBPACK_IMPORTED_MODULE_80__ = __webpack_require__(502); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "switchMap", function() { return _internal_operators_switchMap__WEBPACK_IMPORTED_MODULE_80__["switchMap"]; }); - this.cache[abs] = this.cache[abs] || c +/* harmony import */ var _internal_operators_switchMapTo__WEBPACK_IMPORTED_MODULE_81__ = __webpack_require__(503); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "switchMapTo", function() { return _internal_operators_switchMapTo__WEBPACK_IMPORTED_MODULE_81__["switchMapTo"]; }); - if (needDir && c === 'FILE') - return false +/* harmony import */ var _internal_operators_take__WEBPACK_IMPORTED_MODULE_82__ = __webpack_require__(451); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "take", function() { return _internal_operators_take__WEBPACK_IMPORTED_MODULE_82__["take"]; }); - return c -} +/* harmony import */ var _internal_operators_takeLast__WEBPACK_IMPORTED_MODULE_83__ = __webpack_require__(464); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "takeLast", function() { return _internal_operators_takeLast__WEBPACK_IMPORTED_MODULE_83__["takeLast"]; }); -GlobSync.prototype._mark = function (p) { - return common.mark(this, p) -} +/* harmony import */ var _internal_operators_takeUntil__WEBPACK_IMPORTED_MODULE_84__ = __webpack_require__(504); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "takeUntil", function() { return _internal_operators_takeUntil__WEBPACK_IMPORTED_MODULE_84__["takeUntil"]; }); -GlobSync.prototype._makeAbs = function (f) { - return common.makeAbs(this, f) -} +/* harmony import */ var _internal_operators_takeWhile__WEBPACK_IMPORTED_MODULE_85__ = __webpack_require__(505); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "takeWhile", function() { return _internal_operators_takeWhile__WEBPACK_IMPORTED_MODULE_85__["takeWhile"]; }); +/* harmony import */ var _internal_operators_tap__WEBPACK_IMPORTED_MODULE_86__ = __webpack_require__(506); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "tap", function() { return _internal_operators_tap__WEBPACK_IMPORTED_MODULE_86__["tap"]; }); -/***/ }), -/* 219 */, -/* 220 */, -/* 221 */ -/***/ (function(module, exports, __webpack_require__) { +/* harmony import */ var _internal_operators_throttle__WEBPACK_IMPORTED_MODULE_87__ = __webpack_require__(507); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "throttle", function() { return _internal_operators_throttle__WEBPACK_IMPORTED_MODULE_87__["throttle"]; }); -"use strict"; +/* harmony import */ var _internal_operators_throttleTime__WEBPACK_IMPORTED_MODULE_88__ = __webpack_require__(508); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "throttleTime", function() { return _internal_operators_throttleTime__WEBPACK_IMPORTED_MODULE_88__["throttleTime"]; }); -module.exports = function (flag, argv) { - argv = argv || process.argv; +/* harmony import */ var _internal_operators_throwIfEmpty__WEBPACK_IMPORTED_MODULE_89__ = __webpack_require__(450); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "throwIfEmpty", function() { return _internal_operators_throwIfEmpty__WEBPACK_IMPORTED_MODULE_89__["throwIfEmpty"]; }); - var terminatorPos = argv.indexOf('--'); - var prefix = /^--/.test(flag) ? '' : '--'; - var pos = argv.indexOf(prefix + flag); +/* harmony import */ var _internal_operators_timeInterval__WEBPACK_IMPORTED_MODULE_90__ = __webpack_require__(509); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "timeInterval", function() { return _internal_operators_timeInterval__WEBPACK_IMPORTED_MODULE_90__["timeInterval"]; }); - return pos !== -1 && (terminatorPos !== -1 ? pos < terminatorPos : true); -}; +/* harmony import */ var _internal_operators_timeout__WEBPACK_IMPORTED_MODULE_91__ = __webpack_require__(510); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "timeout", function() { return _internal_operators_timeout__WEBPACK_IMPORTED_MODULE_91__["timeout"]; }); +/* harmony import */ var _internal_operators_timeoutWith__WEBPACK_IMPORTED_MODULE_92__ = __webpack_require__(511); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "timeoutWith", function() { return _internal_operators_timeoutWith__WEBPACK_IMPORTED_MODULE_92__["timeoutWith"]; }); -/***/ }), -/* 222 */, -/* 223 */ -/***/ (function(module, exports, __webpack_require__) { +/* harmony import */ var _internal_operators_timestamp__WEBPACK_IMPORTED_MODULE_93__ = __webpack_require__(512); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "timestamp", function() { return _internal_operators_timestamp__WEBPACK_IMPORTED_MODULE_93__["timestamp"]; }); -var wrappy = __webpack_require__(123) -var reqs = Object.create(null) -var once = __webpack_require__(61) +/* harmony import */ var _internal_operators_toArray__WEBPACK_IMPORTED_MODULE_94__ = __webpack_require__(513); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "toArray", function() { return _internal_operators_toArray__WEBPACK_IMPORTED_MODULE_94__["toArray"]; }); -module.exports = wrappy(inflight) +/* harmony import */ var _internal_operators_window__WEBPACK_IMPORTED_MODULE_95__ = __webpack_require__(514); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "window", function() { return _internal_operators_window__WEBPACK_IMPORTED_MODULE_95__["window"]; }); -function inflight (key, cb) { - if (reqs[key]) { - reqs[key].push(cb) - return null - } else { - reqs[key] = [cb] - return makeres(key) - } -} +/* harmony import */ var _internal_operators_windowCount__WEBPACK_IMPORTED_MODULE_96__ = __webpack_require__(515); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowCount", function() { return _internal_operators_windowCount__WEBPACK_IMPORTED_MODULE_96__["windowCount"]; }); -function makeres (key) { - return once(function RES () { - var cbs = reqs[key] - var len = cbs.length - var args = slice(arguments) +/* harmony import */ var _internal_operators_windowTime__WEBPACK_IMPORTED_MODULE_97__ = __webpack_require__(516); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowTime", function() { return _internal_operators_windowTime__WEBPACK_IMPORTED_MODULE_97__["windowTime"]; }); + +/* harmony import */ var _internal_operators_windowToggle__WEBPACK_IMPORTED_MODULE_98__ = __webpack_require__(517); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowToggle", function() { return _internal_operators_windowToggle__WEBPACK_IMPORTED_MODULE_98__["windowToggle"]; }); - // XXX It's somewhat ambiguous whether a new callback added in this - // pass should be queued for later execution if something in the - // list of callbacks throws, or if it should just be discarded. - // However, it's such an edge case that it hardly matters, and either - // choice is likely as surprising as the other. - // As it happens, we do go ahead and schedule it for later execution. - try { - for (var i = 0; i < len; i++) { - cbs[i].apply(null, args) - } - } finally { - if (cbs.length > len) { - // added more in the interim. - // de-zalgo, just in case, but don't call again. - cbs.splice(0, len) - process.nextTick(function () { - RES.apply(null, args) - }) - } else { - delete reqs[key] - } - } - }) -} +/* harmony import */ var _internal_operators_windowWhen__WEBPACK_IMPORTED_MODULE_99__ = __webpack_require__(518); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowWhen", function() { return _internal_operators_windowWhen__WEBPACK_IMPORTED_MODULE_99__["windowWhen"]; }); -function slice (args) { - var length = args.length - var array = [] +/* harmony import */ var _internal_operators_withLatestFrom__WEBPACK_IMPORTED_MODULE_100__ = __webpack_require__(519); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "withLatestFrom", function() { return _internal_operators_withLatestFrom__WEBPACK_IMPORTED_MODULE_100__["withLatestFrom"]; }); - for (var i = 0; i < length; i++) array[i] = args[i] - return array -} +/* harmony import */ var _internal_operators_zip__WEBPACK_IMPORTED_MODULE_101__ = __webpack_require__(520); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "zip", function() { return _internal_operators_zip__WEBPACK_IMPORTED_MODULE_101__["zip"]; }); +/* harmony import */ var _internal_operators_zipAll__WEBPACK_IMPORTED_MODULE_102__ = __webpack_require__(521); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "zipAll", function() { return _internal_operators_zipAll__WEBPACK_IMPORTED_MODULE_102__["zipAll"]; }); + +/** PURE_IMPORTS_START PURE_IMPORTS_END */ -/***/ }), -/* 224 */ -/***/ (function(module, exports) { -if (typeof Object.create === 'function') { - // implementation from standard node.js 'util' module - module.exports = function inherits(ctor, superCtor) { - ctor.super_ = superCtor - ctor.prototype = Object.create(superCtor.prototype, { - constructor: { - value: ctor, - enumerable: false, - writable: true, - configurable: true - } - }); - }; -} else { - // old school shim for old browsers - module.exports = function inherits(ctor, superCtor) { - ctor.super_ = superCtor - var TempCtor = function () {} - TempCtor.prototype = superCtor.prototype - ctor.prototype = new TempCtor() - ctor.prototype.constructor = ctor - } -} -/***/ }), -/* 225 */, -/* 226 */, -/* 227 */ -/***/ (function(module, exports, __webpack_require__) { -// @flow -/*:: -declare var __webpack_require__: mixed; -*/ -module.exports = typeof __webpack_require__ !== "undefined"; -/***/ }), -/* 228 */, -/* 229 */ -/***/ (function(module, exports) { -/** - * Helpers. - */ -var s = 1000; -var m = s * 60; -var h = m * 60; -var d = h * 24; -var y = d * 365.25; -/** - * Parse or format the given `val`. - * - * Options: - * - * - `long` verbose formatting [false] - * - * @param {String|Number} val - * @param {Object} [options] - * @throws {Error} throw an error if val is not a non-empty string or a number - * @return {String|Number} - * @api public - */ -module.exports = function(val, options) { - options = options || {}; - var type = typeof val; - if (type === 'string' && val.length > 0) { - return parse(val); - } else if (type === 'number' && isNaN(val) === false) { - return options.long ? fmtLong(val) : fmtShort(val); - } - throw new Error( - 'val is not a non-empty string or a valid number. val=' + - JSON.stringify(val) - ); -}; -/** - * Parse the given `str` and return milliseconds. - * - * @param {String} str - * @return {Number} - * @api private - */ -function parse(str) { - str = String(str); - if (str.length > 100) { - return; - } - var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec( - str - ); - if (!match) { - return; - } - var n = parseFloat(match[1]); - var type = (match[2] || 'ms').toLowerCase(); - switch (type) { - case 'years': - case 'year': - case 'yrs': - case 'yr': - case 'y': - return n * y; - case 'days': - case 'day': - case 'd': - return n * d; - case 'hours': - case 'hour': - case 'hrs': - case 'hr': - case 'h': - return n * h; - case 'minutes': - case 'minute': - case 'mins': - case 'min': - case 'm': - return n * m; - case 'seconds': - case 'second': - case 'secs': - case 'sec': - case 's': - return n * s; - case 'milliseconds': - case 'millisecond': - case 'msecs': - case 'msec': - case 'ms': - return n; - default: - return undefined; - } -} -/** - * Short format for `ms`. - * - * @param {Number} ms - * @return {String} - * @api private - */ -function fmtShort(ms) { - if (ms >= d) { - return Math.round(ms / d) + 'd'; - } - if (ms >= h) { - return Math.round(ms / h) + 'h'; - } - if (ms >= m) { - return Math.round(ms / m) + 'm'; - } - if (ms >= s) { - return Math.round(ms / s) + 's'; - } - return ms + 'ms'; -} -/** - * Long format for `ms`. - * - * @param {Number} ms - * @return {String} - * @api private - */ -function fmtLong(ms) { - return plural(ms, d, 'day') || - plural(ms, h, 'hour') || - plural(ms, m, 'minute') || - plural(ms, s, 'second') || - ms + ' ms'; -} -/** - * Pluralization helper. - */ -function plural(ms, n, name) { - if (ms < n) { - return; - } - if (ms < n * 1.5) { - return Math.floor(ms / n) + ' ' + name; - } - return Math.ceil(ms / n) + ' ' + name + 's'; -} -/***/ }), -/* 230 */, -/* 231 */, -/* 232 */, -/* 233 */ -/***/ (function(module, exports, __webpack_require__) { -module.exports = rimraf -rimraf.sync = rimrafSync -var assert = __webpack_require__(22) -var path = __webpack_require__(0) -var fs = __webpack_require__(3) -var glob = __webpack_require__(75) -var _0666 = parseInt('666', 8) -var defaultGlobOpts = { - nosort: true, - silent: true -} -// for EMFILE handling -var timeout = 0 -var isWindows = (process.platform === "win32") -function defaults (options) { - var methods = [ - 'unlink', - 'chmod', - 'stat', - 'lstat', - 'rmdir', - 'readdir' - ] - methods.forEach(function(m) { - options[m] = options[m] || fs[m] - m = m + 'Sync' - options[m] = options[m] || fs[m] - }) - options.maxBusyTries = options.maxBusyTries || 3 - options.emfileWait = options.emfileWait || 1000 - if (options.glob === false) { - options.disableGlob = true - } - options.disableGlob = options.disableGlob || false - options.glob = options.glob || defaultGlobOpts -} -function rimraf (p, options, cb) { - if (typeof options === 'function') { - cb = options - options = {} - } - assert(p, 'rimraf: missing path') - assert.equal(typeof p, 'string', 'rimraf: path should be a string') - assert.equal(typeof cb, 'function', 'rimraf: callback function required') - assert(options, 'rimraf: invalid options argument provided') - assert.equal(typeof options, 'object', 'rimraf: options should be object') - defaults(options) - var busyTries = 0 - var errState = null - var n = 0 - if (options.disableGlob || !glob.hasMagic(p)) - return afterGlob(null, [p]) - options.lstat(p, function (er, stat) { - if (!er) - return afterGlob(null, [p]) - glob(p, options.glob, afterGlob) - }) - function next (er) { - errState = errState || er - if (--n === 0) - cb(errState) - } - function afterGlob (er, results) { - if (er) - return cb(er) - n = results.length - if (n === 0) - return cb() - results.forEach(function (p) { - rimraf_(p, options, function CB (er) { - if (er) { - if ((er.code === "EBUSY" || er.code === "ENOTEMPTY" || er.code === "EPERM") && - busyTries < options.maxBusyTries) { - busyTries ++ - var time = busyTries * 100 - // try again, with the same exact callback as this one. - return setTimeout(function () { - rimraf_(p, options, CB) - }, time) - } - // this one won't happen if graceful-fs is used. - if (er.code === "EMFILE" && timeout < options.emfileWait) { - return setTimeout(function () { - rimraf_(p, options, CB) - }, timeout ++) - } - // already gone - if (er.code === "ENOENT") er = null - } - timeout = 0 - next(er) - }) - }) - } -} -// Two possible strategies. -// 1. Assume it's a file. unlink it, then do the dir stuff on EPERM or EISDIR -// 2. Assume it's a directory. readdir, then do the file stuff on ENOTDIR -// -// Both result in an extra syscall when you guess wrong. However, there -// are likely far more normal files in the world than directories. This -// is based on the assumption that a the average number of files per -// directory is >= 1. -// -// If anyone ever complains about this, then I guess the strategy could -// be made configurable somehow. But until then, YAGNI. -function rimraf_ (p, options, cb) { - assert(p) - assert(options) - assert(typeof cb === 'function') - // sunos lets the root user unlink directories, which is... weird. - // so we have to lstat here and make sure it's not a dir. - options.lstat(p, function (er, st) { - if (er && er.code === "ENOENT") - return cb(null) - // Windows can EPERM on stat. Life is suffering. - if (er && er.code === "EPERM" && isWindows) - fixWinEPERM(p, options, er, cb) - if (st && st.isDirectory()) - return rmdir(p, options, er, cb) - options.unlink(p, function (er) { - if (er) { - if (er.code === "ENOENT") - return cb(null) - if (er.code === "EPERM") - return (isWindows) - ? fixWinEPERM(p, options, er, cb) - : rmdir(p, options, er, cb) - if (er.code === "EISDIR") - return rmdir(p, options, er, cb) - } - return cb(er) - }) - }) -} -function fixWinEPERM (p, options, er, cb) { - assert(p) - assert(options) - assert(typeof cb === 'function') - if (er) - assert(er instanceof Error) - options.chmod(p, _0666, function (er2) { - if (er2) - cb(er2.code === "ENOENT" ? null : er) - else - options.stat(p, function(er3, stats) { - if (er3) - cb(er3.code === "ENOENT" ? null : er) - else if (stats.isDirectory()) - rmdir(p, options, er, cb) - else - options.unlink(p, cb) - }) - }) -} -function fixWinEPERMSync (p, options, er) { - assert(p) - assert(options) - if (er) - assert(er instanceof Error) - try { - options.chmodSync(p, _0666) - } catch (er2) { - if (er2.code === "ENOENT") - return - else - throw er - } - try { - var stats = options.statSync(p) - } catch (er3) { - if (er3.code === "ENOENT") - return - else - throw er - } - if (stats.isDirectory()) - rmdirSync(p, options, er) - else - options.unlinkSync(p) -} -function rmdir (p, options, originalEr, cb) { - assert(p) - assert(options) - if (originalEr) - assert(originalEr instanceof Error) - assert(typeof cb === 'function') - // try to rmdir first, and only readdir on ENOTEMPTY or EEXIST (SunOS) - // if we guessed wrong, and it's not a directory, then - // raise the original error. - options.rmdir(p, function (er) { - if (er && (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM")) - rmkids(p, options, cb) - else if (er && er.code === "ENOTDIR") - cb(originalEr) - else - cb(er) - }) -} -function rmkids(p, options, cb) { - assert(p) - assert(options) - assert(typeof cb === 'function') - options.readdir(p, function (er, files) { - if (er) - return cb(er) - var n = files.length - if (n === 0) - return options.rmdir(p, cb) - var errState - files.forEach(function (f) { - rimraf(path.join(p, f), options, function (er) { - if (errState) - return - if (er) - return cb(errState = er) - if (--n === 0) - options.rmdir(p, cb) - }) - }) - }) -} -// this looks simpler, and is strictly *faster*, but will -// tie up the JavaScript thread and fail on excessively -// deep directory trees. -function rimrafSync (p, options) { - options = options || {} - defaults(options) - assert(p, 'rimraf: missing path') - assert.equal(typeof p, 'string', 'rimraf: path should be a string') - assert(options, 'rimraf: missing options') - assert.equal(typeof options, 'object', 'rimraf: options should be object') - var results - if (options.disableGlob || !glob.hasMagic(p)) { - results = [p] - } else { - try { - options.lstatSync(p) - results = [p] - } catch (er) { - results = glob.sync(p, options.glob) - } - } - if (!results.length) - return - for (var i = 0; i < results.length; i++) { - var p = results[i] - try { - var st = options.lstatSync(p) - } catch (er) { - if (er.code === "ENOENT") - return - // Windows can EPERM on stat. Life is suffering. - if (er.code === "EPERM" && isWindows) - fixWinEPERMSync(p, options, er) - } - try { - // sunos lets the root user unlink directories, which is... weird. - if (st && st.isDirectory()) - rmdirSync(p, options, null) - else - options.unlinkSync(p) - } catch (er) { - if (er.code === "ENOENT") - return - if (er.code === "EPERM") - return isWindows ? fixWinEPERMSync(p, options, er) : rmdirSync(p, options, er) - if (er.code !== "EISDIR") - throw er - rmdirSync(p, options, er) - } - } -} -function rmdirSync (p, options, originalEr) { - assert(p) - assert(options) - if (originalEr) - assert(originalEr instanceof Error) - try { - options.rmdirSync(p) - } catch (er) { - if (er.code === "ENOENT") - return - if (er.code === "ENOTDIR") - throw originalEr - if (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM") - rmkidsSync(p, options) - } -} -function rmkidsSync (p, options) { - assert(p) - assert(options) - options.readdirSync(p).forEach(function (f) { - rimrafSync(path.join(p, f), options) - }) - // We only end up here once we got ENOTEMPTY at least once, and - // at this point, we are guaranteed to have removed all the kids. - // So, we know that it won't be ENOENT or ENOTDIR or anything else. - // try really hard to delete stuff on windows, because it has a - // PROFOUNDLY annoying habit of not closing handles promptly when - // files are deleted, resulting in spurious ENOTEMPTY errors. - var retries = isWindows ? 100 : 1 - var i = 0 - do { - var threw = true - try { - var ret = options.rmdirSync(p, options) - threw = false - return ret - } finally { - if (++i < retries && threw) - continue - } - } while (true) -} -/***/ }), -/* 234 */, -/* 235 */, -/* 236 */, -/* 237 */, -/* 238 */, -/* 239 */ -/***/ (function(module, exports, __webpack_require__) { -"use strict"; -var hasFlag = __webpack_require__(221); -var support = function (level) { - if (level === 0) { - return false; - } - return { - level: level, - hasBasic: true, - has256: level >= 2, - has16m: level >= 3 - }; -}; -var supportLevel = (function () { - if (hasFlag('no-color') || - hasFlag('no-colors') || - hasFlag('color=false')) { - return 0; - } - if (hasFlag('color=16m') || - hasFlag('color=full') || - hasFlag('color=truecolor')) { - return 3; - } - if (hasFlag('color=256')) { - return 2; - } - if (hasFlag('color') || - hasFlag('colors') || - hasFlag('color=true') || - hasFlag('color=always')) { - return 1; - } - if (process.stdout && !process.stdout.isTTY) { - return 0; - } - if (process.platform === 'win32') { - return 1; - } - if ('CI' in process.env) { - if ('TRAVIS' in process.env || process.env.CI === 'Travis') { - return 1; - } - return 0; - } - if ('TEAMCITY_VERSION' in process.env) { - return process.env.TEAMCITY_VERSION.match(/^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/) === null ? 0 : 1; - } - if (/^(screen|xterm)-256(?:color)?/.test(process.env.TERM)) { - return 2; - } - if (/^screen|^xterm|^vt100|color|ansi|cygwin|linux/i.test(process.env.TERM)) { - return 1; - } - if ('COLORTERM' in process.env) { - return 1; - } - if (process.env.TERM === 'dumb') { - return 0; - } - return 0; -})(); -if (supportLevel === 0 && 'FORCE_COLOR' in process.env) { - supportLevel = 1; -} -module.exports = process && support(supportLevel); -/***/ }) -/******/ ]); -/***/ }), -/* 410 */ -/***/ (function(module, exports) { -module.exports = require("buffer"); + + + + +//# sourceMappingURL=index.js.map + /***/ }), -/* 411 */ +/* 425 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "validateDependencies", function() { return validateDependencies; }); -/* harmony import */ var _yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(409); -/* harmony import */ var _yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var dedent__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2); -/* harmony import */ var dedent__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(dedent__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(114); -/* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(chalk__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_3__); -/* harmony import */ var _fs__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(231); -/* harmony import */ var _log__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(220); -/* harmony import */ var _package_json__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(343); -/* harmony import */ var _projects_tree__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(412); -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ -// @ts-expect-error published types are useless - +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "audit", function() { return audit; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(91); +/** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ +function audit(durationSelector) { + return function auditOperatorFunction(source) { + return source.lift(new AuditOperator(durationSelector)); + }; +} +var AuditOperator = /*@__PURE__*/ (function () { + function AuditOperator(durationSelector) { + this.durationSelector = durationSelector; + } + AuditOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new AuditSubscriber(subscriber, this.durationSelector)); + }; + return AuditOperator; +}()); +var AuditSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](AuditSubscriber, _super); + function AuditSubscriber(destination, durationSelector) { + var _this = _super.call(this, destination) || this; + _this.durationSelector = durationSelector; + _this.hasValue = false; + return _this; + } + AuditSubscriber.prototype._next = function (value) { + this.value = value; + this.hasValue = true; + if (!this.throttled) { + var duration = void 0; + try { + var durationSelector = this.durationSelector; + duration = durationSelector(value); + } + catch (err) { + return this.destination.error(err); + } + var innerSubscription = Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["innerSubscribe"])(duration, new _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleInnerSubscriber"](this)); + if (!innerSubscription || innerSubscription.closed) { + this.clearThrottle(); + } + else { + this.add(this.throttled = innerSubscription); + } + } + }; + AuditSubscriber.prototype.clearThrottle = function () { + var _a = this, value = _a.value, hasValue = _a.hasValue, throttled = _a.throttled; + if (throttled) { + this.remove(throttled); + this.throttled = undefined; + throttled.unsubscribe(); + } + if (hasValue) { + this.value = undefined; + this.hasValue = false; + this.destination.next(value); + } + }; + AuditSubscriber.prototype.notifyNext = function () { + this.clearThrottle(); + }; + AuditSubscriber.prototype.notifyComplete = function () { + this.clearThrottle(); + }; + return AuditSubscriber; +}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleOuterSubscriber"])); +//# sourceMappingURL=audit.js.map +/***/ }), +/* 426 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "auditTime", function() { return auditTime; }); +/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(56); +/* harmony import */ var _audit__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(425); +/* harmony import */ var _observable_timer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(109); +/** PURE_IMPORTS_START _scheduler_async,_audit,_observable_timer PURE_IMPORTS_END */ -async function validateDependencies(kbn, yarnLock) { - // look through all of the packages in the yarn.lock file to see if - // we have accidentally installed multiple lodash v4 versions - const lodash4Versions = new Set(); - const lodash4Reqs = new Set(); - for (const [req, dep] of Object.entries(yarnLock)) { - if (req.startsWith('lodash@') && dep.version.startsWith('4.')) { - lodash4Reqs.add(req); - lodash4Versions.add(dep.version); +function auditTime(duration, scheduler) { + if (scheduler === void 0) { + scheduler = _scheduler_async__WEBPACK_IMPORTED_MODULE_0__["async"]; } - } // if we find more than one lodash v4 version installed then delete - // lodash v4 requests from the yarn.lock file and prompt the user to - // retry bootstrap so that a single v4 version will be installed + return Object(_audit__WEBPACK_IMPORTED_MODULE_1__["audit"])(function () { return Object(_observable_timer__WEBPACK_IMPORTED_MODULE_2__["timer"])(duration, scheduler); }); +} +//# sourceMappingURL=auditTime.js.map - if (lodash4Versions.size > 1) { - for (const req of lodash4Reqs) { - delete yarnLock[req]; +/***/ }), +/* 427 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "buffer", function() { return buffer; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(91); +/** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ + + +function buffer(closingNotifier) { + return function bufferOperatorFunction(source) { + return source.lift(new BufferOperator(closingNotifier)); + }; +} +var BufferOperator = /*@__PURE__*/ (function () { + function BufferOperator(closingNotifier) { + this.closingNotifier = closingNotifier; + } + BufferOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new BufferSubscriber(subscriber, this.closingNotifier)); + }; + return BufferOperator; +}()); +var BufferSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](BufferSubscriber, _super); + function BufferSubscriber(destination, closingNotifier) { + var _this = _super.call(this, destination) || this; + _this.buffer = []; + _this.add(Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["innerSubscribe"])(closingNotifier, new _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleInnerSubscriber"](_this))); + return _this; } + BufferSubscriber.prototype._next = function (value) { + this.buffer.push(value); + }; + BufferSubscriber.prototype.notifyNext = function () { + var buffer = this.buffer; + this.buffer = []; + this.destination.next(buffer); + }; + return BufferSubscriber; +}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleOuterSubscriber"])); +//# sourceMappingURL=buffer.js.map - await Object(_fs__WEBPACK_IMPORTED_MODULE_4__["writeFile"])(kbn.getAbsolute('yarn.lock'), Object(_yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__["stringify"])(yarnLock), 'utf8'); - _log__WEBPACK_IMPORTED_MODULE_5__["log"].error(dedent__WEBPACK_IMPORTED_MODULE_1___default.a` - Multiple version of lodash v4 were detected, so they have been removed - from the yarn.lock file. Please rerun yarn kbn bootstrap to coalese the - lodash versions installed. +/***/ }), +/* 428 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { - If you still see this error when you re-bootstrap then you might need - to force a new dependency to use the latest version of lodash via the - "resolutions" field in package.json. +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "bufferCount", function() { return bufferCount; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); +/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ - If you have questions about this please reach out to the operations team. - `); - process.exit(1); - } // look through all the dependencies of production packages and production - // dependencies of those packages to determine if we're shipping any versions - // of lodash v3 in the distributable +function bufferCount(bufferSize, startBufferEvery) { + if (startBufferEvery === void 0) { + startBufferEvery = null; + } + return function bufferCountOperatorFunction(source) { + return source.lift(new BufferCountOperator(bufferSize, startBufferEvery)); + }; +} +var BufferCountOperator = /*@__PURE__*/ (function () { + function BufferCountOperator(bufferSize, startBufferEvery) { + this.bufferSize = bufferSize; + this.startBufferEvery = startBufferEvery; + if (!startBufferEvery || bufferSize === startBufferEvery) { + this.subscriberClass = BufferCountSubscriber; + } + else { + this.subscriberClass = BufferSkipCountSubscriber; + } + } + BufferCountOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new this.subscriberClass(subscriber, this.bufferSize, this.startBufferEvery)); + }; + return BufferCountOperator; +}()); +var BufferCountSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](BufferCountSubscriber, _super); + function BufferCountSubscriber(destination, bufferSize) { + var _this = _super.call(this, destination) || this; + _this.bufferSize = bufferSize; + _this.buffer = []; + return _this; + } + BufferCountSubscriber.prototype._next = function (value) { + var buffer = this.buffer; + buffer.push(value); + if (buffer.length == this.bufferSize) { + this.destination.next(buffer); + this.buffer = []; + } + }; + BufferCountSubscriber.prototype._complete = function () { + var buffer = this.buffer; + if (buffer.length > 0) { + this.destination.next(buffer); + } + _super.prototype._complete.call(this); + }; + return BufferCountSubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); +var BufferSkipCountSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](BufferSkipCountSubscriber, _super); + function BufferSkipCountSubscriber(destination, bufferSize, startBufferEvery) { + var _this = _super.call(this, destination) || this; + _this.bufferSize = bufferSize; + _this.startBufferEvery = startBufferEvery; + _this.buffers = []; + _this.count = 0; + return _this; + } + BufferSkipCountSubscriber.prototype._next = function (value) { + var _a = this, bufferSize = _a.bufferSize, startBufferEvery = _a.startBufferEvery, buffers = _a.buffers, count = _a.count; + this.count++; + if (count % startBufferEvery === 0) { + buffers.push([]); + } + for (var i = buffers.length; i--;) { + var buffer = buffers[i]; + buffer.push(value); + if (buffer.length === bufferSize) { + buffers.splice(i, 1); + this.destination.next(buffer); + } + } + }; + BufferSkipCountSubscriber.prototype._complete = function () { + var _a = this, buffers = _a.buffers, destination = _a.destination; + while (buffers.length > 0) { + var buffer = buffers.shift(); + if (buffer.length > 0) { + destination.next(buffer); + } + } + _super.prototype._complete.call(this); + }; + return BufferSkipCountSubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); +//# sourceMappingURL=bufferCount.js.map - const prodDependencies = kbn.resolveAllProductionDependencies(yarnLock, _log__WEBPACK_IMPORTED_MODULE_5__["log"]); - const lodash3Versions = new Set(); +/***/ }), +/* 429 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { - for (const dep of prodDependencies.values()) { - if (dep.name === 'lodash' && dep.version.startsWith('3.')) { - lodash3Versions.add(dep.version); +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "bufferTime", function() { return bufferTime; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); +/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(56); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(12); +/* harmony import */ var _util_isScheduler__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(46); +/** PURE_IMPORTS_START tslib,_scheduler_async,_Subscriber,_util_isScheduler PURE_IMPORTS_END */ + + + + +function bufferTime(bufferTimeSpan) { + var length = arguments.length; + var scheduler = _scheduler_async__WEBPACK_IMPORTED_MODULE_1__["async"]; + if (Object(_util_isScheduler__WEBPACK_IMPORTED_MODULE_3__["isScheduler"])(arguments[arguments.length - 1])) { + scheduler = arguments[arguments.length - 1]; + length--; + } + var bufferCreationInterval = null; + if (length >= 2) { + bufferCreationInterval = arguments[1]; + } + var maxBufferSize = Number.POSITIVE_INFINITY; + if (length >= 3) { + maxBufferSize = arguments[2]; + } + return function bufferTimeOperatorFunction(source) { + return source.lift(new BufferTimeOperator(bufferTimeSpan, bufferCreationInterval, maxBufferSize, scheduler)); + }; +} +var BufferTimeOperator = /*@__PURE__*/ (function () { + function BufferTimeOperator(bufferTimeSpan, bufferCreationInterval, maxBufferSize, scheduler) { + this.bufferTimeSpan = bufferTimeSpan; + this.bufferCreationInterval = bufferCreationInterval; + this.maxBufferSize = maxBufferSize; + this.scheduler = scheduler; + } + BufferTimeOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new BufferTimeSubscriber(subscriber, this.bufferTimeSpan, this.bufferCreationInterval, this.maxBufferSize, this.scheduler)); + }; + return BufferTimeOperator; +}()); +var Context = /*@__PURE__*/ (function () { + function Context() { + this.buffer = []; + } + return Context; +}()); +var BufferTimeSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](BufferTimeSubscriber, _super); + function BufferTimeSubscriber(destination, bufferTimeSpan, bufferCreationInterval, maxBufferSize, scheduler) { + var _this = _super.call(this, destination) || this; + _this.bufferTimeSpan = bufferTimeSpan; + _this.bufferCreationInterval = bufferCreationInterval; + _this.maxBufferSize = maxBufferSize; + _this.scheduler = scheduler; + _this.contexts = []; + var context = _this.openContext(); + _this.timespanOnly = bufferCreationInterval == null || bufferCreationInterval < 0; + if (_this.timespanOnly) { + var timeSpanOnlyState = { subscriber: _this, context: context, bufferTimeSpan: bufferTimeSpan }; + _this.add(context.closeAction = scheduler.schedule(dispatchBufferTimeSpanOnly, bufferTimeSpan, timeSpanOnlyState)); + } + else { + var closeState = { subscriber: _this, context: context }; + var creationState = { bufferTimeSpan: bufferTimeSpan, bufferCreationInterval: bufferCreationInterval, subscriber: _this, scheduler: scheduler }; + _this.add(context.closeAction = scheduler.schedule(dispatchBufferClose, bufferTimeSpan, closeState)); + _this.add(scheduler.schedule(dispatchBufferCreation, bufferCreationInterval, creationState)); + } + return _this; + } + BufferTimeSubscriber.prototype._next = function (value) { + var contexts = this.contexts; + var len = contexts.length; + var filledBufferContext; + for (var i = 0; i < len; i++) { + var context_1 = contexts[i]; + var buffer = context_1.buffer; + buffer.push(value); + if (buffer.length == this.maxBufferSize) { + filledBufferContext = context_1; + } + } + if (filledBufferContext) { + this.onBufferFull(filledBufferContext); + } + }; + BufferTimeSubscriber.prototype._error = function (err) { + this.contexts.length = 0; + _super.prototype._error.call(this, err); + }; + BufferTimeSubscriber.prototype._complete = function () { + var _a = this, contexts = _a.contexts, destination = _a.destination; + while (contexts.length > 0) { + var context_2 = contexts.shift(); + destination.next(context_2.buffer); + } + _super.prototype._complete.call(this); + }; + BufferTimeSubscriber.prototype._unsubscribe = function () { + this.contexts = null; + }; + BufferTimeSubscriber.prototype.onBufferFull = function (context) { + this.closeContext(context); + var closeAction = context.closeAction; + closeAction.unsubscribe(); + this.remove(closeAction); + if (!this.closed && this.timespanOnly) { + context = this.openContext(); + var bufferTimeSpan = this.bufferTimeSpan; + var timeSpanOnlyState = { subscriber: this, context: context, bufferTimeSpan: bufferTimeSpan }; + this.add(context.closeAction = this.scheduler.schedule(dispatchBufferTimeSpanOnly, bufferTimeSpan, timeSpanOnlyState)); + } + }; + BufferTimeSubscriber.prototype.openContext = function () { + var context = new Context(); + this.contexts.push(context); + return context; + }; + BufferTimeSubscriber.prototype.closeContext = function (context) { + this.destination.next(context.buffer); + var contexts = this.contexts; + var spliceIndex = contexts ? contexts.indexOf(context) : -1; + if (spliceIndex >= 0) { + contexts.splice(contexts.indexOf(context), 1); + } + }; + return BufferTimeSubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_2__["Subscriber"])); +function dispatchBufferTimeSpanOnly(state) { + var subscriber = state.subscriber; + var prevContext = state.context; + if (prevContext) { + subscriber.closeContext(prevContext); } - } // if any lodash v3 packages were found we abort and tell the user to fix things - - - if (lodash3Versions.size) { - _log__WEBPACK_IMPORTED_MODULE_5__["log"].error(dedent__WEBPACK_IMPORTED_MODULE_1___default.a` - - Due to changes in the yarn.lock file and/or package.json files a version of - lodash 3 is now included in the production dependencies. To reduce the size of - our distributable and especially our front-end bundles we have decided to - prevent adding any new instances of lodash 3. - - Please inspect the changes to yarn.lock or package.json files to identify where - the lodash 3 version is coming from and remove it. - - If you have questions about this please reack out to the operations team. - - `); - process.exit(1); - } // TODO: remove this once we move into a single package.json - // look through all the package.json files to find packages which have mismatched version ranges - - - const depRanges = new Map(); - - for (const project of kbn.getAllProjects().values()) { - var _kbn$kibanaProject; - - // Skip if this is an external plugin - if (project.path.includes(`${(_kbn$kibanaProject = kbn.kibanaProject) === null || _kbn$kibanaProject === void 0 ? void 0 : _kbn$kibanaProject.path}${path__WEBPACK_IMPORTED_MODULE_3__["sep"]}plugins`)) { - continue; + if (!subscriber.closed) { + state.context = subscriber.openContext(); + state.context.closeAction = this.schedule(state, state.bufferTimeSpan); } - - for (const [dep, range] of Object.entries(project.allDependencies)) { - const existingDep = depRanges.get(dep); - - if (!existingDep) { - depRanges.set(dep, [{ - range, - projects: [project] - }]); - continue; - } - - const existingRange = existingDep.find(existing => existing.range === range); - - if (!existingRange) { - existingDep.push({ - range, - projects: [project] - }); - continue; - } - - existingRange.projects.push(project); +} +function dispatchBufferCreation(state) { + var bufferCreationInterval = state.bufferCreationInterval, bufferTimeSpan = state.bufferTimeSpan, subscriber = state.subscriber, scheduler = state.scheduler; + var context = subscriber.openContext(); + var action = this; + if (!subscriber.closed) { + subscriber.add(context.closeAction = scheduler.schedule(dispatchBufferClose, bufferTimeSpan, { subscriber: subscriber, context: context })); + action.schedule(state, bufferCreationInterval); } - } - - const duplicateRanges = Array.from(depRanges.entries()).filter(([, ranges]) => ranges.length > 1 && !ranges.every(rng => Object(_package_json__WEBPACK_IMPORTED_MODULE_6__["isLinkDependency"])(rng.range))).reduce((acc, [dep, ranges]) => [...acc, dep, ...ranges.map(({ - range, - projects - }) => ` ${range} => ${projects.map(p => p.name).join(', ')}`)], []).join('\n '); - - if (duplicateRanges) { - _log__WEBPACK_IMPORTED_MODULE_5__["log"].error(dedent__WEBPACK_IMPORTED_MODULE_1___default.a` - - [single_version_dependencies] Multiple version ranges for the same dependency - were found declared across different package.json files. Please consolidate - those to match across all package.json files. Different versions for the - same dependency is not supported. - - If you have questions about this please reach out to the operations team. - - The conflicting dependencies are: - - ${duplicateRanges} - `); - process.exit(1); - } // look for packages that have the the `kibana.devOnly` flag in their package.json - // and make sure they aren't included in the production dependencies of Kibana - - - const devOnlyProjectsInProduction = getDevOnlyProductionDepsTree(kbn, 'kibana'); - - if (devOnlyProjectsInProduction) { - _log__WEBPACK_IMPORTED_MODULE_5__["log"].error(dedent__WEBPACK_IMPORTED_MODULE_1___default.a` - Some of the packages in the production dependency chain for Kibana and X-Pack are - flagged with "kibana.devOnly" in their package.json. Please check changes made to - packages and their dependencies to ensure they don't end up in production. - - The devOnly dependencies that are being dependend on in production are: - - ${Object(_projects_tree__WEBPACK_IMPORTED_MODULE_7__["treeToString"])(devOnlyProjectsInProduction).split('\n').join('\n ')} - `); - process.exit(1); - } - - _log__WEBPACK_IMPORTED_MODULE_5__["log"].success('yarn.lock analysis completed without any issues'); } - -function getDevOnlyProductionDepsTree(kbn, projectName) { - const project = kbn.getProject(projectName); - const childProjectNames = [...Object.keys(project.productionDependencies).filter(name => kbn.hasProject(name)), ...(projectName === 'kibana' ? ['x-pack'] : [])]; - const children = childProjectNames.map(n => getDevOnlyProductionDepsTree(kbn, n)).filter(t => !!t); - - if (!children.length && !project.isFlaggedAsDevOnly()) { - return; - } - - const tree = { - name: project.isFlaggedAsDevOnly() ? chalk__WEBPACK_IMPORTED_MODULE_2___default.a.red.bold(projectName) : projectName, - children - }; - return tree; +function dispatchBufferClose(arg) { + var subscriber = arg.subscriber, context = arg.context; + subscriber.closeContext(context); } +//# sourceMappingURL=bufferTime.js.map + /***/ }), -/* 412 */ +/* 430 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "renderProjectsTree", function() { return renderProjectsTree; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "treeToString", function() { return treeToString; }); -/* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(114); -/* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(chalk__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_1__); -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "bufferToggle", function() { return bufferToggle; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); +/* harmony import */ var _Subscription__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(18); +/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(71); +/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(70); +/** PURE_IMPORTS_START tslib,_Subscription,_util_subscribeToResult,_OuterSubscriber PURE_IMPORTS_END */ -const projectKey = Symbol('__project'); -function renderProjectsTree(rootPath, projects) { - const projectsTree = buildProjectsTree(rootPath, projects); - return treeToString(createTreeStructure(projectsTree)); -} -function treeToString(tree) { - return [tree.name].concat(childrenToStrings(tree.children, '')).join('\n'); -} -function childrenToStrings(tree, treePrefix) { - if (tree === undefined) { - return []; - } - let strings = []; - tree.forEach((node, index) => { - const isLastNode = tree.length - 1 === index; - const nodePrefix = isLastNode ? '└── ' : '├── '; - const childPrefix = isLastNode ? ' ' : '│ '; - const childrenPrefix = treePrefix + childPrefix; - strings.push(`${treePrefix}${nodePrefix}${node.name}`); - strings = strings.concat(childrenToStrings(node.children, childrenPrefix)); - }); - return strings; +function bufferToggle(openings, closingSelector) { + return function bufferToggleOperatorFunction(source) { + return source.lift(new BufferToggleOperator(openings, closingSelector)); + }; } - -function createTreeStructure(tree) { - let name; - const children = []; - - for (const [dir, project] of tree.entries()) { - // This is a leaf node (aka a project) - if (typeof project === 'string') { - name = chalk__WEBPACK_IMPORTED_MODULE_0___default.a.green(project); - continue; - } // If there's only one project and the key indicates it's a leaf node, we - // know that we're at a package folder that contains a package.json, so we - // "inline it" so we don't get unnecessary levels, i.e. we'll just see - // `foo` instead of `foo -> foo`. - - - if (project.size === 1 && project.has(projectKey)) { - const projectName = project.get(projectKey); - children.push({ - children: [], - name: dirOrProjectName(dir, projectName) - }); - continue; +var BufferToggleOperator = /*@__PURE__*/ (function () { + function BufferToggleOperator(openings, closingSelector) { + this.openings = openings; + this.closingSelector = closingSelector; } - - const subtree = createTreeStructure(project); // If the name is specified, we know there's a package at the "root" of the - // subtree itself. - - if (subtree.name !== undefined) { - const projectName = subtree.name; - children.push({ - children: subtree.children, - name: dirOrProjectName(dir, projectName) - }); - continue; - } // Special-case whenever we have one child, so we don't get unnecessary - // folders in the output. E.g. instead of `foo -> bar -> baz` we get - // `foo/bar/baz` instead. - - - if (subtree.children && subtree.children.length === 1) { - const child = subtree.children[0]; - const newName = chalk__WEBPACK_IMPORTED_MODULE_0___default.a.dim(path__WEBPACK_IMPORTED_MODULE_1___default.a.join(dir.toString(), child.name)); - children.push({ - children: child.children, - name: newName - }); - continue; + BufferToggleOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new BufferToggleSubscriber(subscriber, this.openings, this.closingSelector)); + }; + return BufferToggleOperator; +}()); +var BufferToggleSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](BufferToggleSubscriber, _super); + function BufferToggleSubscriber(destination, openings, closingSelector) { + var _this = _super.call(this, destination) || this; + _this.closingSelector = closingSelector; + _this.contexts = []; + _this.add(Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__["subscribeToResult"])(_this, openings)); + return _this; } + BufferToggleSubscriber.prototype._next = function (value) { + var contexts = this.contexts; + var len = contexts.length; + for (var i = 0; i < len; i++) { + contexts[i].buffer.push(value); + } + }; + BufferToggleSubscriber.prototype._error = function (err) { + var contexts = this.contexts; + while (contexts.length > 0) { + var context_1 = contexts.shift(); + context_1.subscription.unsubscribe(); + context_1.buffer = null; + context_1.subscription = null; + } + this.contexts = null; + _super.prototype._error.call(this, err); + }; + BufferToggleSubscriber.prototype._complete = function () { + var contexts = this.contexts; + while (contexts.length > 0) { + var context_2 = contexts.shift(); + this.destination.next(context_2.buffer); + context_2.subscription.unsubscribe(); + context_2.buffer = null; + context_2.subscription = null; + } + this.contexts = null; + _super.prototype._complete.call(this); + }; + BufferToggleSubscriber.prototype.notifyNext = function (outerValue, innerValue) { + outerValue ? this.closeBuffer(outerValue) : this.openBuffer(innerValue); + }; + BufferToggleSubscriber.prototype.notifyComplete = function (innerSub) { + this.closeBuffer(innerSub.context); + }; + BufferToggleSubscriber.prototype.openBuffer = function (value) { + try { + var closingSelector = this.closingSelector; + var closingNotifier = closingSelector.call(this, value); + if (closingNotifier) { + this.trySubscribe(closingNotifier); + } + } + catch (err) { + this._error(err); + } + }; + BufferToggleSubscriber.prototype.closeBuffer = function (context) { + var contexts = this.contexts; + if (contexts && context) { + var buffer = context.buffer, subscription = context.subscription; + this.destination.next(buffer); + contexts.splice(contexts.indexOf(context), 1); + this.remove(subscription); + subscription.unsubscribe(); + } + }; + BufferToggleSubscriber.prototype.trySubscribe = function (closingNotifier) { + var contexts = this.contexts; + var buffer = []; + var subscription = new _Subscription__WEBPACK_IMPORTED_MODULE_1__["Subscription"](); + var context = { buffer: buffer, subscription: subscription }; + contexts.push(context); + var innerSubscription = Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__["subscribeToResult"])(this, closingNotifier, context); + if (!innerSubscription || innerSubscription.closed) { + this.closeBuffer(context); + } + else { + innerSubscription.context = context; + this.add(innerSubscription); + subscription.add(innerSubscription); + } + }; + return BufferToggleSubscriber; +}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__["OuterSubscriber"])); +//# sourceMappingURL=bufferToggle.js.map - children.push({ - children: subtree.children, - name: chalk__WEBPACK_IMPORTED_MODULE_0___default.a.dim(dir.toString()) - }); - } - return { - name, - children - }; -} +/***/ }), +/* 431 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { -function dirOrProjectName(dir, projectName) { - return dir === projectName ? chalk__WEBPACK_IMPORTED_MODULE_0___default.a.green(dir) : chalk__WEBPACK_IMPORTED_MODULE_0___default.a`{dim ${dir.toString()} ({reset.green ${projectName}})}`; -} +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "bufferWhen", function() { return bufferWhen; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); +/* harmony import */ var _Subscription__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(18); +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(91); +/** PURE_IMPORTS_START tslib,_Subscription,_innerSubscribe PURE_IMPORTS_END */ -function buildProjectsTree(rootPath, projects) { - const tree = new Map(); - for (const project of projects.values()) { - if (rootPath === project.path) { - tree.set(projectKey, project.name); - } else { - const relativeProjectPath = path__WEBPACK_IMPORTED_MODULE_1___default.a.relative(rootPath, project.path); - addProjectToTree(tree, relativeProjectPath.split(path__WEBPACK_IMPORTED_MODULE_1___default.a.sep), project); - } - } - return tree; +function bufferWhen(closingSelector) { + return function (source) { + return source.lift(new BufferWhenOperator(closingSelector)); + }; } - -function addProjectToTree(tree, pathParts, project) { - if (pathParts.length === 0) { - tree.set(projectKey, project.name); - } else { - const [currentDir, ...rest] = pathParts; - - if (!tree.has(currentDir)) { - tree.set(currentDir, new Map()); +var BufferWhenOperator = /*@__PURE__*/ (function () { + function BufferWhenOperator(closingSelector) { + this.closingSelector = closingSelector; + } + BufferWhenOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new BufferWhenSubscriber(subscriber, this.closingSelector)); + }; + return BufferWhenOperator; +}()); +var BufferWhenSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](BufferWhenSubscriber, _super); + function BufferWhenSubscriber(destination, closingSelector) { + var _this = _super.call(this, destination) || this; + _this.closingSelector = closingSelector; + _this.subscribing = false; + _this.openBuffer(); + return _this; } + BufferWhenSubscriber.prototype._next = function (value) { + this.buffer.push(value); + }; + BufferWhenSubscriber.prototype._complete = function () { + var buffer = this.buffer; + if (buffer) { + this.destination.next(buffer); + } + _super.prototype._complete.call(this); + }; + BufferWhenSubscriber.prototype._unsubscribe = function () { + this.buffer = undefined; + this.subscribing = false; + }; + BufferWhenSubscriber.prototype.notifyNext = function () { + this.openBuffer(); + }; + BufferWhenSubscriber.prototype.notifyComplete = function () { + if (this.subscribing) { + this.complete(); + } + else { + this.openBuffer(); + } + }; + BufferWhenSubscriber.prototype.openBuffer = function () { + var closingSubscription = this.closingSubscription; + if (closingSubscription) { + this.remove(closingSubscription); + closingSubscription.unsubscribe(); + } + var buffer = this.buffer; + if (this.buffer) { + this.destination.next(buffer); + } + this.buffer = []; + var closingNotifier; + try { + var closingSelector = this.closingSelector; + closingNotifier = closingSelector(); + } + catch (err) { + return this.error(err); + } + closingSubscription = new _Subscription__WEBPACK_IMPORTED_MODULE_1__["Subscription"](); + this.closingSubscription = closingSubscription; + this.add(closingSubscription); + this.subscribing = true; + closingSubscription.add(Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_2__["innerSubscribe"])(closingNotifier, new _innerSubscribe__WEBPACK_IMPORTED_MODULE_2__["SimpleInnerSubscriber"](this))); + this.subscribing = false; + }; + return BufferWhenSubscriber; +}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_2__["SimpleOuterSubscriber"])); +//# sourceMappingURL=bufferWhen.js.map - const subtree = tree.get(currentDir); - addProjectToTree(subtree, rest, project); - } -} /***/ }), -/* 413 */ +/* 432 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony import */ var _yarn_integrity__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(414); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "yarnIntegrityFileExists", function() { return _yarn_integrity__WEBPACK_IMPORTED_MODULE_0__["yarnIntegrityFileExists"]; }); - -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ensureYarnIntegrityFileExists", function() { return _yarn_integrity__WEBPACK_IMPORTED_MODULE_0__["ensureYarnIntegrityFileExists"]; }); - -/* harmony import */ var _get_cache_folders__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(415); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "getBazelDiskCacheFolder", function() { return _get_cache_folders__WEBPACK_IMPORTED_MODULE_1__["getBazelDiskCacheFolder"]; }); - -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "getBazelRepositoryCacheFolder", function() { return _get_cache_folders__WEBPACK_IMPORTED_MODULE_1__["getBazelRepositoryCacheFolder"]; }); - -/* harmony import */ var _install_tools__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(416); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "isBazelBinAvailable", function() { return _install_tools__WEBPACK_IMPORTED_MODULE_2__["isBazelBinAvailable"]; }); - -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "installBazelTools", function() { return _install_tools__WEBPACK_IMPORTED_MODULE_2__["installBazelTools"]; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "catchError", function() { return catchError; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(91); +/** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ -/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(417); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "runBazel", function() { return _run__WEBPACK_IMPORTED_MODULE_3__["runBazel"]; }); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "runIBazel", function() { return _run__WEBPACK_IMPORTED_MODULE_3__["runIBazel"]; }); +function catchError(selector) { + return function catchErrorOperatorFunction(source) { + var operator = new CatchOperator(selector); + var caught = source.lift(operator); + return (operator.caught = caught); + }; +} +var CatchOperator = /*@__PURE__*/ (function () { + function CatchOperator(selector) { + this.selector = selector; + } + CatchOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new CatchSubscriber(subscriber, this.selector, this.caught)); + }; + return CatchOperator; +}()); +var CatchSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](CatchSubscriber, _super); + function CatchSubscriber(destination, selector, caught) { + var _this = _super.call(this, destination) || this; + _this.selector = selector; + _this.caught = caught; + return _this; + } + CatchSubscriber.prototype.error = function (err) { + if (!this.isStopped) { + var result = void 0; + try { + result = this.selector(err, this.caught); + } + catch (err2) { + _super.prototype.error.call(this, err2); + return; + } + this._unsubscribeAndRecycle(); + var innerSubscriber = new _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleInnerSubscriber"](this); + this.add(innerSubscriber); + var innerSubscription = Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["innerSubscribe"])(result, innerSubscriber); + if (innerSubscription !== innerSubscriber) { + this.add(innerSubscription); + } + } + }; + return CatchSubscriber; +}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleOuterSubscriber"])); +//# sourceMappingURL=catchError.js.map -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ +/***/ }), +/* 433 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "combineAll", function() { return combineAll; }); +/* harmony import */ var _observable_combineLatest__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(69); +/** PURE_IMPORTS_START _observable_combineLatest PURE_IMPORTS_END */ +function combineAll(project) { + return function (source) { return source.lift(new _observable_combineLatest__WEBPACK_IMPORTED_MODULE_0__["CombineLatestOperator"](project)); }; +} +//# sourceMappingURL=combineAll.js.map /***/ }), -/* 414 */ +/* 434 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "yarnIntegrityFileExists", function() { return yarnIntegrityFileExists; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ensureYarnIntegrityFileExists", function() { return ensureYarnIntegrityFileExists; }); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var _fs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(231); -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "combineLatest", function() { return combineLatest; }); +/* harmony import */ var _util_isArray__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(19); +/* harmony import */ var _observable_combineLatest__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(69); +/* harmony import */ var _observable_from__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(84); +/** PURE_IMPORTS_START _util_isArray,_observable_combineLatest,_observable_from PURE_IMPORTS_END */ -async function yarnIntegrityFileExists(nodeModulesPath) { - try { - const nodeModulesRealPath = await Object(_fs__WEBPACK_IMPORTED_MODULE_1__["tryRealpath"])(nodeModulesPath); - const yarnIntegrityFilePath = Object(path__WEBPACK_IMPORTED_MODULE_0__["join"])(nodeModulesRealPath, '.yarn-integrity'); // check if the file already exists - if (await Object(_fs__WEBPACK_IMPORTED_MODULE_1__["isFile"])(yarnIntegrityFilePath)) { - return true; +var none = {}; +function combineLatest() { + var observables = []; + for (var _i = 0; _i < arguments.length; _i++) { + observables[_i] = arguments[_i]; } - } catch {// no-op - } - - return false; + var project = null; + if (typeof observables[observables.length - 1] === 'function') { + project = observables.pop(); + } + if (observables.length === 1 && Object(_util_isArray__WEBPACK_IMPORTED_MODULE_0__["isArray"])(observables[0])) { + observables = observables[0].slice(); + } + return function (source) { return source.lift.call(Object(_observable_from__WEBPACK_IMPORTED_MODULE_2__["from"])([source].concat(observables)), new _observable_combineLatest__WEBPACK_IMPORTED_MODULE_1__["CombineLatestOperator"](project)); }; } -async function ensureYarnIntegrityFileExists(nodeModulesPath) { - try { - const nodeModulesRealPath = await Object(_fs__WEBPACK_IMPORTED_MODULE_1__["tryRealpath"])(nodeModulesPath); - const yarnIntegrityFilePath = Object(path__WEBPACK_IMPORTED_MODULE_0__["join"])(nodeModulesRealPath, '.yarn-integrity'); // ensure node_modules folder is created - - await Object(_fs__WEBPACK_IMPORTED_MODULE_1__["mkdirp"])(nodeModulesRealPath); // write a blank file in case it doesn't exists +//# sourceMappingURL=combineLatest.js.map - await Object(_fs__WEBPACK_IMPORTED_MODULE_1__["writeFile"])(yarnIntegrityFilePath, '', { - flag: 'wx' - }); - } catch {// no-op - } -} /***/ }), -/* 415 */ +/* 435 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getBazelDiskCacheFolder", function() { return getBazelDiskCacheFolder; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getBazelRepositoryCacheFolder", function() { return getBazelRepositoryCacheFolder; }); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var _child_process__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(221); -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - - +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "concat", function() { return concat; }); +/* harmony import */ var _observable_concat__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(80); +/** PURE_IMPORTS_START _observable_concat PURE_IMPORTS_END */ -async function rawRunBazelInfoRepoCache() { - const { - stdout: bazelRepositoryCachePath - } = await Object(_child_process__WEBPACK_IMPORTED_MODULE_1__["spawn"])('bazel', ['info', 'repository_cache'], { - stdio: 'pipe' - }); - return bazelRepositoryCachePath; +function concat() { + var observables = []; + for (var _i = 0; _i < arguments.length; _i++) { + observables[_i] = arguments[_i]; + } + return function (source) { return source.lift.call(_observable_concat__WEBPACK_IMPORTED_MODULE_0__["concat"].apply(void 0, [source].concat(observables))); }; } +//# sourceMappingURL=concat.js.map -async function getBazelDiskCacheFolder() { - return Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(Object(path__WEBPACK_IMPORTED_MODULE_0__["dirname"])(await rawRunBazelInfoRepoCache()), 'disk-cache'); -} -async function getBazelRepositoryCacheFolder() { - return await rawRunBazelInfoRepoCache(); -} /***/ }), -/* 416 */ +/* 436 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isBazelBinAvailable", function() { return isBazelBinAvailable; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "installBazelTools", function() { return installBazelTools; }); -/* harmony import */ var dedent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2); -/* harmony import */ var dedent__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(dedent__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var _child_process__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(221); -/* harmony import */ var _fs__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(231); -/* harmony import */ var _log__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(220); -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - - - - - - -async function readBazelToolsVersionFile(repoRootPath, versionFilename) { - const version = (await Object(_fs__WEBPACK_IMPORTED_MODULE_3__["readFile"])(Object(path__WEBPACK_IMPORTED_MODULE_1__["resolve"])(repoRootPath, versionFilename))).toString().split('\n')[0]; - - if (!version) { - throw new Error(`[bazel_tools] Failed on reading bazel tools versions\n ${versionFilename} file do not contain any version set`); - } - - return version; -} +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "concatMap", function() { return concatMap; }); +/* harmony import */ var _mergeMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(83); +/** PURE_IMPORTS_START _mergeMap PURE_IMPORTS_END */ -async function isBazelBinAvailable() { - try { - await Object(_child_process__WEBPACK_IMPORTED_MODULE_2__["spawn"])('bazel', ['--version'], { - stdio: 'pipe' - }); - return true; - } catch { - return false; - } +function concatMap(project, resultSelector) { + return Object(_mergeMap__WEBPACK_IMPORTED_MODULE_0__["mergeMap"])(project, resultSelector, 1); } +//# sourceMappingURL=concatMap.js.map -async function isBazeliskInstalled(bazeliskVersion) { - try { - const { - stdout: bazeliskPkgInstallStdout - } = await Object(_child_process__WEBPACK_IMPORTED_MODULE_2__["spawn"])('npm', ['ls', '--global', '--parseable', '--long', `@bazel/bazelisk@${bazeliskVersion}`], { - stdio: 'pipe' - }); - return bazeliskPkgInstallStdout.includes(`@bazel/bazelisk@${bazeliskVersion}`); - } catch { - return false; - } -} -async function tryRemoveBazeliskFromYarnGlobal() { - try { - // Check if Bazelisk is installed on the yarn global scope - const { - stdout: bazeliskPkgInstallStdout - } = await Object(_child_process__WEBPACK_IMPORTED_MODULE_2__["spawn"])('yarn', ['global', 'list'], { - stdio: 'pipe' - }); // Bazelisk was found on yarn global scope so lets remove it +/***/ }), +/* 437 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { - if (bazeliskPkgInstallStdout.includes(`@bazel/bazelisk@`)) { - await Object(_child_process__WEBPACK_IMPORTED_MODULE_2__["spawn"])('yarn', ['global', 'remove', `@bazel/bazelisk`], { - stdio: 'pipe' - }); - _log__WEBPACK_IMPORTED_MODULE_4__["log"].info(`[bazel_tools] bazelisk was installed on Yarn global packages and is now removed`); - return true; - } +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "concatMapTo", function() { return concatMapTo; }); +/* harmony import */ var _concatMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(436); +/** PURE_IMPORTS_START _concatMap PURE_IMPORTS_END */ - return false; - } catch { - return false; - } +function concatMapTo(innerObservable, resultSelector) { + return Object(_concatMap__WEBPACK_IMPORTED_MODULE_0__["concatMap"])(function () { return innerObservable; }, resultSelector); } +//# sourceMappingURL=concatMapTo.js.map -async function installBazelTools(repoRootPath) { - _log__WEBPACK_IMPORTED_MODULE_4__["log"].debug(`[bazel_tools] reading bazel tools versions from version files`); - const bazeliskVersion = await readBazelToolsVersionFile(repoRootPath, '.bazeliskversion'); - const bazelVersion = await readBazelToolsVersionFile(repoRootPath, '.bazelversion'); // Check what globals are installed - - _log__WEBPACK_IMPORTED_MODULE_4__["log"].debug(`[bazel_tools] verify if bazelisk is installed`); // Check if we need to remove bazelisk from yarn - - await tryRemoveBazeliskFromYarnGlobal(); // Test if bazelisk is already installed in the correct version - - const isBazeliskPkgInstalled = await isBazeliskInstalled(bazeliskVersion); // Test if bazel bin is available - - const isBazelBinAlreadyAvailable = await isBazelBinAvailable(); // Install bazelisk if not installed - - if (!isBazeliskPkgInstalled || !isBazelBinAlreadyAvailable) { - _log__WEBPACK_IMPORTED_MODULE_4__["log"].info(`[bazel_tools] installing Bazel tools`); - _log__WEBPACK_IMPORTED_MODULE_4__["log"].debug(`[bazel_tools] bazelisk is not installed. Installing @bazel/bazelisk@${bazeliskVersion} and bazel@${bazelVersion}`); - await Object(_child_process__WEBPACK_IMPORTED_MODULE_2__["spawn"])('npm', ['install', '--global', `@bazel/bazelisk@${bazeliskVersion}`], { - env: { - USE_BAZEL_VERSION: bazelVersion - }, - stdio: 'pipe' - }); - const isBazelBinAvailableAfterInstall = await isBazelBinAvailable(); - - if (!isBazelBinAvailableAfterInstall) { - throw new Error(dedent__WEBPACK_IMPORTED_MODULE_0___default.a` - [bazel_tools] an error occurred when installing the Bazel tools. Please make sure you have access to npm globally installed modules on your $PATH - `); - } - } - - _log__WEBPACK_IMPORTED_MODULE_4__["log"].success(`[bazel_tools] all bazel tools are correctly installed`); -} /***/ }), -/* 417 */ +/* 438 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "runBazel", function() { return runBazel; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "runIBazel", function() { return runIBazel; }); -/* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(114); -/* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(chalk__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var rxjs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(9); -/* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(418); -/* harmony import */ var _kbn_dev_utils_stdio__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(516); -/* harmony import */ var _kbn_dev_utils_stdio__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_kbn_dev_utils_stdio__WEBPACK_IMPORTED_MODULE_3__); -/* harmony import */ var _child_process__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(221); -/* harmony import */ var _log__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(220); -/* harmony import */ var _errors__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(341); -function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } - -function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } - -function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } - -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - - - - - - - - -async function runBazelCommandWithRunner(bazelCommandRunner, bazelArgs, offline = false, runOpts = {}) { - // Force logs to pipe in order to control the output of them - const bazelOpts = _objectSpread(_objectSpread({}, runOpts), {}, { - stdio: 'pipe' - }); - - if (offline) { - bazelArgs = [...bazelArgs, '--config=offline']; - } - - const bazelProc = Object(_child_process__WEBPACK_IMPORTED_MODULE_4__["spawn"])(bazelCommandRunner, bazelArgs, bazelOpts); - const bazelLogs$ = new rxjs__WEBPACK_IMPORTED_MODULE_1__["Subject"](); // Bazel outputs machine readable output into stdout and human readable output goes to stderr. - // Therefore we need to get both. In order to get errors we need to parse the actual text line - - const bazelLogSubscription = rxjs__WEBPACK_IMPORTED_MODULE_1__["merge"](Object(_kbn_dev_utils_stdio__WEBPACK_IMPORTED_MODULE_3__["observeLines"])(bazelProc.stdout).pipe(Object(rxjs_operators__WEBPACK_IMPORTED_MODULE_2__["tap"])(line => _log__WEBPACK_IMPORTED_MODULE_5__["log"].info(`${chalk__WEBPACK_IMPORTED_MODULE_0___default.a.cyan(`[${bazelCommandRunner}]`)} ${line}`))), Object(_kbn_dev_utils_stdio__WEBPACK_IMPORTED_MODULE_3__["observeLines"])(bazelProc.stderr).pipe(Object(rxjs_operators__WEBPACK_IMPORTED_MODULE_2__["tap"])(line => _log__WEBPACK_IMPORTED_MODULE_5__["log"].info(`${chalk__WEBPACK_IMPORTED_MODULE_0___default.a.cyan(`[${bazelCommandRunner}]`)} ${line}`)))).subscribe(bazelLogs$); // Wait for process and logs to finish, unsubscribing in the end - - try { - await bazelProc; - } catch { - throw new _errors__WEBPACK_IMPORTED_MODULE_6__["CliError"](`The bazel command that was running failed to complete.`); - } +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "count", function() { return count; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); +/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ - await bazelLogs$.toPromise(); - await bazelLogSubscription.unsubscribe(); -} -async function runBazel(bazelArgs, offline = false, runOpts = {}) { - await runBazelCommandWithRunner('bazel', bazelArgs, offline, runOpts); +function count(predicate) { + return function (source) { return source.lift(new CountOperator(predicate, source)); }; } -async function runIBazel(bazelArgs, offline = false, runOpts = {}) { - const extendedEnv = _objectSpread({ - IBAZEL_USE_LEGACY_WATCHER: '0' - }, runOpts === null || runOpts === void 0 ? void 0 : runOpts.env); +var CountOperator = /*@__PURE__*/ (function () { + function CountOperator(predicate, source) { + this.predicate = predicate; + this.source = source; + } + CountOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new CountSubscriber(subscriber, this.predicate, this.source)); + }; + return CountOperator; +}()); +var CountSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](CountSubscriber, _super); + function CountSubscriber(destination, predicate, source) { + var _this = _super.call(this, destination) || this; + _this.predicate = predicate; + _this.source = source; + _this.count = 0; + _this.index = 0; + return _this; + } + CountSubscriber.prototype._next = function (value) { + if (this.predicate) { + this._tryPredicate(value); + } + else { + this.count++; + } + }; + CountSubscriber.prototype._tryPredicate = function (value) { + var result; + try { + result = this.predicate(value, this.index++, this.source); + } + catch (err) { + this.destination.error(err); + return; + } + if (result) { + this.count++; + } + }; + CountSubscriber.prototype._complete = function () { + this.destination.next(this.count); + this.destination.complete(); + }; + return CountSubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); +//# sourceMappingURL=count.js.map - await runBazelCommandWithRunner('ibazel', bazelArgs, offline, _objectSpread(_objectSpread({}, runOpts), {}, { - env: extendedEnv - })); -} /***/ }), -/* 418 */ +/* 439 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony import */ var _internal_operators_audit__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(419); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "audit", function() { return _internal_operators_audit__WEBPACK_IMPORTED_MODULE_0__["audit"]; }); - -/* harmony import */ var _internal_operators_auditTime__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(420); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "auditTime", function() { return _internal_operators_auditTime__WEBPACK_IMPORTED_MODULE_1__["auditTime"]; }); - -/* harmony import */ var _internal_operators_buffer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(421); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buffer", function() { return _internal_operators_buffer__WEBPACK_IMPORTED_MODULE_2__["buffer"]; }); - -/* harmony import */ var _internal_operators_bufferCount__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(422); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "bufferCount", function() { return _internal_operators_bufferCount__WEBPACK_IMPORTED_MODULE_3__["bufferCount"]; }); - -/* harmony import */ var _internal_operators_bufferTime__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(423); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "bufferTime", function() { return _internal_operators_bufferTime__WEBPACK_IMPORTED_MODULE_4__["bufferTime"]; }); - -/* harmony import */ var _internal_operators_bufferToggle__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(424); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "bufferToggle", function() { return _internal_operators_bufferToggle__WEBPACK_IMPORTED_MODULE_5__["bufferToggle"]; }); - -/* harmony import */ var _internal_operators_bufferWhen__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(425); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "bufferWhen", function() { return _internal_operators_bufferWhen__WEBPACK_IMPORTED_MODULE_6__["bufferWhen"]; }); - -/* harmony import */ var _internal_operators_catchError__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(426); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "catchError", function() { return _internal_operators_catchError__WEBPACK_IMPORTED_MODULE_7__["catchError"]; }); - -/* harmony import */ var _internal_operators_combineAll__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(427); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "combineAll", function() { return _internal_operators_combineAll__WEBPACK_IMPORTED_MODULE_8__["combineAll"]; }); - -/* harmony import */ var _internal_operators_combineLatest__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(428); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "combineLatest", function() { return _internal_operators_combineLatest__WEBPACK_IMPORTED_MODULE_9__["combineLatest"]; }); - -/* harmony import */ var _internal_operators_concat__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(429); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "concat", function() { return _internal_operators_concat__WEBPACK_IMPORTED_MODULE_10__["concat"]; }); - -/* harmony import */ var _internal_operators_concatAll__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(81); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "concatAll", function() { return _internal_operators_concatAll__WEBPACK_IMPORTED_MODULE_11__["concatAll"]; }); - -/* harmony import */ var _internal_operators_concatMap__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(430); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "concatMap", function() { return _internal_operators_concatMap__WEBPACK_IMPORTED_MODULE_12__["concatMap"]; }); - -/* harmony import */ var _internal_operators_concatMapTo__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(431); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "concatMapTo", function() { return _internal_operators_concatMapTo__WEBPACK_IMPORTED_MODULE_13__["concatMapTo"]; }); - -/* harmony import */ var _internal_operators_count__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(432); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "count", function() { return _internal_operators_count__WEBPACK_IMPORTED_MODULE_14__["count"]; }); - -/* harmony import */ var _internal_operators_debounce__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(433); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "debounce", function() { return _internal_operators_debounce__WEBPACK_IMPORTED_MODULE_15__["debounce"]; }); - -/* harmony import */ var _internal_operators_debounceTime__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(434); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "debounceTime", function() { return _internal_operators_debounceTime__WEBPACK_IMPORTED_MODULE_16__["debounceTime"]; }); - -/* harmony import */ var _internal_operators_defaultIfEmpty__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(435); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "defaultIfEmpty", function() { return _internal_operators_defaultIfEmpty__WEBPACK_IMPORTED_MODULE_17__["defaultIfEmpty"]; }); - -/* harmony import */ var _internal_operators_delay__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(436); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "delay", function() { return _internal_operators_delay__WEBPACK_IMPORTED_MODULE_18__["delay"]; }); - -/* harmony import */ var _internal_operators_delayWhen__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(438); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "delayWhen", function() { return _internal_operators_delayWhen__WEBPACK_IMPORTED_MODULE_19__["delayWhen"]; }); - -/* harmony import */ var _internal_operators_dematerialize__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(439); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "dematerialize", function() { return _internal_operators_dematerialize__WEBPACK_IMPORTED_MODULE_20__["dematerialize"]; }); - -/* harmony import */ var _internal_operators_distinct__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(440); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "distinct", function() { return _internal_operators_distinct__WEBPACK_IMPORTED_MODULE_21__["distinct"]; }); - -/* harmony import */ var _internal_operators_distinctUntilChanged__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(441); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "distinctUntilChanged", function() { return _internal_operators_distinctUntilChanged__WEBPACK_IMPORTED_MODULE_22__["distinctUntilChanged"]; }); - -/* harmony import */ var _internal_operators_distinctUntilKeyChanged__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(442); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "distinctUntilKeyChanged", function() { return _internal_operators_distinctUntilKeyChanged__WEBPACK_IMPORTED_MODULE_23__["distinctUntilKeyChanged"]; }); - -/* harmony import */ var _internal_operators_elementAt__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(443); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "elementAt", function() { return _internal_operators_elementAt__WEBPACK_IMPORTED_MODULE_24__["elementAt"]; }); - -/* harmony import */ var _internal_operators_endWith__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(446); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "endWith", function() { return _internal_operators_endWith__WEBPACK_IMPORTED_MODULE_25__["endWith"]; }); - -/* harmony import */ var _internal_operators_every__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(447); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "every", function() { return _internal_operators_every__WEBPACK_IMPORTED_MODULE_26__["every"]; }); - -/* harmony import */ var _internal_operators_exhaust__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(448); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "exhaust", function() { return _internal_operators_exhaust__WEBPACK_IMPORTED_MODULE_27__["exhaust"]; }); - -/* harmony import */ var _internal_operators_exhaustMap__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(449); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "exhaustMap", function() { return _internal_operators_exhaustMap__WEBPACK_IMPORTED_MODULE_28__["exhaustMap"]; }); - -/* harmony import */ var _internal_operators_expand__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(450); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "expand", function() { return _internal_operators_expand__WEBPACK_IMPORTED_MODULE_29__["expand"]; }); - -/* harmony import */ var _internal_operators_filter__WEBPACK_IMPORTED_MODULE_30__ = __webpack_require__(106); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "filter", function() { return _internal_operators_filter__WEBPACK_IMPORTED_MODULE_30__["filter"]; }); - -/* harmony import */ var _internal_operators_finalize__WEBPACK_IMPORTED_MODULE_31__ = __webpack_require__(451); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "finalize", function() { return _internal_operators_finalize__WEBPACK_IMPORTED_MODULE_31__["finalize"]; }); - -/* harmony import */ var _internal_operators_find__WEBPACK_IMPORTED_MODULE_32__ = __webpack_require__(452); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "find", function() { return _internal_operators_find__WEBPACK_IMPORTED_MODULE_32__["find"]; }); - -/* harmony import */ var _internal_operators_findIndex__WEBPACK_IMPORTED_MODULE_33__ = __webpack_require__(453); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "findIndex", function() { return _internal_operators_findIndex__WEBPACK_IMPORTED_MODULE_33__["findIndex"]; }); - -/* harmony import */ var _internal_operators_first__WEBPACK_IMPORTED_MODULE_34__ = __webpack_require__(454); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "first", function() { return _internal_operators_first__WEBPACK_IMPORTED_MODULE_34__["first"]; }); - -/* harmony import */ var _internal_operators_groupBy__WEBPACK_IMPORTED_MODULE_35__ = __webpack_require__(32); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "groupBy", function() { return _internal_operators_groupBy__WEBPACK_IMPORTED_MODULE_35__["groupBy"]; }); - -/* harmony import */ var _internal_operators_ignoreElements__WEBPACK_IMPORTED_MODULE_36__ = __webpack_require__(455); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ignoreElements", function() { return _internal_operators_ignoreElements__WEBPACK_IMPORTED_MODULE_36__["ignoreElements"]; }); - -/* harmony import */ var _internal_operators_isEmpty__WEBPACK_IMPORTED_MODULE_37__ = __webpack_require__(456); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "isEmpty", function() { return _internal_operators_isEmpty__WEBPACK_IMPORTED_MODULE_37__["isEmpty"]; }); - -/* harmony import */ var _internal_operators_last__WEBPACK_IMPORTED_MODULE_38__ = __webpack_require__(457); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "last", function() { return _internal_operators_last__WEBPACK_IMPORTED_MODULE_38__["last"]; }); - -/* harmony import */ var _internal_operators_map__WEBPACK_IMPORTED_MODULE_39__ = __webpack_require__(67); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "map", function() { return _internal_operators_map__WEBPACK_IMPORTED_MODULE_39__["map"]; }); - -/* harmony import */ var _internal_operators_mapTo__WEBPACK_IMPORTED_MODULE_40__ = __webpack_require__(459); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "mapTo", function() { return _internal_operators_mapTo__WEBPACK_IMPORTED_MODULE_40__["mapTo"]; }); - -/* harmony import */ var _internal_operators_materialize__WEBPACK_IMPORTED_MODULE_41__ = __webpack_require__(460); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "materialize", function() { return _internal_operators_materialize__WEBPACK_IMPORTED_MODULE_41__["materialize"]; }); - -/* harmony import */ var _internal_operators_max__WEBPACK_IMPORTED_MODULE_42__ = __webpack_require__(461); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "max", function() { return _internal_operators_max__WEBPACK_IMPORTED_MODULE_42__["max"]; }); - -/* harmony import */ var _internal_operators_merge__WEBPACK_IMPORTED_MODULE_43__ = __webpack_require__(464); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "merge", function() { return _internal_operators_merge__WEBPACK_IMPORTED_MODULE_43__["merge"]; }); - -/* harmony import */ var _internal_operators_mergeAll__WEBPACK_IMPORTED_MODULE_44__ = __webpack_require__(82); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "mergeAll", function() { return _internal_operators_mergeAll__WEBPACK_IMPORTED_MODULE_44__["mergeAll"]; }); - -/* harmony import */ var _internal_operators_mergeMap__WEBPACK_IMPORTED_MODULE_45__ = __webpack_require__(83); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "mergeMap", function() { return _internal_operators_mergeMap__WEBPACK_IMPORTED_MODULE_45__["mergeMap"]; }); - -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "flatMap", function() { return _internal_operators_mergeMap__WEBPACK_IMPORTED_MODULE_45__["flatMap"]; }); - -/* harmony import */ var _internal_operators_mergeMapTo__WEBPACK_IMPORTED_MODULE_46__ = __webpack_require__(465); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "mergeMapTo", function() { return _internal_operators_mergeMapTo__WEBPACK_IMPORTED_MODULE_46__["mergeMapTo"]; }); - -/* harmony import */ var _internal_operators_mergeScan__WEBPACK_IMPORTED_MODULE_47__ = __webpack_require__(466); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "mergeScan", function() { return _internal_operators_mergeScan__WEBPACK_IMPORTED_MODULE_47__["mergeScan"]; }); - -/* harmony import */ var _internal_operators_min__WEBPACK_IMPORTED_MODULE_48__ = __webpack_require__(467); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "min", function() { return _internal_operators_min__WEBPACK_IMPORTED_MODULE_48__["min"]; }); - -/* harmony import */ var _internal_operators_multicast__WEBPACK_IMPORTED_MODULE_49__ = __webpack_require__(468); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "multicast", function() { return _internal_operators_multicast__WEBPACK_IMPORTED_MODULE_49__["multicast"]; }); - -/* harmony import */ var _internal_operators_observeOn__WEBPACK_IMPORTED_MODULE_50__ = __webpack_require__(42); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "observeOn", function() { return _internal_operators_observeOn__WEBPACK_IMPORTED_MODULE_50__["observeOn"]; }); - -/* harmony import */ var _internal_operators_onErrorResumeNext__WEBPACK_IMPORTED_MODULE_51__ = __webpack_require__(469); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "onErrorResumeNext", function() { return _internal_operators_onErrorResumeNext__WEBPACK_IMPORTED_MODULE_51__["onErrorResumeNext"]; }); - -/* harmony import */ var _internal_operators_pairwise__WEBPACK_IMPORTED_MODULE_52__ = __webpack_require__(470); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "pairwise", function() { return _internal_operators_pairwise__WEBPACK_IMPORTED_MODULE_52__["pairwise"]; }); - -/* harmony import */ var _internal_operators_partition__WEBPACK_IMPORTED_MODULE_53__ = __webpack_require__(471); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "partition", function() { return _internal_operators_partition__WEBPACK_IMPORTED_MODULE_53__["partition"]; }); - -/* harmony import */ var _internal_operators_pluck__WEBPACK_IMPORTED_MODULE_54__ = __webpack_require__(472); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "pluck", function() { return _internal_operators_pluck__WEBPACK_IMPORTED_MODULE_54__["pluck"]; }); - -/* harmony import */ var _internal_operators_publish__WEBPACK_IMPORTED_MODULE_55__ = __webpack_require__(473); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "publish", function() { return _internal_operators_publish__WEBPACK_IMPORTED_MODULE_55__["publish"]; }); - -/* harmony import */ var _internal_operators_publishBehavior__WEBPACK_IMPORTED_MODULE_56__ = __webpack_require__(474); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "publishBehavior", function() { return _internal_operators_publishBehavior__WEBPACK_IMPORTED_MODULE_56__["publishBehavior"]; }); - -/* harmony import */ var _internal_operators_publishLast__WEBPACK_IMPORTED_MODULE_57__ = __webpack_require__(475); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "publishLast", function() { return _internal_operators_publishLast__WEBPACK_IMPORTED_MODULE_57__["publishLast"]; }); - -/* harmony import */ var _internal_operators_publishReplay__WEBPACK_IMPORTED_MODULE_58__ = __webpack_require__(476); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "publishReplay", function() { return _internal_operators_publishReplay__WEBPACK_IMPORTED_MODULE_58__["publishReplay"]; }); - -/* harmony import */ var _internal_operators_race__WEBPACK_IMPORTED_MODULE_59__ = __webpack_require__(477); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "race", function() { return _internal_operators_race__WEBPACK_IMPORTED_MODULE_59__["race"]; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "debounce", function() { return debounce; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(91); +/** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ -/* harmony import */ var _internal_operators_reduce__WEBPACK_IMPORTED_MODULE_60__ = __webpack_require__(462); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "reduce", function() { return _internal_operators_reduce__WEBPACK_IMPORTED_MODULE_60__["reduce"]; }); -/* harmony import */ var _internal_operators_repeat__WEBPACK_IMPORTED_MODULE_61__ = __webpack_require__(478); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "repeat", function() { return _internal_operators_repeat__WEBPACK_IMPORTED_MODULE_61__["repeat"]; }); +function debounce(durationSelector) { + return function (source) { return source.lift(new DebounceOperator(durationSelector)); }; +} +var DebounceOperator = /*@__PURE__*/ (function () { + function DebounceOperator(durationSelector) { + this.durationSelector = durationSelector; + } + DebounceOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new DebounceSubscriber(subscriber, this.durationSelector)); + }; + return DebounceOperator; +}()); +var DebounceSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](DebounceSubscriber, _super); + function DebounceSubscriber(destination, durationSelector) { + var _this = _super.call(this, destination) || this; + _this.durationSelector = durationSelector; + _this.hasValue = false; + return _this; + } + DebounceSubscriber.prototype._next = function (value) { + try { + var result = this.durationSelector.call(this, value); + if (result) { + this._tryNext(value, result); + } + } + catch (err) { + this.destination.error(err); + } + }; + DebounceSubscriber.prototype._complete = function () { + this.emitValue(); + this.destination.complete(); + }; + DebounceSubscriber.prototype._tryNext = function (value, duration) { + var subscription = this.durationSubscription; + this.value = value; + this.hasValue = true; + if (subscription) { + subscription.unsubscribe(); + this.remove(subscription); + } + subscription = Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["innerSubscribe"])(duration, new _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleInnerSubscriber"](this)); + if (subscription && !subscription.closed) { + this.add(this.durationSubscription = subscription); + } + }; + DebounceSubscriber.prototype.notifyNext = function () { + this.emitValue(); + }; + DebounceSubscriber.prototype.notifyComplete = function () { + this.emitValue(); + }; + DebounceSubscriber.prototype.emitValue = function () { + if (this.hasValue) { + var value = this.value; + var subscription = this.durationSubscription; + if (subscription) { + this.durationSubscription = undefined; + subscription.unsubscribe(); + this.remove(subscription); + } + this.value = undefined; + this.hasValue = false; + _super.prototype._next.call(this, value); + } + }; + return DebounceSubscriber; +}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleOuterSubscriber"])); +//# sourceMappingURL=debounce.js.map -/* harmony import */ var _internal_operators_repeatWhen__WEBPACK_IMPORTED_MODULE_62__ = __webpack_require__(479); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "repeatWhen", function() { return _internal_operators_repeatWhen__WEBPACK_IMPORTED_MODULE_62__["repeatWhen"]; }); -/* harmony import */ var _internal_operators_retry__WEBPACK_IMPORTED_MODULE_63__ = __webpack_require__(480); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "retry", function() { return _internal_operators_retry__WEBPACK_IMPORTED_MODULE_63__["retry"]; }); +/***/ }), +/* 440 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { -/* harmony import */ var _internal_operators_retryWhen__WEBPACK_IMPORTED_MODULE_64__ = __webpack_require__(481); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "retryWhen", function() { return _internal_operators_retryWhen__WEBPACK_IMPORTED_MODULE_64__["retryWhen"]; }); +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "debounceTime", function() { return debounceTime; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); +/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(56); +/** PURE_IMPORTS_START tslib,_Subscriber,_scheduler_async PURE_IMPORTS_END */ -/* harmony import */ var _internal_operators_refCount__WEBPACK_IMPORTED_MODULE_65__ = __webpack_require__(31); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "refCount", function() { return _internal_operators_refCount__WEBPACK_IMPORTED_MODULE_65__["refCount"]; }); -/* harmony import */ var _internal_operators_sample__WEBPACK_IMPORTED_MODULE_66__ = __webpack_require__(482); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "sample", function() { return _internal_operators_sample__WEBPACK_IMPORTED_MODULE_66__["sample"]; }); -/* harmony import */ var _internal_operators_sampleTime__WEBPACK_IMPORTED_MODULE_67__ = __webpack_require__(483); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "sampleTime", function() { return _internal_operators_sampleTime__WEBPACK_IMPORTED_MODULE_67__["sampleTime"]; }); +function debounceTime(dueTime, scheduler) { + if (scheduler === void 0) { + scheduler = _scheduler_async__WEBPACK_IMPORTED_MODULE_2__["async"]; + } + return function (source) { return source.lift(new DebounceTimeOperator(dueTime, scheduler)); }; +} +var DebounceTimeOperator = /*@__PURE__*/ (function () { + function DebounceTimeOperator(dueTime, scheduler) { + this.dueTime = dueTime; + this.scheduler = scheduler; + } + DebounceTimeOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new DebounceTimeSubscriber(subscriber, this.dueTime, this.scheduler)); + }; + return DebounceTimeOperator; +}()); +var DebounceTimeSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](DebounceTimeSubscriber, _super); + function DebounceTimeSubscriber(destination, dueTime, scheduler) { + var _this = _super.call(this, destination) || this; + _this.dueTime = dueTime; + _this.scheduler = scheduler; + _this.debouncedSubscription = null; + _this.lastValue = null; + _this.hasValue = false; + return _this; + } + DebounceTimeSubscriber.prototype._next = function (value) { + this.clearDebounce(); + this.lastValue = value; + this.hasValue = true; + this.add(this.debouncedSubscription = this.scheduler.schedule(dispatchNext, this.dueTime, this)); + }; + DebounceTimeSubscriber.prototype._complete = function () { + this.debouncedNext(); + this.destination.complete(); + }; + DebounceTimeSubscriber.prototype.debouncedNext = function () { + this.clearDebounce(); + if (this.hasValue) { + var lastValue = this.lastValue; + this.lastValue = null; + this.hasValue = false; + this.destination.next(lastValue); + } + }; + DebounceTimeSubscriber.prototype.clearDebounce = function () { + var debouncedSubscription = this.debouncedSubscription; + if (debouncedSubscription !== null) { + this.remove(debouncedSubscription); + debouncedSubscription.unsubscribe(); + this.debouncedSubscription = null; + } + }; + return DebounceTimeSubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); +function dispatchNext(subscriber) { + subscriber.debouncedNext(); +} +//# sourceMappingURL=debounceTime.js.map -/* harmony import */ var _internal_operators_scan__WEBPACK_IMPORTED_MODULE_68__ = __webpack_require__(463); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "scan", function() { return _internal_operators_scan__WEBPACK_IMPORTED_MODULE_68__["scan"]; }); -/* harmony import */ var _internal_operators_sequenceEqual__WEBPACK_IMPORTED_MODULE_69__ = __webpack_require__(484); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "sequenceEqual", function() { return _internal_operators_sequenceEqual__WEBPACK_IMPORTED_MODULE_69__["sequenceEqual"]; }); +/***/ }), +/* 441 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { -/* harmony import */ var _internal_operators_share__WEBPACK_IMPORTED_MODULE_70__ = __webpack_require__(485); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "share", function() { return _internal_operators_share__WEBPACK_IMPORTED_MODULE_70__["share"]; }); +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "defaultIfEmpty", function() { return defaultIfEmpty; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); +/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ -/* harmony import */ var _internal_operators_shareReplay__WEBPACK_IMPORTED_MODULE_71__ = __webpack_require__(486); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "shareReplay", function() { return _internal_operators_shareReplay__WEBPACK_IMPORTED_MODULE_71__["shareReplay"]; }); -/* harmony import */ var _internal_operators_single__WEBPACK_IMPORTED_MODULE_72__ = __webpack_require__(487); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "single", function() { return _internal_operators_single__WEBPACK_IMPORTED_MODULE_72__["single"]; }); +function defaultIfEmpty(defaultValue) { + if (defaultValue === void 0) { + defaultValue = null; + } + return function (source) { return source.lift(new DefaultIfEmptyOperator(defaultValue)); }; +} +var DefaultIfEmptyOperator = /*@__PURE__*/ (function () { + function DefaultIfEmptyOperator(defaultValue) { + this.defaultValue = defaultValue; + } + DefaultIfEmptyOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new DefaultIfEmptySubscriber(subscriber, this.defaultValue)); + }; + return DefaultIfEmptyOperator; +}()); +var DefaultIfEmptySubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](DefaultIfEmptySubscriber, _super); + function DefaultIfEmptySubscriber(destination, defaultValue) { + var _this = _super.call(this, destination) || this; + _this.defaultValue = defaultValue; + _this.isEmpty = true; + return _this; + } + DefaultIfEmptySubscriber.prototype._next = function (value) { + this.isEmpty = false; + this.destination.next(value); + }; + DefaultIfEmptySubscriber.prototype._complete = function () { + if (this.isEmpty) { + this.destination.next(this.defaultValue); + } + this.destination.complete(); + }; + return DefaultIfEmptySubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); +//# sourceMappingURL=defaultIfEmpty.js.map -/* harmony import */ var _internal_operators_skip__WEBPACK_IMPORTED_MODULE_73__ = __webpack_require__(488); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "skip", function() { return _internal_operators_skip__WEBPACK_IMPORTED_MODULE_73__["skip"]; }); -/* harmony import */ var _internal_operators_skipLast__WEBPACK_IMPORTED_MODULE_74__ = __webpack_require__(489); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "skipLast", function() { return _internal_operators_skipLast__WEBPACK_IMPORTED_MODULE_74__["skipLast"]; }); +/***/ }), +/* 442 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { -/* harmony import */ var _internal_operators_skipUntil__WEBPACK_IMPORTED_MODULE_75__ = __webpack_require__(490); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "skipUntil", function() { return _internal_operators_skipUntil__WEBPACK_IMPORTED_MODULE_75__["skipUntil"]; }); +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "delay", function() { return delay; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); +/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(56); +/* harmony import */ var _util_isDate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(443); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(12); +/* harmony import */ var _Notification__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(43); +/** PURE_IMPORTS_START tslib,_scheduler_async,_util_isDate,_Subscriber,_Notification PURE_IMPORTS_END */ -/* harmony import */ var _internal_operators_skipWhile__WEBPACK_IMPORTED_MODULE_76__ = __webpack_require__(491); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "skipWhile", function() { return _internal_operators_skipWhile__WEBPACK_IMPORTED_MODULE_76__["skipWhile"]; }); -/* harmony import */ var _internal_operators_startWith__WEBPACK_IMPORTED_MODULE_77__ = __webpack_require__(492); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "startWith", function() { return _internal_operators_startWith__WEBPACK_IMPORTED_MODULE_77__["startWith"]; }); -/* harmony import */ var _internal_operators_subscribeOn__WEBPACK_IMPORTED_MODULE_78__ = __webpack_require__(493); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "subscribeOn", function() { return _internal_operators_subscribeOn__WEBPACK_IMPORTED_MODULE_78__["subscribeOn"]; }); -/* harmony import */ var _internal_operators_switchAll__WEBPACK_IMPORTED_MODULE_79__ = __webpack_require__(495); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "switchAll", function() { return _internal_operators_switchAll__WEBPACK_IMPORTED_MODULE_79__["switchAll"]; }); -/* harmony import */ var _internal_operators_switchMap__WEBPACK_IMPORTED_MODULE_80__ = __webpack_require__(496); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "switchMap", function() { return _internal_operators_switchMap__WEBPACK_IMPORTED_MODULE_80__["switchMap"]; }); +function delay(delay, scheduler) { + if (scheduler === void 0) { + scheduler = _scheduler_async__WEBPACK_IMPORTED_MODULE_1__["async"]; + } + var absoluteDelay = Object(_util_isDate__WEBPACK_IMPORTED_MODULE_2__["isDate"])(delay); + var delayFor = absoluteDelay ? (+delay - scheduler.now()) : Math.abs(delay); + return function (source) { return source.lift(new DelayOperator(delayFor, scheduler)); }; +} +var DelayOperator = /*@__PURE__*/ (function () { + function DelayOperator(delay, scheduler) { + this.delay = delay; + this.scheduler = scheduler; + } + DelayOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new DelaySubscriber(subscriber, this.delay, this.scheduler)); + }; + return DelayOperator; +}()); +var DelaySubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](DelaySubscriber, _super); + function DelaySubscriber(destination, delay, scheduler) { + var _this = _super.call(this, destination) || this; + _this.delay = delay; + _this.scheduler = scheduler; + _this.queue = []; + _this.active = false; + _this.errored = false; + return _this; + } + DelaySubscriber.dispatch = function (state) { + var source = state.source; + var queue = source.queue; + var scheduler = state.scheduler; + var destination = state.destination; + while (queue.length > 0 && (queue[0].time - scheduler.now()) <= 0) { + queue.shift().notification.observe(destination); + } + if (queue.length > 0) { + var delay_1 = Math.max(0, queue[0].time - scheduler.now()); + this.schedule(state, delay_1); + } + else { + this.unsubscribe(); + source.active = false; + } + }; + DelaySubscriber.prototype._schedule = function (scheduler) { + this.active = true; + var destination = this.destination; + destination.add(scheduler.schedule(DelaySubscriber.dispatch, this.delay, { + source: this, destination: this.destination, scheduler: scheduler + })); + }; + DelaySubscriber.prototype.scheduleNotification = function (notification) { + if (this.errored === true) { + return; + } + var scheduler = this.scheduler; + var message = new DelayMessage(scheduler.now() + this.delay, notification); + this.queue.push(message); + if (this.active === false) { + this._schedule(scheduler); + } + }; + DelaySubscriber.prototype._next = function (value) { + this.scheduleNotification(_Notification__WEBPACK_IMPORTED_MODULE_4__["Notification"].createNext(value)); + }; + DelaySubscriber.prototype._error = function (err) { + this.errored = true; + this.queue = []; + this.destination.error(err); + this.unsubscribe(); + }; + DelaySubscriber.prototype._complete = function () { + this.scheduleNotification(_Notification__WEBPACK_IMPORTED_MODULE_4__["Notification"].createComplete()); + this.unsubscribe(); + }; + return DelaySubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_3__["Subscriber"])); +var DelayMessage = /*@__PURE__*/ (function () { + function DelayMessage(time, notification) { + this.time = time; + this.notification = notification; + } + return DelayMessage; +}()); +//# sourceMappingURL=delay.js.map -/* harmony import */ var _internal_operators_switchMapTo__WEBPACK_IMPORTED_MODULE_81__ = __webpack_require__(497); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "switchMapTo", function() { return _internal_operators_switchMapTo__WEBPACK_IMPORTED_MODULE_81__["switchMapTo"]; }); -/* harmony import */ var _internal_operators_take__WEBPACK_IMPORTED_MODULE_82__ = __webpack_require__(445); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "take", function() { return _internal_operators_take__WEBPACK_IMPORTED_MODULE_82__["take"]; }); +/***/ }), +/* 443 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { -/* harmony import */ var _internal_operators_takeLast__WEBPACK_IMPORTED_MODULE_83__ = __webpack_require__(458); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "takeLast", function() { return _internal_operators_takeLast__WEBPACK_IMPORTED_MODULE_83__["takeLast"]; }); +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isDate", function() { return isDate; }); +/** PURE_IMPORTS_START PURE_IMPORTS_END */ +function isDate(value) { + return value instanceof Date && !isNaN(+value); +} +//# sourceMappingURL=isDate.js.map -/* harmony import */ var _internal_operators_takeUntil__WEBPACK_IMPORTED_MODULE_84__ = __webpack_require__(498); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "takeUntil", function() { return _internal_operators_takeUntil__WEBPACK_IMPORTED_MODULE_84__["takeUntil"]; }); -/* harmony import */ var _internal_operators_takeWhile__WEBPACK_IMPORTED_MODULE_85__ = __webpack_require__(499); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "takeWhile", function() { return _internal_operators_takeWhile__WEBPACK_IMPORTED_MODULE_85__["takeWhile"]; }); +/***/ }), +/* 444 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { -/* harmony import */ var _internal_operators_tap__WEBPACK_IMPORTED_MODULE_86__ = __webpack_require__(500); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "tap", function() { return _internal_operators_tap__WEBPACK_IMPORTED_MODULE_86__["tap"]; }); +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "delayWhen", function() { return delayWhen; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); +/* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(10); +/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(70); +/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(71); +/** PURE_IMPORTS_START tslib,_Subscriber,_Observable,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ -/* harmony import */ var _internal_operators_throttle__WEBPACK_IMPORTED_MODULE_87__ = __webpack_require__(501); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "throttle", function() { return _internal_operators_throttle__WEBPACK_IMPORTED_MODULE_87__["throttle"]; }); -/* harmony import */ var _internal_operators_throttleTime__WEBPACK_IMPORTED_MODULE_88__ = __webpack_require__(502); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "throttleTime", function() { return _internal_operators_throttleTime__WEBPACK_IMPORTED_MODULE_88__["throttleTime"]; }); -/* harmony import */ var _internal_operators_throwIfEmpty__WEBPACK_IMPORTED_MODULE_89__ = __webpack_require__(444); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "throwIfEmpty", function() { return _internal_operators_throwIfEmpty__WEBPACK_IMPORTED_MODULE_89__["throwIfEmpty"]; }); -/* harmony import */ var _internal_operators_timeInterval__WEBPACK_IMPORTED_MODULE_90__ = __webpack_require__(503); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "timeInterval", function() { return _internal_operators_timeInterval__WEBPACK_IMPORTED_MODULE_90__["timeInterval"]; }); -/* harmony import */ var _internal_operators_timeout__WEBPACK_IMPORTED_MODULE_91__ = __webpack_require__(504); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "timeout", function() { return _internal_operators_timeout__WEBPACK_IMPORTED_MODULE_91__["timeout"]; }); +function delayWhen(delayDurationSelector, subscriptionDelay) { + if (subscriptionDelay) { + return function (source) { + return new SubscriptionDelayObservable(source, subscriptionDelay) + .lift(new DelayWhenOperator(delayDurationSelector)); + }; + } + return function (source) { return source.lift(new DelayWhenOperator(delayDurationSelector)); }; +} +var DelayWhenOperator = /*@__PURE__*/ (function () { + function DelayWhenOperator(delayDurationSelector) { + this.delayDurationSelector = delayDurationSelector; + } + DelayWhenOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new DelayWhenSubscriber(subscriber, this.delayDurationSelector)); + }; + return DelayWhenOperator; +}()); +var DelayWhenSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](DelayWhenSubscriber, _super); + function DelayWhenSubscriber(destination, delayDurationSelector) { + var _this = _super.call(this, destination) || this; + _this.delayDurationSelector = delayDurationSelector; + _this.completed = false; + _this.delayNotifierSubscriptions = []; + _this.index = 0; + return _this; + } + DelayWhenSubscriber.prototype.notifyNext = function (outerValue, _innerValue, _outerIndex, _innerIndex, innerSub) { + this.destination.next(outerValue); + this.removeSubscription(innerSub); + this.tryComplete(); + }; + DelayWhenSubscriber.prototype.notifyError = function (error, innerSub) { + this._error(error); + }; + DelayWhenSubscriber.prototype.notifyComplete = function (innerSub) { + var value = this.removeSubscription(innerSub); + if (value) { + this.destination.next(value); + } + this.tryComplete(); + }; + DelayWhenSubscriber.prototype._next = function (value) { + var index = this.index++; + try { + var delayNotifier = this.delayDurationSelector(value, index); + if (delayNotifier) { + this.tryDelay(delayNotifier, value); + } + } + catch (err) { + this.destination.error(err); + } + }; + DelayWhenSubscriber.prototype._complete = function () { + this.completed = true; + this.tryComplete(); + this.unsubscribe(); + }; + DelayWhenSubscriber.prototype.removeSubscription = function (subscription) { + subscription.unsubscribe(); + var subscriptionIdx = this.delayNotifierSubscriptions.indexOf(subscription); + if (subscriptionIdx !== -1) { + this.delayNotifierSubscriptions.splice(subscriptionIdx, 1); + } + return subscription.outerValue; + }; + DelayWhenSubscriber.prototype.tryDelay = function (delayNotifier, value) { + var notifierSubscription = Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__["subscribeToResult"])(this, delayNotifier, value); + if (notifierSubscription && !notifierSubscription.closed) { + var destination = this.destination; + destination.add(notifierSubscription); + this.delayNotifierSubscriptions.push(notifierSubscription); + } + }; + DelayWhenSubscriber.prototype.tryComplete = function () { + if (this.completed && this.delayNotifierSubscriptions.length === 0) { + this.destination.complete(); + } + }; + return DelayWhenSubscriber; +}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__["OuterSubscriber"])); +var SubscriptionDelayObservable = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](SubscriptionDelayObservable, _super); + function SubscriptionDelayObservable(source, subscriptionDelay) { + var _this = _super.call(this) || this; + _this.source = source; + _this.subscriptionDelay = subscriptionDelay; + return _this; + } + SubscriptionDelayObservable.prototype._subscribe = function (subscriber) { + this.subscriptionDelay.subscribe(new SubscriptionDelaySubscriber(subscriber, this.source)); + }; + return SubscriptionDelayObservable; +}(_Observable__WEBPACK_IMPORTED_MODULE_2__["Observable"])); +var SubscriptionDelaySubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](SubscriptionDelaySubscriber, _super); + function SubscriptionDelaySubscriber(parent, source) { + var _this = _super.call(this) || this; + _this.parent = parent; + _this.source = source; + _this.sourceSubscribed = false; + return _this; + } + SubscriptionDelaySubscriber.prototype._next = function (unused) { + this.subscribeToSource(); + }; + SubscriptionDelaySubscriber.prototype._error = function (err) { + this.unsubscribe(); + this.parent.error(err); + }; + SubscriptionDelaySubscriber.prototype._complete = function () { + this.unsubscribe(); + this.subscribeToSource(); + }; + SubscriptionDelaySubscriber.prototype.subscribeToSource = function () { + if (!this.sourceSubscribed) { + this.sourceSubscribed = true; + this.unsubscribe(); + this.source.subscribe(this.parent); + } + }; + return SubscriptionDelaySubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); +//# sourceMappingURL=delayWhen.js.map -/* harmony import */ var _internal_operators_timeoutWith__WEBPACK_IMPORTED_MODULE_92__ = __webpack_require__(505); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "timeoutWith", function() { return _internal_operators_timeoutWith__WEBPACK_IMPORTED_MODULE_92__["timeoutWith"]; }); -/* harmony import */ var _internal_operators_timestamp__WEBPACK_IMPORTED_MODULE_93__ = __webpack_require__(506); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "timestamp", function() { return _internal_operators_timestamp__WEBPACK_IMPORTED_MODULE_93__["timestamp"]; }); +/***/ }), +/* 445 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { -/* harmony import */ var _internal_operators_toArray__WEBPACK_IMPORTED_MODULE_94__ = __webpack_require__(507); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "toArray", function() { return _internal_operators_toArray__WEBPACK_IMPORTED_MODULE_94__["toArray"]; }); +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "dematerialize", function() { return dematerialize; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); +/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ -/* harmony import */ var _internal_operators_window__WEBPACK_IMPORTED_MODULE_95__ = __webpack_require__(508); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "window", function() { return _internal_operators_window__WEBPACK_IMPORTED_MODULE_95__["window"]; }); -/* harmony import */ var _internal_operators_windowCount__WEBPACK_IMPORTED_MODULE_96__ = __webpack_require__(509); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowCount", function() { return _internal_operators_windowCount__WEBPACK_IMPORTED_MODULE_96__["windowCount"]; }); +function dematerialize() { + return function dematerializeOperatorFunction(source) { + return source.lift(new DeMaterializeOperator()); + }; +} +var DeMaterializeOperator = /*@__PURE__*/ (function () { + function DeMaterializeOperator() { + } + DeMaterializeOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new DeMaterializeSubscriber(subscriber)); + }; + return DeMaterializeOperator; +}()); +var DeMaterializeSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](DeMaterializeSubscriber, _super); + function DeMaterializeSubscriber(destination) { + return _super.call(this, destination) || this; + } + DeMaterializeSubscriber.prototype._next = function (value) { + value.observe(this.destination); + }; + return DeMaterializeSubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); +//# sourceMappingURL=dematerialize.js.map -/* harmony import */ var _internal_operators_windowTime__WEBPACK_IMPORTED_MODULE_97__ = __webpack_require__(510); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowTime", function() { return _internal_operators_windowTime__WEBPACK_IMPORTED_MODULE_97__["windowTime"]; }); -/* harmony import */ var _internal_operators_windowToggle__WEBPACK_IMPORTED_MODULE_98__ = __webpack_require__(511); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowToggle", function() { return _internal_operators_windowToggle__WEBPACK_IMPORTED_MODULE_98__["windowToggle"]; }); +/***/ }), +/* 446 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { -/* harmony import */ var _internal_operators_windowWhen__WEBPACK_IMPORTED_MODULE_99__ = __webpack_require__(512); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowWhen", function() { return _internal_operators_windowWhen__WEBPACK_IMPORTED_MODULE_99__["windowWhen"]; }); +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "distinct", function() { return distinct; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "DistinctSubscriber", function() { return DistinctSubscriber; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(91); +/** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ -/* harmony import */ var _internal_operators_withLatestFrom__WEBPACK_IMPORTED_MODULE_100__ = __webpack_require__(513); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "withLatestFrom", function() { return _internal_operators_withLatestFrom__WEBPACK_IMPORTED_MODULE_100__["withLatestFrom"]; }); -/* harmony import */ var _internal_operators_zip__WEBPACK_IMPORTED_MODULE_101__ = __webpack_require__(514); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "zip", function() { return _internal_operators_zip__WEBPACK_IMPORTED_MODULE_101__["zip"]; }); +function distinct(keySelector, flushes) { + return function (source) { return source.lift(new DistinctOperator(keySelector, flushes)); }; +} +var DistinctOperator = /*@__PURE__*/ (function () { + function DistinctOperator(keySelector, flushes) { + this.keySelector = keySelector; + this.flushes = flushes; + } + DistinctOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new DistinctSubscriber(subscriber, this.keySelector, this.flushes)); + }; + return DistinctOperator; +}()); +var DistinctSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](DistinctSubscriber, _super); + function DistinctSubscriber(destination, keySelector, flushes) { + var _this = _super.call(this, destination) || this; + _this.keySelector = keySelector; + _this.values = new Set(); + if (flushes) { + _this.add(Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["innerSubscribe"])(flushes, new _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleInnerSubscriber"](_this))); + } + return _this; + } + DistinctSubscriber.prototype.notifyNext = function () { + this.values.clear(); + }; + DistinctSubscriber.prototype.notifyError = function (error) { + this._error(error); + }; + DistinctSubscriber.prototype._next = function (value) { + if (this.keySelector) { + this._useKeySelector(value); + } + else { + this._finalizeNext(value, value); + } + }; + DistinctSubscriber.prototype._useKeySelector = function (value) { + var key; + var destination = this.destination; + try { + key = this.keySelector(value); + } + catch (err) { + destination.error(err); + return; + } + this._finalizeNext(key, value); + }; + DistinctSubscriber.prototype._finalizeNext = function (key, value) { + var values = this.values; + if (!values.has(key)) { + values.add(key); + this.destination.next(value); + } + }; + return DistinctSubscriber; +}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleOuterSubscriber"])); -/* harmony import */ var _internal_operators_zipAll__WEBPACK_IMPORTED_MODULE_102__ = __webpack_require__(515); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "zipAll", function() { return _internal_operators_zipAll__WEBPACK_IMPORTED_MODULE_102__["zipAll"]; }); +//# sourceMappingURL=distinct.js.map -/** PURE_IMPORTS_START PURE_IMPORTS_END */ +/***/ }), +/* 447 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "distinctUntilChanged", function() { return distinctUntilChanged; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); +/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ +function distinctUntilChanged(compare, keySelector) { + return function (source) { return source.lift(new DistinctUntilChangedOperator(compare, keySelector)); }; +} +var DistinctUntilChangedOperator = /*@__PURE__*/ (function () { + function DistinctUntilChangedOperator(compare, keySelector) { + this.compare = compare; + this.keySelector = keySelector; + } + DistinctUntilChangedOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new DistinctUntilChangedSubscriber(subscriber, this.compare, this.keySelector)); + }; + return DistinctUntilChangedOperator; +}()); +var DistinctUntilChangedSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](DistinctUntilChangedSubscriber, _super); + function DistinctUntilChangedSubscriber(destination, compare, keySelector) { + var _this = _super.call(this, destination) || this; + _this.keySelector = keySelector; + _this.hasKey = false; + if (typeof compare === 'function') { + _this.compare = compare; + } + return _this; + } + DistinctUntilChangedSubscriber.prototype.compare = function (x, y) { + return x === y; + }; + DistinctUntilChangedSubscriber.prototype._next = function (value) { + var key; + try { + var keySelector = this.keySelector; + key = keySelector ? keySelector(value) : value; + } + catch (err) { + return this.destination.error(err); + } + var result = false; + if (this.hasKey) { + try { + var compare = this.compare; + result = compare(this.key, key); + } + catch (err) { + return this.destination.error(err); + } + } + else { + this.hasKey = true; + } + if (!result) { + this.key = key; + this.destination.next(value); + } + }; + return DistinctUntilChangedSubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); +//# sourceMappingURL=distinctUntilChanged.js.map +/***/ }), +/* 448 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "distinctUntilKeyChanged", function() { return distinctUntilKeyChanged; }); +/* harmony import */ var _distinctUntilChanged__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(447); +/** PURE_IMPORTS_START _distinctUntilChanged PURE_IMPORTS_END */ +function distinctUntilKeyChanged(key, compare) { + return Object(_distinctUntilChanged__WEBPACK_IMPORTED_MODULE_0__["distinctUntilChanged"])(function (x, y) { return compare ? compare(x[key], y[key]) : x[key] === y[key]; }); +} +//# sourceMappingURL=distinctUntilKeyChanged.js.map +/***/ }), +/* 449 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "elementAt", function() { return elementAt; }); +/* harmony import */ var _util_ArgumentOutOfRangeError__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(63); +/* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(106); +/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(450); +/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(441); +/* harmony import */ var _take__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(451); +/** PURE_IMPORTS_START _util_ArgumentOutOfRangeError,_filter,_throwIfEmpty,_defaultIfEmpty,_take PURE_IMPORTS_END */ +function elementAt(index, defaultValue) { + if (index < 0) { + throw new _util_ArgumentOutOfRangeError__WEBPACK_IMPORTED_MODULE_0__["ArgumentOutOfRangeError"](); + } + var hasDefaultValue = arguments.length >= 2; + return function (source) { + return source.pipe(Object(_filter__WEBPACK_IMPORTED_MODULE_1__["filter"])(function (v, i) { return i === index; }), Object(_take__WEBPACK_IMPORTED_MODULE_4__["take"])(1), hasDefaultValue + ? Object(_defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__["defaultIfEmpty"])(defaultValue) + : Object(_throwIfEmpty__WEBPACK_IMPORTED_MODULE_2__["throwIfEmpty"])(function () { return new _util_ArgumentOutOfRangeError__WEBPACK_IMPORTED_MODULE_0__["ArgumentOutOfRangeError"](); })); + }; +} +//# sourceMappingURL=elementAt.js.map +/***/ }), +/* 450 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "throwIfEmpty", function() { return throwIfEmpty; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); +/* harmony import */ var _util_EmptyError__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(64); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(12); +/** PURE_IMPORTS_START tslib,_util_EmptyError,_Subscriber PURE_IMPORTS_END */ +function throwIfEmpty(errorFactory) { + if (errorFactory === void 0) { + errorFactory = defaultErrorFactory; + } + return function (source) { + return source.lift(new ThrowIfEmptyOperator(errorFactory)); + }; +} +var ThrowIfEmptyOperator = /*@__PURE__*/ (function () { + function ThrowIfEmptyOperator(errorFactory) { + this.errorFactory = errorFactory; + } + ThrowIfEmptyOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new ThrowIfEmptySubscriber(subscriber, this.errorFactory)); + }; + return ThrowIfEmptyOperator; +}()); +var ThrowIfEmptySubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](ThrowIfEmptySubscriber, _super); + function ThrowIfEmptySubscriber(destination, errorFactory) { + var _this = _super.call(this, destination) || this; + _this.errorFactory = errorFactory; + _this.hasValue = false; + return _this; + } + ThrowIfEmptySubscriber.prototype._next = function (value) { + this.hasValue = true; + this.destination.next(value); + }; + ThrowIfEmptySubscriber.prototype._complete = function () { + if (!this.hasValue) { + var err = void 0; + try { + err = this.errorFactory(); + } + catch (e) { + err = e; + } + this.destination.error(err); + } + else { + return this.destination.complete(); + } + }; + return ThrowIfEmptySubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_2__["Subscriber"])); +function defaultErrorFactory() { + return new _util_EmptyError__WEBPACK_IMPORTED_MODULE_1__["EmptyError"](); +} +//# sourceMappingURL=throwIfEmpty.js.map +/***/ }), +/* 451 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "take", function() { return take; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); +/* harmony import */ var _util_ArgumentOutOfRangeError__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(63); +/* harmony import */ var _observable_empty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(44); +/** PURE_IMPORTS_START tslib,_Subscriber,_util_ArgumentOutOfRangeError,_observable_empty PURE_IMPORTS_END */ +function take(count) { + return function (source) { + if (count === 0) { + return Object(_observable_empty__WEBPACK_IMPORTED_MODULE_3__["empty"])(); + } + else { + return source.lift(new TakeOperator(count)); + } + }; +} +var TakeOperator = /*@__PURE__*/ (function () { + function TakeOperator(total) { + this.total = total; + if (this.total < 0) { + throw new _util_ArgumentOutOfRangeError__WEBPACK_IMPORTED_MODULE_2__["ArgumentOutOfRangeError"]; + } + } + TakeOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new TakeSubscriber(subscriber, this.total)); + }; + return TakeOperator; +}()); +var TakeSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](TakeSubscriber, _super); + function TakeSubscriber(destination, total) { + var _this = _super.call(this, destination) || this; + _this.total = total; + _this.count = 0; + return _this; + } + TakeSubscriber.prototype._next = function (value) { + var total = this.total; + var count = ++this.count; + if (count <= total) { + this.destination.next(value); + if (count === total) { + this.destination.complete(); + this.unsubscribe(); + } + } + }; + return TakeSubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); +//# sourceMappingURL=take.js.map +/***/ }), +/* 452 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "endWith", function() { return endWith; }); +/* harmony import */ var _observable_concat__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(80); +/* harmony import */ var _observable_of__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(45); +/** PURE_IMPORTS_START _observable_concat,_observable_of PURE_IMPORTS_END */ +function endWith() { + var array = []; + for (var _i = 0; _i < arguments.length; _i++) { + array[_i] = arguments[_i]; + } + return function (source) { return Object(_observable_concat__WEBPACK_IMPORTED_MODULE_0__["concat"])(source, _observable_of__WEBPACK_IMPORTED_MODULE_1__["of"].apply(void 0, array)); }; +} +//# sourceMappingURL=endWith.js.map +/***/ }), +/* 453 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "every", function() { return every; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); +/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ +function every(predicate, thisArg) { + return function (source) { return source.lift(new EveryOperator(predicate, thisArg, source)); }; +} +var EveryOperator = /*@__PURE__*/ (function () { + function EveryOperator(predicate, thisArg, source) { + this.predicate = predicate; + this.thisArg = thisArg; + this.source = source; + } + EveryOperator.prototype.call = function (observer, source) { + return source.subscribe(new EverySubscriber(observer, this.predicate, this.thisArg, this.source)); + }; + return EveryOperator; +}()); +var EverySubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](EverySubscriber, _super); + function EverySubscriber(destination, predicate, thisArg, source) { + var _this = _super.call(this, destination) || this; + _this.predicate = predicate; + _this.thisArg = thisArg; + _this.source = source; + _this.index = 0; + _this.thisArg = thisArg || _this; + return _this; + } + EverySubscriber.prototype.notifyComplete = function (everyValueMatch) { + this.destination.next(everyValueMatch); + this.destination.complete(); + }; + EverySubscriber.prototype._next = function (value) { + var result = false; + try { + result = this.predicate.call(this.thisArg, value, this.index++, this.source); + } + catch (err) { + this.destination.error(err); + return; + } + if (!result) { + this.notifyComplete(false); + } + }; + EverySubscriber.prototype._complete = function () { + this.notifyComplete(true); + }; + return EverySubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); +//# sourceMappingURL=every.js.map +/***/ }), +/* 454 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "exhaust", function() { return exhaust; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(91); +/** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ +function exhaust() { + return function (source) { return source.lift(new SwitchFirstOperator()); }; +} +var SwitchFirstOperator = /*@__PURE__*/ (function () { + function SwitchFirstOperator() { + } + SwitchFirstOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new SwitchFirstSubscriber(subscriber)); + }; + return SwitchFirstOperator; +}()); +var SwitchFirstSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](SwitchFirstSubscriber, _super); + function SwitchFirstSubscriber(destination) { + var _this = _super.call(this, destination) || this; + _this.hasCompleted = false; + _this.hasSubscription = false; + return _this; + } + SwitchFirstSubscriber.prototype._next = function (value) { + if (!this.hasSubscription) { + this.hasSubscription = true; + this.add(Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["innerSubscribe"])(value, new _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleInnerSubscriber"](this))); + } + }; + SwitchFirstSubscriber.prototype._complete = function () { + this.hasCompleted = true; + if (!this.hasSubscription) { + this.destination.complete(); + } + }; + SwitchFirstSubscriber.prototype.notifyComplete = function () { + this.hasSubscription = false; + if (this.hasCompleted) { + this.destination.complete(); + } + }; + return SwitchFirstSubscriber; +}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleOuterSubscriber"])); +//# sourceMappingURL=exhaust.js.map +/***/ }), +/* 455 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "exhaustMap", function() { return exhaustMap; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); +/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(67); +/* harmony import */ var _observable_from__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(84); +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(91); +/** PURE_IMPORTS_START tslib,_map,_observable_from,_innerSubscribe PURE_IMPORTS_END */ +function exhaustMap(project, resultSelector) { + if (resultSelector) { + return function (source) { return source.pipe(exhaustMap(function (a, i) { return Object(_observable_from__WEBPACK_IMPORTED_MODULE_2__["from"])(project(a, i)).pipe(Object(_map__WEBPACK_IMPORTED_MODULE_1__["map"])(function (b, ii) { return resultSelector(a, b, i, ii); })); })); }; + } + return function (source) { + return source.lift(new ExhaustMapOperator(project)); + }; +} +var ExhaustMapOperator = /*@__PURE__*/ (function () { + function ExhaustMapOperator(project) { + this.project = project; + } + ExhaustMapOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new ExhaustMapSubscriber(subscriber, this.project)); + }; + return ExhaustMapOperator; +}()); +var ExhaustMapSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](ExhaustMapSubscriber, _super); + function ExhaustMapSubscriber(destination, project) { + var _this = _super.call(this, destination) || this; + _this.project = project; + _this.hasSubscription = false; + _this.hasCompleted = false; + _this.index = 0; + return _this; + } + ExhaustMapSubscriber.prototype._next = function (value) { + if (!this.hasSubscription) { + this.tryNext(value); + } + }; + ExhaustMapSubscriber.prototype.tryNext = function (value) { + var result; + var index = this.index++; + try { + result = this.project(value, index); + } + catch (err) { + this.destination.error(err); + return; + } + this.hasSubscription = true; + this._innerSub(result); + }; + ExhaustMapSubscriber.prototype._innerSub = function (result) { + var innerSubscriber = new _innerSubscribe__WEBPACK_IMPORTED_MODULE_3__["SimpleInnerSubscriber"](this); + var destination = this.destination; + destination.add(innerSubscriber); + var innerSubscription = Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_3__["innerSubscribe"])(result, innerSubscriber); + if (innerSubscription !== innerSubscriber) { + destination.add(innerSubscription); + } + }; + ExhaustMapSubscriber.prototype._complete = function () { + this.hasCompleted = true; + if (!this.hasSubscription) { + this.destination.complete(); + } + this.unsubscribe(); + }; + ExhaustMapSubscriber.prototype.notifyNext = function (innerValue) { + this.destination.next(innerValue); + }; + ExhaustMapSubscriber.prototype.notifyError = function (err) { + this.destination.error(err); + }; + ExhaustMapSubscriber.prototype.notifyComplete = function () { + this.hasSubscription = false; + if (this.hasCompleted) { + this.destination.complete(); + } + }; + return ExhaustMapSubscriber; +}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_3__["SimpleOuterSubscriber"])); +//# sourceMappingURL=exhaustMap.js.map +/***/ }), +/* 456 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "expand", function() { return expand; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ExpandOperator", function() { return ExpandOperator; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ExpandSubscriber", function() { return ExpandSubscriber; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(91); +/** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ +function expand(project, concurrent, scheduler) { + if (concurrent === void 0) { + concurrent = Number.POSITIVE_INFINITY; + } + concurrent = (concurrent || 0) < 1 ? Number.POSITIVE_INFINITY : concurrent; + return function (source) { return source.lift(new ExpandOperator(project, concurrent, scheduler)); }; +} +var ExpandOperator = /*@__PURE__*/ (function () { + function ExpandOperator(project, concurrent, scheduler) { + this.project = project; + this.concurrent = concurrent; + this.scheduler = scheduler; + } + ExpandOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new ExpandSubscriber(subscriber, this.project, this.concurrent, this.scheduler)); + }; + return ExpandOperator; +}()); +var ExpandSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](ExpandSubscriber, _super); + function ExpandSubscriber(destination, project, concurrent, scheduler) { + var _this = _super.call(this, destination) || this; + _this.project = project; + _this.concurrent = concurrent; + _this.scheduler = scheduler; + _this.index = 0; + _this.active = 0; + _this.hasCompleted = false; + if (concurrent < Number.POSITIVE_INFINITY) { + _this.buffer = []; + } + return _this; + } + ExpandSubscriber.dispatch = function (arg) { + var subscriber = arg.subscriber, result = arg.result, value = arg.value, index = arg.index; + subscriber.subscribeToProjection(result, value, index); + }; + ExpandSubscriber.prototype._next = function (value) { + var destination = this.destination; + if (destination.closed) { + this._complete(); + return; + } + var index = this.index++; + if (this.active < this.concurrent) { + destination.next(value); + try { + var project = this.project; + var result = project(value, index); + if (!this.scheduler) { + this.subscribeToProjection(result, value, index); + } + else { + var state = { subscriber: this, result: result, value: value, index: index }; + var destination_1 = this.destination; + destination_1.add(this.scheduler.schedule(ExpandSubscriber.dispatch, 0, state)); + } + } + catch (e) { + destination.error(e); + } + } + else { + this.buffer.push(value); + } + }; + ExpandSubscriber.prototype.subscribeToProjection = function (result, value, index) { + this.active++; + var destination = this.destination; + destination.add(Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["innerSubscribe"])(result, new _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleInnerSubscriber"](this))); + }; + ExpandSubscriber.prototype._complete = function () { + this.hasCompleted = true; + if (this.hasCompleted && this.active === 0) { + this.destination.complete(); + } + this.unsubscribe(); + }; + ExpandSubscriber.prototype.notifyNext = function (innerValue) { + this._next(innerValue); + }; + ExpandSubscriber.prototype.notifyComplete = function () { + var buffer = this.buffer; + this.active--; + if (buffer && buffer.length > 0) { + this._next(buffer.shift()); + } + if (this.hasCompleted && this.active === 0) { + this.destination.complete(); + } + }; + return ExpandSubscriber; +}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleOuterSubscriber"])); +//# sourceMappingURL=expand.js.map +/***/ }), +/* 457 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "finalize", function() { return finalize; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); +/* harmony import */ var _Subscription__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(18); +/** PURE_IMPORTS_START tslib,_Subscriber,_Subscription PURE_IMPORTS_END */ +function finalize(callback) { + return function (source) { return source.lift(new FinallyOperator(callback)); }; +} +var FinallyOperator = /*@__PURE__*/ (function () { + function FinallyOperator(callback) { + this.callback = callback; + } + FinallyOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new FinallySubscriber(subscriber, this.callback)); + }; + return FinallyOperator; +}()); +var FinallySubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](FinallySubscriber, _super); + function FinallySubscriber(destination, callback) { + var _this = _super.call(this, destination) || this; + _this.add(new _Subscription__WEBPACK_IMPORTED_MODULE_2__["Subscription"](callback)); + return _this; + } + return FinallySubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); +//# sourceMappingURL=finalize.js.map +/***/ }), +/* 458 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "find", function() { return find; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "FindValueOperator", function() { return FindValueOperator; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "FindValueSubscriber", function() { return FindValueSubscriber; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); +/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ +function find(predicate, thisArg) { + if (typeof predicate !== 'function') { + throw new TypeError('predicate is not a function'); + } + return function (source) { return source.lift(new FindValueOperator(predicate, source, false, thisArg)); }; +} +var FindValueOperator = /*@__PURE__*/ (function () { + function FindValueOperator(predicate, source, yieldIndex, thisArg) { + this.predicate = predicate; + this.source = source; + this.yieldIndex = yieldIndex; + this.thisArg = thisArg; + } + FindValueOperator.prototype.call = function (observer, source) { + return source.subscribe(new FindValueSubscriber(observer, this.predicate, this.source, this.yieldIndex, this.thisArg)); + }; + return FindValueOperator; +}()); +var FindValueSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](FindValueSubscriber, _super); + function FindValueSubscriber(destination, predicate, source, yieldIndex, thisArg) { + var _this = _super.call(this, destination) || this; + _this.predicate = predicate; + _this.source = source; + _this.yieldIndex = yieldIndex; + _this.thisArg = thisArg; + _this.index = 0; + return _this; + } + FindValueSubscriber.prototype.notifyComplete = function (value) { + var destination = this.destination; + destination.next(value); + destination.complete(); + this.unsubscribe(); + }; + FindValueSubscriber.prototype._next = function (value) { + var _a = this, predicate = _a.predicate, thisArg = _a.thisArg; + var index = this.index++; + try { + var result = predicate.call(thisArg || this, value, index, this.source); + if (result) { + this.notifyComplete(this.yieldIndex ? index : value); + } + } + catch (err) { + this.destination.error(err); + } + }; + FindValueSubscriber.prototype._complete = function () { + this.notifyComplete(this.yieldIndex ? -1 : undefined); + }; + return FindValueSubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); +//# sourceMappingURL=find.js.map +/***/ }), +/* 459 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "findIndex", function() { return findIndex; }); +/* harmony import */ var _operators_find__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(458); +/** PURE_IMPORTS_START _operators_find PURE_IMPORTS_END */ +function findIndex(predicate, thisArg) { + return function (source) { return source.lift(new _operators_find__WEBPACK_IMPORTED_MODULE_0__["FindValueOperator"](predicate, source, true, thisArg)); }; +} +//# sourceMappingURL=findIndex.js.map +/***/ }), +/* 460 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "first", function() { return first; }); +/* harmony import */ var _util_EmptyError__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(64); +/* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(106); +/* harmony import */ var _take__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(451); +/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(441); +/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(450); +/* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(26); +/** PURE_IMPORTS_START _util_EmptyError,_filter,_take,_defaultIfEmpty,_throwIfEmpty,_util_identity PURE_IMPORTS_END */ +function first(predicate, defaultValue) { + var hasDefaultValue = arguments.length >= 2; + return function (source) { return source.pipe(predicate ? Object(_filter__WEBPACK_IMPORTED_MODULE_1__["filter"])(function (v, i) { return predicate(v, i, source); }) : _util_identity__WEBPACK_IMPORTED_MODULE_5__["identity"], Object(_take__WEBPACK_IMPORTED_MODULE_2__["take"])(1), hasDefaultValue ? Object(_defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__["defaultIfEmpty"])(defaultValue) : Object(_throwIfEmpty__WEBPACK_IMPORTED_MODULE_4__["throwIfEmpty"])(function () { return new _util_EmptyError__WEBPACK_IMPORTED_MODULE_0__["EmptyError"](); })); }; +} +//# sourceMappingURL=first.js.map +/***/ }), +/* 461 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ignoreElements", function() { return ignoreElements; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); +/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ +function ignoreElements() { + return function ignoreElementsOperatorFunction(source) { + return source.lift(new IgnoreElementsOperator()); + }; +} +var IgnoreElementsOperator = /*@__PURE__*/ (function () { + function IgnoreElementsOperator() { + } + IgnoreElementsOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new IgnoreElementsSubscriber(subscriber)); + }; + return IgnoreElementsOperator; +}()); +var IgnoreElementsSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](IgnoreElementsSubscriber, _super); + function IgnoreElementsSubscriber() { + return _super !== null && _super.apply(this, arguments) || this; + } + IgnoreElementsSubscriber.prototype._next = function (unused) { + }; + return IgnoreElementsSubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); +//# sourceMappingURL=ignoreElements.js.map +/***/ }), +/* 462 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isEmpty", function() { return isEmpty; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); +/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ +function isEmpty() { + return function (source) { return source.lift(new IsEmptyOperator()); }; +} +var IsEmptyOperator = /*@__PURE__*/ (function () { + function IsEmptyOperator() { + } + IsEmptyOperator.prototype.call = function (observer, source) { + return source.subscribe(new IsEmptySubscriber(observer)); + }; + return IsEmptyOperator; +}()); +var IsEmptySubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](IsEmptySubscriber, _super); + function IsEmptySubscriber(destination) { + return _super.call(this, destination) || this; + } + IsEmptySubscriber.prototype.notifyComplete = function (isEmpty) { + var destination = this.destination; + destination.next(isEmpty); + destination.complete(); + }; + IsEmptySubscriber.prototype._next = function (value) { + this.notifyComplete(false); + }; + IsEmptySubscriber.prototype._complete = function () { + this.notifyComplete(true); + }; + return IsEmptySubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); +//# sourceMappingURL=isEmpty.js.map +/***/ }), +/* 463 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "last", function() { return last; }); +/* harmony import */ var _util_EmptyError__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(64); +/* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(106); +/* harmony import */ var _takeLast__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(464); +/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(450); +/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(441); +/* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(26); +/** PURE_IMPORTS_START _util_EmptyError,_filter,_takeLast,_throwIfEmpty,_defaultIfEmpty,_util_identity PURE_IMPORTS_END */ -//# sourceMappingURL=index.js.map +function last(predicate, defaultValue) { + var hasDefaultValue = arguments.length >= 2; + return function (source) { return source.pipe(predicate ? Object(_filter__WEBPACK_IMPORTED_MODULE_1__["filter"])(function (v, i) { return predicate(v, i, source); }) : _util_identity__WEBPACK_IMPORTED_MODULE_5__["identity"], Object(_takeLast__WEBPACK_IMPORTED_MODULE_2__["takeLast"])(1), hasDefaultValue ? Object(_defaultIfEmpty__WEBPACK_IMPORTED_MODULE_4__["defaultIfEmpty"])(defaultValue) : Object(_throwIfEmpty__WEBPACK_IMPORTED_MODULE_3__["throwIfEmpty"])(function () { return new _util_EmptyError__WEBPACK_IMPORTED_MODULE_0__["EmptyError"](); })); }; +} +//# sourceMappingURL=last.js.map /***/ }), -/* 419 */ +/* 464 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "audit", function() { return audit; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "takeLast", function() { return takeLast; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(91); -/** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); +/* harmony import */ var _util_ArgumentOutOfRangeError__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(63); +/* harmony import */ var _observable_empty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(44); +/** PURE_IMPORTS_START tslib,_Subscriber,_util_ArgumentOutOfRangeError,_observable_empty PURE_IMPORTS_END */ -function audit(durationSelector) { - return function auditOperatorFunction(source) { - return source.lift(new AuditOperator(durationSelector)); + + +function takeLast(count) { + return function takeLastOperatorFunction(source) { + if (count === 0) { + return Object(_observable_empty__WEBPACK_IMPORTED_MODULE_3__["empty"])(); + } + else { + return source.lift(new TakeLastOperator(count)); + } }; } -var AuditOperator = /*@__PURE__*/ (function () { - function AuditOperator(durationSelector) { - this.durationSelector = durationSelector; +var TakeLastOperator = /*@__PURE__*/ (function () { + function TakeLastOperator(total) { + this.total = total; + if (this.total < 0) { + throw new _util_ArgumentOutOfRangeError__WEBPACK_IMPORTED_MODULE_2__["ArgumentOutOfRangeError"]; + } } - AuditOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new AuditSubscriber(subscriber, this.durationSelector)); + TakeLastOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new TakeLastSubscriber(subscriber, this.total)); }; - return AuditOperator; + return TakeLastOperator; }()); -var AuditSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](AuditSubscriber, _super); - function AuditSubscriber(destination, durationSelector) { +var TakeLastSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](TakeLastSubscriber, _super); + function TakeLastSubscriber(destination, total) { var _this = _super.call(this, destination) || this; - _this.durationSelector = durationSelector; - _this.hasValue = false; + _this.total = total; + _this.ring = new Array(); + _this.count = 0; return _this; } - AuditSubscriber.prototype._next = function (value) { - this.value = value; - this.hasValue = true; - if (!this.throttled) { - var duration = void 0; - try { - var durationSelector = this.durationSelector; - duration = durationSelector(value); - } - catch (err) { - return this.destination.error(err); - } - var innerSubscription = Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["innerSubscribe"])(duration, new _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleInnerSubscriber"](this)); - if (!innerSubscription || innerSubscription.closed) { - this.clearThrottle(); - } - else { - this.add(this.throttled = innerSubscription); - } - } - }; - AuditSubscriber.prototype.clearThrottle = function () { - var _a = this, value = _a.value, hasValue = _a.hasValue, throttled = _a.throttled; - if (throttled) { - this.remove(throttled); - this.throttled = undefined; - throttled.unsubscribe(); + TakeLastSubscriber.prototype._next = function (value) { + var ring = this.ring; + var total = this.total; + var count = this.count++; + if (ring.length < total) { + ring.push(value); } - if (hasValue) { - this.value = undefined; - this.hasValue = false; - this.destination.next(value); + else { + var index = count % total; + ring[index] = value; } }; - AuditSubscriber.prototype.notifyNext = function () { - this.clearThrottle(); - }; - AuditSubscriber.prototype.notifyComplete = function () { - this.clearThrottle(); + TakeLastSubscriber.prototype._complete = function () { + var destination = this.destination; + var count = this.count; + if (count > 0) { + var total = this.count >= this.total ? this.total : this.count; + var ring = this.ring; + for (var i = 0; i < total; i++) { + var idx = (count++) % total; + destination.next(ring[idx]); + } + } + destination.complete(); }; - return AuditSubscriber; -}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleOuterSubscriber"])); -//# sourceMappingURL=audit.js.map + return TakeLastSubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); +//# sourceMappingURL=takeLast.js.map /***/ }), -/* 420 */ +/* 465 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "auditTime", function() { return auditTime; }); -/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(56); -/* harmony import */ var _audit__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(419); -/* harmony import */ var _observable_timer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(109); -/** PURE_IMPORTS_START _scheduler_async,_audit,_observable_timer PURE_IMPORTS_END */ - +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "mapTo", function() { return mapTo; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); +/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ -function auditTime(duration, scheduler) { - if (scheduler === void 0) { - scheduler = _scheduler_async__WEBPACK_IMPORTED_MODULE_0__["async"]; - } - return Object(_audit__WEBPACK_IMPORTED_MODULE_1__["audit"])(function () { return Object(_observable_timer__WEBPACK_IMPORTED_MODULE_2__["timer"])(duration, scheduler); }); +function mapTo(value) { + return function (source) { return source.lift(new MapToOperator(value)); }; } -//# sourceMappingURL=auditTime.js.map +var MapToOperator = /*@__PURE__*/ (function () { + function MapToOperator(value) { + this.value = value; + } + MapToOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new MapToSubscriber(subscriber, this.value)); + }; + return MapToOperator; +}()); +var MapToSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](MapToSubscriber, _super); + function MapToSubscriber(destination, value) { + var _this = _super.call(this, destination) || this; + _this.value = value; + return _this; + } + MapToSubscriber.prototype._next = function (x) { + this.destination.next(this.value); + }; + return MapToSubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); +//# sourceMappingURL=mapTo.js.map /***/ }), -/* 421 */ +/* 466 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "buffer", function() { return buffer; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "materialize", function() { return materialize; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(91); -/** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); +/* harmony import */ var _Notification__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(43); +/** PURE_IMPORTS_START tslib,_Subscriber,_Notification PURE_IMPORTS_END */ -function buffer(closingNotifier) { - return function bufferOperatorFunction(source) { - return source.lift(new BufferOperator(closingNotifier)); + +function materialize() { + return function materializeOperatorFunction(source) { + return source.lift(new MaterializeOperator()); }; } -var BufferOperator = /*@__PURE__*/ (function () { - function BufferOperator(closingNotifier) { - this.closingNotifier = closingNotifier; +var MaterializeOperator = /*@__PURE__*/ (function () { + function MaterializeOperator() { } - BufferOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new BufferSubscriber(subscriber, this.closingNotifier)); + MaterializeOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new MaterializeSubscriber(subscriber)); }; - return BufferOperator; + return MaterializeOperator; }()); -var BufferSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](BufferSubscriber, _super); - function BufferSubscriber(destination, closingNotifier) { - var _this = _super.call(this, destination) || this; - _this.buffer = []; - _this.add(Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["innerSubscribe"])(closingNotifier, new _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleInnerSubscriber"](_this))); - return _this; +var MaterializeSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](MaterializeSubscriber, _super); + function MaterializeSubscriber(destination) { + return _super.call(this, destination) || this; + } + MaterializeSubscriber.prototype._next = function (value) { + this.destination.next(_Notification__WEBPACK_IMPORTED_MODULE_2__["Notification"].createNext(value)); + }; + MaterializeSubscriber.prototype._error = function (err) { + var destination = this.destination; + destination.next(_Notification__WEBPACK_IMPORTED_MODULE_2__["Notification"].createError(err)); + destination.complete(); + }; + MaterializeSubscriber.prototype._complete = function () { + var destination = this.destination; + destination.next(_Notification__WEBPACK_IMPORTED_MODULE_2__["Notification"].createComplete()); + destination.complete(); + }; + return MaterializeSubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); +//# sourceMappingURL=materialize.js.map + + +/***/ }), +/* 467 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "max", function() { return max; }); +/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(468); +/** PURE_IMPORTS_START _reduce PURE_IMPORTS_END */ + +function max(comparer) { + var max = (typeof comparer === 'function') + ? function (x, y) { return comparer(x, y) > 0 ? x : y; } + : function (x, y) { return x > y ? x : y; }; + return Object(_reduce__WEBPACK_IMPORTED_MODULE_0__["reduce"])(max); +} +//# sourceMappingURL=max.js.map + + +/***/ }), +/* 468 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "reduce", function() { return reduce; }); +/* harmony import */ var _scan__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(469); +/* harmony import */ var _takeLast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(464); +/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(441); +/* harmony import */ var _util_pipe__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(25); +/** PURE_IMPORTS_START _scan,_takeLast,_defaultIfEmpty,_util_pipe PURE_IMPORTS_END */ + + + + +function reduce(accumulator, seed) { + if (arguments.length >= 2) { + return function reduceOperatorFunctionWithSeed(source) { + return Object(_util_pipe__WEBPACK_IMPORTED_MODULE_3__["pipe"])(Object(_scan__WEBPACK_IMPORTED_MODULE_0__["scan"])(accumulator, seed), Object(_takeLast__WEBPACK_IMPORTED_MODULE_1__["takeLast"])(1), Object(_defaultIfEmpty__WEBPACK_IMPORTED_MODULE_2__["defaultIfEmpty"])(seed))(source); + }; } - BufferSubscriber.prototype._next = function (value) { - this.buffer.push(value); - }; - BufferSubscriber.prototype.notifyNext = function () { - var buffer = this.buffer; - this.buffer = []; - this.destination.next(buffer); + return function reduceOperatorFunction(source) { + return Object(_util_pipe__WEBPACK_IMPORTED_MODULE_3__["pipe"])(Object(_scan__WEBPACK_IMPORTED_MODULE_0__["scan"])(function (acc, value, index) { return accumulator(acc, value, index + 1); }), Object(_takeLast__WEBPACK_IMPORTED_MODULE_1__["takeLast"])(1))(source); }; - return BufferSubscriber; -}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleOuterSubscriber"])); -//# sourceMappingURL=buffer.js.map +} +//# sourceMappingURL=reduce.js.map /***/ }), -/* 422 */ +/* 469 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "bufferCount", function() { return bufferCount; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "scan", function() { return scan; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ -function bufferCount(bufferSize, startBufferEvery) { - if (startBufferEvery === void 0) { - startBufferEvery = null; +function scan(accumulator, seed) { + var hasSeed = false; + if (arguments.length >= 2) { + hasSeed = true; } - return function bufferCountOperatorFunction(source) { - return source.lift(new BufferCountOperator(bufferSize, startBufferEvery)); + return function scanOperatorFunction(source) { + return source.lift(new ScanOperator(accumulator, seed, hasSeed)); }; } -var BufferCountOperator = /*@__PURE__*/ (function () { - function BufferCountOperator(bufferSize, startBufferEvery) { - this.bufferSize = bufferSize; - this.startBufferEvery = startBufferEvery; - if (!startBufferEvery || bufferSize === startBufferEvery) { - this.subscriberClass = BufferCountSubscriber; - } - else { - this.subscriberClass = BufferSkipCountSubscriber; +var ScanOperator = /*@__PURE__*/ (function () { + function ScanOperator(accumulator, seed, hasSeed) { + if (hasSeed === void 0) { + hasSeed = false; } + this.accumulator = accumulator; + this.seed = seed; + this.hasSeed = hasSeed; } - BufferCountOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new this.subscriberClass(subscriber, this.bufferSize, this.startBufferEvery)); + ScanOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new ScanSubscriber(subscriber, this.accumulator, this.seed, this.hasSeed)); }; - return BufferCountOperator; + return ScanOperator; }()); -var BufferCountSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](BufferCountSubscriber, _super); - function BufferCountSubscriber(destination, bufferSize) { +var ScanSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](ScanSubscriber, _super); + function ScanSubscriber(destination, accumulator, _seed, hasSeed) { var _this = _super.call(this, destination) || this; - _this.bufferSize = bufferSize; - _this.buffer = []; + _this.accumulator = accumulator; + _this._seed = _seed; + _this.hasSeed = hasSeed; + _this.index = 0; return _this; } - BufferCountSubscriber.prototype._next = function (value) { - var buffer = this.buffer; - buffer.push(value); - if (buffer.length == this.bufferSize) { - this.destination.next(buffer); - this.buffer = []; + Object.defineProperty(ScanSubscriber.prototype, "seed", { + get: function () { + return this._seed; + }, + set: function (value) { + this.hasSeed = true; + this._seed = value; + }, + enumerable: true, + configurable: true + }); + ScanSubscriber.prototype._next = function (value) { + if (!this.hasSeed) { + this.seed = value; + this.destination.next(value); } - }; - BufferCountSubscriber.prototype._complete = function () { - var buffer = this.buffer; - if (buffer.length > 0) { - this.destination.next(buffer); + else { + return this._tryNext(value); } - _super.prototype._complete.call(this); }; - return BufferCountSubscriber; -}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -var BufferSkipCountSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](BufferSkipCountSubscriber, _super); - function BufferSkipCountSubscriber(destination, bufferSize, startBufferEvery) { - var _this = _super.call(this, destination) || this; - _this.bufferSize = bufferSize; - _this.startBufferEvery = startBufferEvery; - _this.buffers = []; - _this.count = 0; - return _this; - } - BufferSkipCountSubscriber.prototype._next = function (value) { - var _a = this, bufferSize = _a.bufferSize, startBufferEvery = _a.startBufferEvery, buffers = _a.buffers, count = _a.count; - this.count++; - if (count % startBufferEvery === 0) { - buffers.push([]); - } - for (var i = buffers.length; i--;) { - var buffer = buffers[i]; - buffer.push(value); - if (buffer.length === bufferSize) { - buffers.splice(i, 1); - this.destination.next(buffer); - } + ScanSubscriber.prototype._tryNext = function (value) { + var index = this.index++; + var result; + try { + result = this.accumulator(this.seed, value, index); } - }; - BufferSkipCountSubscriber.prototype._complete = function () { - var _a = this, buffers = _a.buffers, destination = _a.destination; - while (buffers.length > 0) { - var buffer = buffers.shift(); - if (buffer.length > 0) { - destination.next(buffer); - } + catch (err) { + this.destination.error(err); } - _super.prototype._complete.call(this); + this.seed = result; + this.destination.next(result); }; - return BufferSkipCountSubscriber; + return ScanSubscriber; }(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -//# sourceMappingURL=bufferCount.js.map +//# sourceMappingURL=scan.js.map /***/ }), -/* 423 */ +/* 470 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "bufferTime", function() { return bufferTime; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(56); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(12); -/* harmony import */ var _util_isScheduler__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(46); -/** PURE_IMPORTS_START tslib,_scheduler_async,_Subscriber,_util_isScheduler PURE_IMPORTS_END */ +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "merge", function() { return merge; }); +/* harmony import */ var _observable_merge__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(100); +/** PURE_IMPORTS_START _observable_merge PURE_IMPORTS_END */ +function merge() { + var observables = []; + for (var _i = 0; _i < arguments.length; _i++) { + observables[_i] = arguments[_i]; + } + return function (source) { return source.lift.call(_observable_merge__WEBPACK_IMPORTED_MODULE_0__["merge"].apply(void 0, [source].concat(observables))); }; +} +//# sourceMappingURL=merge.js.map +/***/ }), +/* 471 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { -function bufferTime(bufferTimeSpan) { - var length = arguments.length; - var scheduler = _scheduler_async__WEBPACK_IMPORTED_MODULE_1__["async"]; - if (Object(_util_isScheduler__WEBPACK_IMPORTED_MODULE_3__["isScheduler"])(arguments[arguments.length - 1])) { - scheduler = arguments[arguments.length - 1]; - length--; +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "mergeMapTo", function() { return mergeMapTo; }); +/* harmony import */ var _mergeMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(83); +/** PURE_IMPORTS_START _mergeMap PURE_IMPORTS_END */ + +function mergeMapTo(innerObservable, resultSelector, concurrent) { + if (concurrent === void 0) { + concurrent = Number.POSITIVE_INFINITY; } - var bufferCreationInterval = null; - if (length >= 2) { - bufferCreationInterval = arguments[1]; + if (typeof resultSelector === 'function') { + return Object(_mergeMap__WEBPACK_IMPORTED_MODULE_0__["mergeMap"])(function () { return innerObservable; }, resultSelector, concurrent); } - var maxBufferSize = Number.POSITIVE_INFINITY; - if (length >= 3) { - maxBufferSize = arguments[2]; + if (typeof resultSelector === 'number') { + concurrent = resultSelector; } - return function bufferTimeOperatorFunction(source) { - return source.lift(new BufferTimeOperator(bufferTimeSpan, bufferCreationInterval, maxBufferSize, scheduler)); - }; + return Object(_mergeMap__WEBPACK_IMPORTED_MODULE_0__["mergeMap"])(function () { return innerObservable; }, concurrent); } -var BufferTimeOperator = /*@__PURE__*/ (function () { - function BufferTimeOperator(bufferTimeSpan, bufferCreationInterval, maxBufferSize, scheduler) { - this.bufferTimeSpan = bufferTimeSpan; - this.bufferCreationInterval = bufferCreationInterval; - this.maxBufferSize = maxBufferSize; - this.scheduler = scheduler; +//# sourceMappingURL=mergeMapTo.js.map + + +/***/ }), +/* 472 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "mergeScan", function() { return mergeScan; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MergeScanOperator", function() { return MergeScanOperator; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MergeScanSubscriber", function() { return MergeScanSubscriber; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(91); +/** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ + + +function mergeScan(accumulator, seed, concurrent) { + if (concurrent === void 0) { + concurrent = Number.POSITIVE_INFINITY; } - BufferTimeOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new BufferTimeSubscriber(subscriber, this.bufferTimeSpan, this.bufferCreationInterval, this.maxBufferSize, this.scheduler)); - }; - return BufferTimeOperator; -}()); -var Context = /*@__PURE__*/ (function () { - function Context() { - this.buffer = []; + return function (source) { return source.lift(new MergeScanOperator(accumulator, seed, concurrent)); }; +} +var MergeScanOperator = /*@__PURE__*/ (function () { + function MergeScanOperator(accumulator, seed, concurrent) { + this.accumulator = accumulator; + this.seed = seed; + this.concurrent = concurrent; } - return Context; + MergeScanOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new MergeScanSubscriber(subscriber, this.accumulator, this.seed, this.concurrent)); + }; + return MergeScanOperator; }()); -var BufferTimeSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](BufferTimeSubscriber, _super); - function BufferTimeSubscriber(destination, bufferTimeSpan, bufferCreationInterval, maxBufferSize, scheduler) { + +var MergeScanSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](MergeScanSubscriber, _super); + function MergeScanSubscriber(destination, accumulator, acc, concurrent) { var _this = _super.call(this, destination) || this; - _this.bufferTimeSpan = bufferTimeSpan; - _this.bufferCreationInterval = bufferCreationInterval; - _this.maxBufferSize = maxBufferSize; - _this.scheduler = scheduler; - _this.contexts = []; - var context = _this.openContext(); - _this.timespanOnly = bufferCreationInterval == null || bufferCreationInterval < 0; - if (_this.timespanOnly) { - var timeSpanOnlyState = { subscriber: _this, context: context, bufferTimeSpan: bufferTimeSpan }; - _this.add(context.closeAction = scheduler.schedule(dispatchBufferTimeSpanOnly, bufferTimeSpan, timeSpanOnlyState)); - } - else { - var closeState = { subscriber: _this, context: context }; - var creationState = { bufferTimeSpan: bufferTimeSpan, bufferCreationInterval: bufferCreationInterval, subscriber: _this, scheduler: scheduler }; - _this.add(context.closeAction = scheduler.schedule(dispatchBufferClose, bufferTimeSpan, closeState)); - _this.add(scheduler.schedule(dispatchBufferCreation, bufferCreationInterval, creationState)); - } + _this.accumulator = accumulator; + _this.acc = acc; + _this.concurrent = concurrent; + _this.hasValue = false; + _this.hasCompleted = false; + _this.buffer = []; + _this.active = 0; + _this.index = 0; return _this; } - BufferTimeSubscriber.prototype._next = function (value) { - var contexts = this.contexts; - var len = contexts.length; - var filledBufferContext; - for (var i = 0; i < len; i++) { - var context_1 = contexts[i]; - var buffer = context_1.buffer; - buffer.push(value); - if (buffer.length == this.maxBufferSize) { - filledBufferContext = context_1; + MergeScanSubscriber.prototype._next = function (value) { + if (this.active < this.concurrent) { + var index = this.index++; + var destination = this.destination; + var ish = void 0; + try { + var accumulator = this.accumulator; + ish = accumulator(this.acc, value, index); + } + catch (e) { + return destination.error(e); } + this.active++; + this._innerSub(ish); } - if (filledBufferContext) { - this.onBufferFull(filledBufferContext); + else { + this.buffer.push(value); } }; - BufferTimeSubscriber.prototype._error = function (err) { - this.contexts.length = 0; - _super.prototype._error.call(this, err); - }; - BufferTimeSubscriber.prototype._complete = function () { - var _a = this, contexts = _a.contexts, destination = _a.destination; - while (contexts.length > 0) { - var context_2 = contexts.shift(); - destination.next(context_2.buffer); + MergeScanSubscriber.prototype._innerSub = function (ish) { + var innerSubscriber = new _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleInnerSubscriber"](this); + var destination = this.destination; + destination.add(innerSubscriber); + var innerSubscription = Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["innerSubscribe"])(ish, innerSubscriber); + if (innerSubscription !== innerSubscriber) { + destination.add(innerSubscription); } - _super.prototype._complete.call(this); - }; - BufferTimeSubscriber.prototype._unsubscribe = function () { - this.contexts = null; }; - BufferTimeSubscriber.prototype.onBufferFull = function (context) { - this.closeContext(context); - var closeAction = context.closeAction; - closeAction.unsubscribe(); - this.remove(closeAction); - if (!this.closed && this.timespanOnly) { - context = this.openContext(); - var bufferTimeSpan = this.bufferTimeSpan; - var timeSpanOnlyState = { subscriber: this, context: context, bufferTimeSpan: bufferTimeSpan }; - this.add(context.closeAction = this.scheduler.schedule(dispatchBufferTimeSpanOnly, bufferTimeSpan, timeSpanOnlyState)); + MergeScanSubscriber.prototype._complete = function () { + this.hasCompleted = true; + if (this.active === 0 && this.buffer.length === 0) { + if (this.hasValue === false) { + this.destination.next(this.acc); + } + this.destination.complete(); } + this.unsubscribe(); }; - BufferTimeSubscriber.prototype.openContext = function () { - var context = new Context(); - this.contexts.push(context); - return context; + MergeScanSubscriber.prototype.notifyNext = function (innerValue) { + var destination = this.destination; + this.acc = innerValue; + this.hasValue = true; + destination.next(innerValue); }; - BufferTimeSubscriber.prototype.closeContext = function (context) { - this.destination.next(context.buffer); - var contexts = this.contexts; - var spliceIndex = contexts ? contexts.indexOf(context) : -1; - if (spliceIndex >= 0) { - contexts.splice(contexts.indexOf(context), 1); + MergeScanSubscriber.prototype.notifyComplete = function () { + var buffer = this.buffer; + this.active--; + if (buffer.length > 0) { + this._next(buffer.shift()); + } + else if (this.active === 0 && this.hasCompleted) { + if (this.hasValue === false) { + this.destination.next(this.acc); + } + this.destination.complete(); } }; - return BufferTimeSubscriber; -}(_Subscriber__WEBPACK_IMPORTED_MODULE_2__["Subscriber"])); -function dispatchBufferTimeSpanOnly(state) { - var subscriber = state.subscriber; - var prevContext = state.context; - if (prevContext) { - subscriber.closeContext(prevContext); - } - if (!subscriber.closed) { - state.context = subscriber.openContext(); - state.context.closeAction = this.schedule(state, state.bufferTimeSpan); - } -} -function dispatchBufferCreation(state) { - var bufferCreationInterval = state.bufferCreationInterval, bufferTimeSpan = state.bufferTimeSpan, subscriber = state.subscriber, scheduler = state.scheduler; - var context = subscriber.openContext(); - var action = this; - if (!subscriber.closed) { - subscriber.add(context.closeAction = scheduler.schedule(dispatchBufferClose, bufferTimeSpan, { subscriber: subscriber, context: context })); - action.schedule(state, bufferCreationInterval); - } -} -function dispatchBufferClose(arg) { - var subscriber = arg.subscriber, context = arg.context; - subscriber.closeContext(context); -} -//# sourceMappingURL=bufferTime.js.map + return MergeScanSubscriber; +}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleOuterSubscriber"])); + +//# sourceMappingURL=mergeScan.js.map /***/ }), -/* 424 */ +/* 473 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "bufferToggle", function() { return bufferToggle; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _Subscription__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(18); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(71); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(70); -/** PURE_IMPORTS_START tslib,_Subscription,_util_subscribeToResult,_OuterSubscriber PURE_IMPORTS_END */ +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "min", function() { return min; }); +/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(468); +/** PURE_IMPORTS_START _reduce PURE_IMPORTS_END */ +function min(comparer) { + var min = (typeof comparer === 'function') + ? function (x, y) { return comparer(x, y) < 0 ? x : y; } + : function (x, y) { return x < y ? x : y; }; + return Object(_reduce__WEBPACK_IMPORTED_MODULE_0__["reduce"])(min); +} +//# sourceMappingURL=min.js.map +/***/ }), +/* 474 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { -function bufferToggle(openings, closingSelector) { - return function bufferToggleOperatorFunction(source) { - return source.lift(new BufferToggleOperator(openings, closingSelector)); - }; -} -var BufferToggleOperator = /*@__PURE__*/ (function () { - function BufferToggleOperator(openings, closingSelector) { - this.openings = openings; - this.closingSelector = closingSelector; - } - BufferToggleOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new BufferToggleSubscriber(subscriber, this.openings, this.closingSelector)); - }; - return BufferToggleOperator; -}()); -var BufferToggleSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](BufferToggleSubscriber, _super); - function BufferToggleSubscriber(destination, openings, closingSelector) { - var _this = _super.call(this, destination) || this; - _this.closingSelector = closingSelector; - _this.contexts = []; - _this.add(Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__["subscribeToResult"])(_this, openings)); - return _this; - } - BufferToggleSubscriber.prototype._next = function (value) { - var contexts = this.contexts; - var len = contexts.length; - for (var i = 0; i < len; i++) { - contexts[i].buffer.push(value); - } - }; - BufferToggleSubscriber.prototype._error = function (err) { - var contexts = this.contexts; - while (contexts.length > 0) { - var context_1 = contexts.shift(); - context_1.subscription.unsubscribe(); - context_1.buffer = null; - context_1.subscription = null; - } - this.contexts = null; - _super.prototype._error.call(this, err); - }; - BufferToggleSubscriber.prototype._complete = function () { - var contexts = this.contexts; - while (contexts.length > 0) { - var context_2 = contexts.shift(); - this.destination.next(context_2.buffer); - context_2.subscription.unsubscribe(); - context_2.buffer = null; - context_2.subscription = null; - } - this.contexts = null; - _super.prototype._complete.call(this); - }; - BufferToggleSubscriber.prototype.notifyNext = function (outerValue, innerValue) { - outerValue ? this.closeBuffer(outerValue) : this.openBuffer(innerValue); - }; - BufferToggleSubscriber.prototype.notifyComplete = function (innerSub) { - this.closeBuffer(innerSub.context); - }; - BufferToggleSubscriber.prototype.openBuffer = function (value) { - try { - var closingSelector = this.closingSelector; - var closingNotifier = closingSelector.call(this, value); - if (closingNotifier) { - this.trySubscribe(closingNotifier); - } - } - catch (err) { - this._error(err); - } - }; - BufferToggleSubscriber.prototype.closeBuffer = function (context) { - var contexts = this.contexts; - if (contexts && context) { - var buffer = context.buffer, subscription = context.subscription; - this.destination.next(buffer); - contexts.splice(contexts.indexOf(context), 1); - this.remove(subscription); - subscription.unsubscribe(); - } - }; - BufferToggleSubscriber.prototype.trySubscribe = function (closingNotifier) { - var contexts = this.contexts; - var buffer = []; - var subscription = new _Subscription__WEBPACK_IMPORTED_MODULE_1__["Subscription"](); - var context = { buffer: buffer, subscription: subscription }; - contexts.push(context); - var innerSubscription = Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__["subscribeToResult"])(this, closingNotifier, context); - if (!innerSubscription || innerSubscription.closed) { - this.closeBuffer(context); +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "multicast", function() { return multicast; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MulticastOperator", function() { return MulticastOperator; }); +/* harmony import */ var _observable_ConnectableObservable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(27); +/** PURE_IMPORTS_START _observable_ConnectableObservable PURE_IMPORTS_END */ + +function multicast(subjectOrSubjectFactory, selector) { + return function multicastOperatorFunction(source) { + var subjectFactory; + if (typeof subjectOrSubjectFactory === 'function') { + subjectFactory = subjectOrSubjectFactory; } else { - innerSubscription.context = context; - this.add(innerSubscription); - subscription.add(innerSubscription); + subjectFactory = function subjectFactory() { + return subjectOrSubjectFactory; + }; + } + if (typeof selector === 'function') { + return source.lift(new MulticastOperator(subjectFactory, selector)); } + var connectable = Object.create(source, _observable_ConnectableObservable__WEBPACK_IMPORTED_MODULE_0__["connectableObservableDescriptor"]); + connectable.source = source; + connectable.subjectFactory = subjectFactory; + return connectable; }; - return BufferToggleSubscriber; -}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__["OuterSubscriber"])); -//# sourceMappingURL=bufferToggle.js.map +} +var MulticastOperator = /*@__PURE__*/ (function () { + function MulticastOperator(subjectFactory, selector) { + this.subjectFactory = subjectFactory; + this.selector = selector; + } + MulticastOperator.prototype.call = function (subscriber, source) { + var selector = this.selector; + var subject = this.subjectFactory(); + var subscription = selector(subject).subscribe(subscriber); + subscription.add(source.subscribe(subject)); + return subscription; + }; + return MulticastOperator; +}()); + +//# sourceMappingURL=multicast.js.map /***/ }), -/* 425 */ +/* 475 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "bufferWhen", function() { return bufferWhen; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "onErrorResumeNext", function() { return onErrorResumeNext; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "onErrorResumeNextStatic", function() { return onErrorResumeNextStatic; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _Subscription__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(18); -/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(91); -/** PURE_IMPORTS_START tslib,_Subscription,_innerSubscribe PURE_IMPORTS_END */ +/* harmony import */ var _observable_from__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(84); +/* harmony import */ var _util_isArray__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(19); +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(91); +/** PURE_IMPORTS_START tslib,_observable_from,_util_isArray,_innerSubscribe PURE_IMPORTS_END */ -function bufferWhen(closingSelector) { - return function (source) { - return source.lift(new BufferWhenOperator(closingSelector)); - }; + +function onErrorResumeNext() { + var nextSources = []; + for (var _i = 0; _i < arguments.length; _i++) { + nextSources[_i] = arguments[_i]; + } + if (nextSources.length === 1 && Object(_util_isArray__WEBPACK_IMPORTED_MODULE_2__["isArray"])(nextSources[0])) { + nextSources = nextSources[0]; + } + return function (source) { return source.lift(new OnErrorResumeNextOperator(nextSources)); }; } -var BufferWhenOperator = /*@__PURE__*/ (function () { - function BufferWhenOperator(closingSelector) { - this.closingSelector = closingSelector; +function onErrorResumeNextStatic() { + var nextSources = []; + for (var _i = 0; _i < arguments.length; _i++) { + nextSources[_i] = arguments[_i]; } - BufferWhenOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new BufferWhenSubscriber(subscriber, this.closingSelector)); + var source = undefined; + if (nextSources.length === 1 && Object(_util_isArray__WEBPACK_IMPORTED_MODULE_2__["isArray"])(nextSources[0])) { + nextSources = nextSources[0]; + } + source = nextSources.shift(); + return Object(_observable_from__WEBPACK_IMPORTED_MODULE_1__["from"])(source).lift(new OnErrorResumeNextOperator(nextSources)); +} +var OnErrorResumeNextOperator = /*@__PURE__*/ (function () { + function OnErrorResumeNextOperator(nextSources) { + this.nextSources = nextSources; + } + OnErrorResumeNextOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new OnErrorResumeNextSubscriber(subscriber, this.nextSources)); }; - return BufferWhenOperator; + return OnErrorResumeNextOperator; }()); -var BufferWhenSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](BufferWhenSubscriber, _super); - function BufferWhenSubscriber(destination, closingSelector) { +var OnErrorResumeNextSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](OnErrorResumeNextSubscriber, _super); + function OnErrorResumeNextSubscriber(destination, nextSources) { var _this = _super.call(this, destination) || this; - _this.closingSelector = closingSelector; - _this.subscribing = false; - _this.openBuffer(); + _this.destination = destination; + _this.nextSources = nextSources; return _this; } - BufferWhenSubscriber.prototype._next = function (value) { - this.buffer.push(value); + OnErrorResumeNextSubscriber.prototype.notifyError = function () { + this.subscribeToNextSource(); }; - BufferWhenSubscriber.prototype._complete = function () { - var buffer = this.buffer; - if (buffer) { - this.destination.next(buffer); - } - _super.prototype._complete.call(this); + OnErrorResumeNextSubscriber.prototype.notifyComplete = function () { + this.subscribeToNextSource(); }; - BufferWhenSubscriber.prototype._unsubscribe = function () { - this.buffer = undefined; - this.subscribing = false; + OnErrorResumeNextSubscriber.prototype._error = function (err) { + this.subscribeToNextSource(); + this.unsubscribe(); }; - BufferWhenSubscriber.prototype.notifyNext = function () { - this.openBuffer(); + OnErrorResumeNextSubscriber.prototype._complete = function () { + this.subscribeToNextSource(); + this.unsubscribe(); }; - BufferWhenSubscriber.prototype.notifyComplete = function () { - if (this.subscribing) { - this.complete(); + OnErrorResumeNextSubscriber.prototype.subscribeToNextSource = function () { + var next = this.nextSources.shift(); + if (!!next) { + var innerSubscriber = new _innerSubscribe__WEBPACK_IMPORTED_MODULE_3__["SimpleInnerSubscriber"](this); + var destination = this.destination; + destination.add(innerSubscriber); + var innerSubscription = Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_3__["innerSubscribe"])(next, innerSubscriber); + if (innerSubscription !== innerSubscriber) { + destination.add(innerSubscription); + } } else { - this.openBuffer(); - } - }; - BufferWhenSubscriber.prototype.openBuffer = function () { - var closingSubscription = this.closingSubscription; - if (closingSubscription) { - this.remove(closingSubscription); - closingSubscription.unsubscribe(); - } - var buffer = this.buffer; - if (this.buffer) { - this.destination.next(buffer); - } - this.buffer = []; - var closingNotifier; - try { - var closingSelector = this.closingSelector; - closingNotifier = closingSelector(); - } - catch (err) { - return this.error(err); + this.destination.complete(); } - closingSubscription = new _Subscription__WEBPACK_IMPORTED_MODULE_1__["Subscription"](); - this.closingSubscription = closingSubscription; - this.add(closingSubscription); - this.subscribing = true; - closingSubscription.add(Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_2__["innerSubscribe"])(closingNotifier, new _innerSubscribe__WEBPACK_IMPORTED_MODULE_2__["SimpleInnerSubscriber"](this))); - this.subscribing = false; }; - return BufferWhenSubscriber; -}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_2__["SimpleOuterSubscriber"])); -//# sourceMappingURL=bufferWhen.js.map + return OnErrorResumeNextSubscriber; +}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_3__["SimpleOuterSubscriber"])); +//# sourceMappingURL=onErrorResumeNext.js.map /***/ }), -/* 426 */ +/* 476 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "catchError", function() { return catchError; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "pairwise", function() { return pairwise; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(91); -/** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); +/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ -function catchError(selector) { - return function catchErrorOperatorFunction(source) { - var operator = new CatchOperator(selector); - var caught = source.lift(operator); - return (operator.caught = caught); - }; +function pairwise() { + return function (source) { return source.lift(new PairwiseOperator()); }; } -var CatchOperator = /*@__PURE__*/ (function () { - function CatchOperator(selector) { - this.selector = selector; +var PairwiseOperator = /*@__PURE__*/ (function () { + function PairwiseOperator() { } - CatchOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new CatchSubscriber(subscriber, this.selector, this.caught)); + PairwiseOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new PairwiseSubscriber(subscriber)); }; - return CatchOperator; + return PairwiseOperator; }()); -var CatchSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](CatchSubscriber, _super); - function CatchSubscriber(destination, selector, caught) { +var PairwiseSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](PairwiseSubscriber, _super); + function PairwiseSubscriber(destination) { var _this = _super.call(this, destination) || this; - _this.selector = selector; - _this.caught = caught; + _this.hasPrev = false; return _this; } - CatchSubscriber.prototype.error = function (err) { - if (!this.isStopped) { - var result = void 0; - try { - result = this.selector(err, this.caught); - } - catch (err2) { - _super.prototype.error.call(this, err2); - return; - } - this._unsubscribeAndRecycle(); - var innerSubscriber = new _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleInnerSubscriber"](this); - this.add(innerSubscriber); - var innerSubscription = Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["innerSubscribe"])(result, innerSubscriber); - if (innerSubscription !== innerSubscriber) { - this.add(innerSubscription); - } + PairwiseSubscriber.prototype._next = function (value) { + var pair; + if (this.hasPrev) { + pair = [this.prev, value]; + } + else { + this.hasPrev = true; + } + this.prev = value; + if (pair) { + this.destination.next(pair); } }; - return CatchSubscriber; -}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleOuterSubscriber"])); -//# sourceMappingURL=catchError.js.map + return PairwiseSubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); +//# sourceMappingURL=pairwise.js.map /***/ }), -/* 427 */ +/* 477 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "combineAll", function() { return combineAll; }); -/* harmony import */ var _observable_combineLatest__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(69); -/** PURE_IMPORTS_START _observable_combineLatest PURE_IMPORTS_END */ +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "partition", function() { return partition; }); +/* harmony import */ var _util_not__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(105); +/* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(106); +/** PURE_IMPORTS_START _util_not,_filter PURE_IMPORTS_END */ -function combineAll(project) { - return function (source) { return source.lift(new _observable_combineLatest__WEBPACK_IMPORTED_MODULE_0__["CombineLatestOperator"](project)); }; + +function partition(predicate, thisArg) { + return function (source) { + return [ + Object(_filter__WEBPACK_IMPORTED_MODULE_1__["filter"])(predicate, thisArg)(source), + Object(_filter__WEBPACK_IMPORTED_MODULE_1__["filter"])(Object(_util_not__WEBPACK_IMPORTED_MODULE_0__["not"])(predicate, thisArg))(source) + ]; + }; } -//# sourceMappingURL=combineAll.js.map +//# sourceMappingURL=partition.js.map /***/ }), -/* 428 */ +/* 478 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "combineLatest", function() { return combineLatest; }); -/* harmony import */ var _util_isArray__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(19); -/* harmony import */ var _observable_combineLatest__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(69); -/* harmony import */ var _observable_from__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(84); -/** PURE_IMPORTS_START _util_isArray,_observable_combineLatest,_observable_from PURE_IMPORTS_END */ - - +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "pluck", function() { return pluck; }); +/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(67); +/** PURE_IMPORTS_START _map PURE_IMPORTS_END */ -var none = {}; -function combineLatest() { - var observables = []; +function pluck() { + var properties = []; for (var _i = 0; _i < arguments.length; _i++) { - observables[_i] = arguments[_i]; - } - var project = null; - if (typeof observables[observables.length - 1] === 'function') { - project = observables.pop(); + properties[_i] = arguments[_i]; } - if (observables.length === 1 && Object(_util_isArray__WEBPACK_IMPORTED_MODULE_0__["isArray"])(observables[0])) { - observables = observables[0].slice(); + var length = properties.length; + if (length === 0) { + throw new Error('list of properties cannot be empty.'); } - return function (source) { return source.lift.call(Object(_observable_from__WEBPACK_IMPORTED_MODULE_2__["from"])([source].concat(observables)), new _observable_combineLatest__WEBPACK_IMPORTED_MODULE_1__["CombineLatestOperator"](project)); }; + return function (source) { return Object(_map__WEBPACK_IMPORTED_MODULE_0__["map"])(plucker(properties, length))(source); }; } -//# sourceMappingURL=combineLatest.js.map +function plucker(props, length) { + var mapper = function (x) { + var currentProp = x; + for (var i = 0; i < length; i++) { + var p = currentProp != null ? currentProp[props[i]] : undefined; + if (p !== void 0) { + currentProp = p; + } + else { + return undefined; + } + } + return currentProp; + }; + return mapper; +} +//# sourceMappingURL=pluck.js.map /***/ }), -/* 429 */ +/* 479 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "concat", function() { return concat; }); -/* harmony import */ var _observable_concat__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(80); -/** PURE_IMPORTS_START _observable_concat PURE_IMPORTS_END */ +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "publish", function() { return publish; }); +/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(28); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(474); +/** PURE_IMPORTS_START _Subject,_multicast PURE_IMPORTS_END */ -function concat() { - var observables = []; - for (var _i = 0; _i < arguments.length; _i++) { - observables[_i] = arguments[_i]; - } - return function (source) { return source.lift.call(_observable_concat__WEBPACK_IMPORTED_MODULE_0__["concat"].apply(void 0, [source].concat(observables))); }; + +function publish(selector) { + return selector ? + Object(_multicast__WEBPACK_IMPORTED_MODULE_1__["multicast"])(function () { return new _Subject__WEBPACK_IMPORTED_MODULE_0__["Subject"](); }, selector) : + Object(_multicast__WEBPACK_IMPORTED_MODULE_1__["multicast"])(new _Subject__WEBPACK_IMPORTED_MODULE_0__["Subject"]()); } -//# sourceMappingURL=concat.js.map +//# sourceMappingURL=publish.js.map /***/ }), -/* 430 */ +/* 480 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "concatMap", function() { return concatMap; }); -/* harmony import */ var _mergeMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(83); -/** PURE_IMPORTS_START _mergeMap PURE_IMPORTS_END */ +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "publishBehavior", function() { return publishBehavior; }); +/* harmony import */ var _BehaviorSubject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(33); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(474); +/** PURE_IMPORTS_START _BehaviorSubject,_multicast PURE_IMPORTS_END */ -function concatMap(project, resultSelector) { - return Object(_mergeMap__WEBPACK_IMPORTED_MODULE_0__["mergeMap"])(project, resultSelector, 1); + +function publishBehavior(value) { + return function (source) { return Object(_multicast__WEBPACK_IMPORTED_MODULE_1__["multicast"])(new _BehaviorSubject__WEBPACK_IMPORTED_MODULE_0__["BehaviorSubject"](value))(source); }; } -//# sourceMappingURL=concatMap.js.map +//# sourceMappingURL=publishBehavior.js.map /***/ }), -/* 431 */ +/* 481 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "concatMapTo", function() { return concatMapTo; }); -/* harmony import */ var _concatMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(430); -/** PURE_IMPORTS_START _concatMap PURE_IMPORTS_END */ +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "publishLast", function() { return publishLast; }); +/* harmony import */ var _AsyncSubject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(51); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(474); +/** PURE_IMPORTS_START _AsyncSubject,_multicast PURE_IMPORTS_END */ -function concatMapTo(innerObservable, resultSelector) { - return Object(_concatMap__WEBPACK_IMPORTED_MODULE_0__["concatMap"])(function () { return innerObservable; }, resultSelector); + +function publishLast() { + return function (source) { return Object(_multicast__WEBPACK_IMPORTED_MODULE_1__["multicast"])(new _AsyncSubject__WEBPACK_IMPORTED_MODULE_0__["AsyncSubject"]())(source); }; } -//# sourceMappingURL=concatMapTo.js.map +//# sourceMappingURL=publishLast.js.map /***/ }), -/* 432 */ +/* 482 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "count", function() { return count; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); -/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "publishReplay", function() { return publishReplay; }); +/* harmony import */ var _ReplaySubject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(34); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(474); +/** PURE_IMPORTS_START _ReplaySubject,_multicast PURE_IMPORTS_END */ -function count(predicate) { - return function (source) { return source.lift(new CountOperator(predicate, source)); }; -} -var CountOperator = /*@__PURE__*/ (function () { - function CountOperator(predicate, source) { - this.predicate = predicate; - this.source = source; - } - CountOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new CountSubscriber(subscriber, this.predicate, this.source)); - }; - return CountOperator; -}()); -var CountSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](CountSubscriber, _super); - function CountSubscriber(destination, predicate, source) { - var _this = _super.call(this, destination) || this; - _this.predicate = predicate; - _this.source = source; - _this.count = 0; - _this.index = 0; - return _this; +function publishReplay(bufferSize, windowTime, selectorOrScheduler, scheduler) { + if (selectorOrScheduler && typeof selectorOrScheduler !== 'function') { + scheduler = selectorOrScheduler; } - CountSubscriber.prototype._next = function (value) { - if (this.predicate) { - this._tryPredicate(value); - } - else { - this.count++; - } - }; - CountSubscriber.prototype._tryPredicate = function (value) { - var result; - try { - result = this.predicate(value, this.index++, this.source); - } - catch (err) { - this.destination.error(err); - return; - } - if (result) { - this.count++; - } - }; - CountSubscriber.prototype._complete = function () { - this.destination.next(this.count); - this.destination.complete(); - }; - return CountSubscriber; -}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -//# sourceMappingURL=count.js.map + var selector = typeof selectorOrScheduler === 'function' ? selectorOrScheduler : undefined; + var subject = new _ReplaySubject__WEBPACK_IMPORTED_MODULE_0__["ReplaySubject"](bufferSize, windowTime, scheduler); + return function (source) { return Object(_multicast__WEBPACK_IMPORTED_MODULE_1__["multicast"])(function () { return subject; }, selector)(source); }; +} +//# sourceMappingURL=publishReplay.js.map /***/ }), -/* 433 */ +/* 483 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "debounce", function() { return debounce; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(91); -/** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "race", function() { return race; }); +/* harmony import */ var _util_isArray__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(19); +/* harmony import */ var _observable_race__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(107); +/** PURE_IMPORTS_START _util_isArray,_observable_race PURE_IMPORTS_END */ -function debounce(durationSelector) { - return function (source) { return source.lift(new DebounceOperator(durationSelector)); }; -} -var DebounceOperator = /*@__PURE__*/ (function () { - function DebounceOperator(durationSelector) { - this.durationSelector = durationSelector; - } - DebounceOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new DebounceSubscriber(subscriber, this.durationSelector)); - }; - return DebounceOperator; -}()); -var DebounceSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](DebounceSubscriber, _super); - function DebounceSubscriber(destination, durationSelector) { - var _this = _super.call(this, destination) || this; - _this.durationSelector = durationSelector; - _this.hasValue = false; - return _this; - } - DebounceSubscriber.prototype._next = function (value) { - try { - var result = this.durationSelector.call(this, value); - if (result) { - this._tryNext(value, result); - } - } - catch (err) { - this.destination.error(err); - } - }; - DebounceSubscriber.prototype._complete = function () { - this.emitValue(); - this.destination.complete(); - }; - DebounceSubscriber.prototype._tryNext = function (value, duration) { - var subscription = this.durationSubscription; - this.value = value; - this.hasValue = true; - if (subscription) { - subscription.unsubscribe(); - this.remove(subscription); - } - subscription = Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["innerSubscribe"])(duration, new _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleInnerSubscriber"](this)); - if (subscription && !subscription.closed) { - this.add(this.durationSubscription = subscription); - } - }; - DebounceSubscriber.prototype.notifyNext = function () { - this.emitValue(); - }; - DebounceSubscriber.prototype.notifyComplete = function () { - this.emitValue(); - }; - DebounceSubscriber.prototype.emitValue = function () { - if (this.hasValue) { - var value = this.value; - var subscription = this.durationSubscription; - if (subscription) { - this.durationSubscription = undefined; - subscription.unsubscribe(); - this.remove(subscription); - } - this.value = undefined; - this.hasValue = false; - _super.prototype._next.call(this, value); +function race() { + var observables = []; + for (var _i = 0; _i < arguments.length; _i++) { + observables[_i] = arguments[_i]; + } + return function raceOperatorFunction(source) { + if (observables.length === 1 && Object(_util_isArray__WEBPACK_IMPORTED_MODULE_0__["isArray"])(observables[0])) { + observables = observables[0]; } + return source.lift.call(_observable_race__WEBPACK_IMPORTED_MODULE_1__["race"].apply(void 0, [source].concat(observables))); }; - return DebounceSubscriber; -}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleOuterSubscriber"])); -//# sourceMappingURL=debounce.js.map +} +//# sourceMappingURL=race.js.map /***/ }), -/* 434 */ +/* 484 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "debounceTime", function() { return debounceTime; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "repeat", function() { return repeat; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); -/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(56); -/** PURE_IMPORTS_START tslib,_Subscriber,_scheduler_async PURE_IMPORTS_END */ +/* harmony import */ var _observable_empty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(44); +/** PURE_IMPORTS_START tslib,_Subscriber,_observable_empty PURE_IMPORTS_END */ -function debounceTime(dueTime, scheduler) { - if (scheduler === void 0) { - scheduler = _scheduler_async__WEBPACK_IMPORTED_MODULE_2__["async"]; +function repeat(count) { + if (count === void 0) { + count = -1; } - return function (source) { return source.lift(new DebounceTimeOperator(dueTime, scheduler)); }; + return function (source) { + if (count === 0) { + return Object(_observable_empty__WEBPACK_IMPORTED_MODULE_2__["empty"])(); + } + else if (count < 0) { + return source.lift(new RepeatOperator(-1, source)); + } + else { + return source.lift(new RepeatOperator(count - 1, source)); + } + }; } -var DebounceTimeOperator = /*@__PURE__*/ (function () { - function DebounceTimeOperator(dueTime, scheduler) { - this.dueTime = dueTime; - this.scheduler = scheduler; +var RepeatOperator = /*@__PURE__*/ (function () { + function RepeatOperator(count, source) { + this.count = count; + this.source = source; } - DebounceTimeOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new DebounceTimeSubscriber(subscriber, this.dueTime, this.scheduler)); + RepeatOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new RepeatSubscriber(subscriber, this.count, this.source)); }; - return DebounceTimeOperator; + return RepeatOperator; }()); -var DebounceTimeSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](DebounceTimeSubscriber, _super); - function DebounceTimeSubscriber(destination, dueTime, scheduler) { +var RepeatSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](RepeatSubscriber, _super); + function RepeatSubscriber(destination, count, source) { var _this = _super.call(this, destination) || this; - _this.dueTime = dueTime; - _this.scheduler = scheduler; - _this.debouncedSubscription = null; - _this.lastValue = null; - _this.hasValue = false; + _this.count = count; + _this.source = source; return _this; } - DebounceTimeSubscriber.prototype._next = function (value) { - this.clearDebounce(); - this.lastValue = value; - this.hasValue = true; - this.add(this.debouncedSubscription = this.scheduler.schedule(dispatchNext, this.dueTime, this)); - }; - DebounceTimeSubscriber.prototype._complete = function () { - this.debouncedNext(); - this.destination.complete(); - }; - DebounceTimeSubscriber.prototype.debouncedNext = function () { - this.clearDebounce(); - if (this.hasValue) { - var lastValue = this.lastValue; - this.lastValue = null; - this.hasValue = false; - this.destination.next(lastValue); - } - }; - DebounceTimeSubscriber.prototype.clearDebounce = function () { - var debouncedSubscription = this.debouncedSubscription; - if (debouncedSubscription !== null) { - this.remove(debouncedSubscription); - debouncedSubscription.unsubscribe(); - this.debouncedSubscription = null; + RepeatSubscriber.prototype.complete = function () { + if (!this.isStopped) { + var _a = this, source = _a.source, count = _a.count; + if (count === 0) { + return _super.prototype.complete.call(this); + } + else if (count > -1) { + this.count = count - 1; + } + source.subscribe(this._unsubscribeAndRecycle()); } }; - return DebounceTimeSubscriber; + return RepeatSubscriber; }(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -function dispatchNext(subscriber) { - subscriber.debouncedNext(); -} -//# sourceMappingURL=debounceTime.js.map +//# sourceMappingURL=repeat.js.map /***/ }), -/* 435 */ +/* 485 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "defaultIfEmpty", function() { return defaultIfEmpty; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "repeatWhen", function() { return repeatWhen; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); -/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ +/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(28); +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(91); +/** PURE_IMPORTS_START tslib,_Subject,_innerSubscribe PURE_IMPORTS_END */ -function defaultIfEmpty(defaultValue) { - if (defaultValue === void 0) { - defaultValue = null; - } - return function (source) { return source.lift(new DefaultIfEmptyOperator(defaultValue)); }; + +function repeatWhen(notifier) { + return function (source) { return source.lift(new RepeatWhenOperator(notifier)); }; } -var DefaultIfEmptyOperator = /*@__PURE__*/ (function () { - function DefaultIfEmptyOperator(defaultValue) { - this.defaultValue = defaultValue; +var RepeatWhenOperator = /*@__PURE__*/ (function () { + function RepeatWhenOperator(notifier) { + this.notifier = notifier; } - DefaultIfEmptyOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new DefaultIfEmptySubscriber(subscriber, this.defaultValue)); + RepeatWhenOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new RepeatWhenSubscriber(subscriber, this.notifier, source)); }; - return DefaultIfEmptyOperator; + return RepeatWhenOperator; }()); -var DefaultIfEmptySubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](DefaultIfEmptySubscriber, _super); - function DefaultIfEmptySubscriber(destination, defaultValue) { +var RepeatWhenSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](RepeatWhenSubscriber, _super); + function RepeatWhenSubscriber(destination, notifier, source) { var _this = _super.call(this, destination) || this; - _this.defaultValue = defaultValue; - _this.isEmpty = true; + _this.notifier = notifier; + _this.source = source; + _this.sourceIsBeingSubscribedTo = true; return _this; } - DefaultIfEmptySubscriber.prototype._next = function (value) { - this.isEmpty = false; - this.destination.next(value); + RepeatWhenSubscriber.prototype.notifyNext = function () { + this.sourceIsBeingSubscribedTo = true; + this.source.subscribe(this); }; - DefaultIfEmptySubscriber.prototype._complete = function () { - if (this.isEmpty) { - this.destination.next(this.defaultValue); + RepeatWhenSubscriber.prototype.notifyComplete = function () { + if (this.sourceIsBeingSubscribedTo === false) { + return _super.prototype.complete.call(this); } - this.destination.complete(); }; - return DefaultIfEmptySubscriber; -}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -//# sourceMappingURL=defaultIfEmpty.js.map + RepeatWhenSubscriber.prototype.complete = function () { + this.sourceIsBeingSubscribedTo = false; + if (!this.isStopped) { + if (!this.retries) { + this.subscribeToRetries(); + } + if (!this.retriesSubscription || this.retriesSubscription.closed) { + return _super.prototype.complete.call(this); + } + this._unsubscribeAndRecycle(); + this.notifications.next(undefined); + } + }; + RepeatWhenSubscriber.prototype._unsubscribe = function () { + var _a = this, notifications = _a.notifications, retriesSubscription = _a.retriesSubscription; + if (notifications) { + notifications.unsubscribe(); + this.notifications = undefined; + } + if (retriesSubscription) { + retriesSubscription.unsubscribe(); + this.retriesSubscription = undefined; + } + this.retries = undefined; + }; + RepeatWhenSubscriber.prototype._unsubscribeAndRecycle = function () { + var _unsubscribe = this._unsubscribe; + this._unsubscribe = null; + _super.prototype._unsubscribeAndRecycle.call(this); + this._unsubscribe = _unsubscribe; + return this; + }; + RepeatWhenSubscriber.prototype.subscribeToRetries = function () { + this.notifications = new _Subject__WEBPACK_IMPORTED_MODULE_1__["Subject"](); + var retries; + try { + var notifier = this.notifier; + retries = notifier(this.notifications); + } + catch (e) { + return _super.prototype.complete.call(this); + } + this.retries = retries; + this.retriesSubscription = Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_2__["innerSubscribe"])(retries, new _innerSubscribe__WEBPACK_IMPORTED_MODULE_2__["SimpleInnerSubscriber"](this)); + }; + return RepeatWhenSubscriber; +}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_2__["SimpleOuterSubscriber"])); +//# sourceMappingURL=repeatWhen.js.map /***/ }), -/* 436 */ +/* 486 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "delay", function() { return delay; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "retry", function() { return retry; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(56); -/* harmony import */ var _util_isDate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(437); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(12); -/* harmony import */ var _Notification__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(43); -/** PURE_IMPORTS_START tslib,_scheduler_async,_util_isDate,_Subscriber,_Notification PURE_IMPORTS_END */ - - - +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); +/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ -function delay(delay, scheduler) { - if (scheduler === void 0) { - scheduler = _scheduler_async__WEBPACK_IMPORTED_MODULE_1__["async"]; +function retry(count) { + if (count === void 0) { + count = -1; } - var absoluteDelay = Object(_util_isDate__WEBPACK_IMPORTED_MODULE_2__["isDate"])(delay); - var delayFor = absoluteDelay ? (+delay - scheduler.now()) : Math.abs(delay); - return function (source) { return source.lift(new DelayOperator(delayFor, scheduler)); }; + return function (source) { return source.lift(new RetryOperator(count, source)); }; } -var DelayOperator = /*@__PURE__*/ (function () { - function DelayOperator(delay, scheduler) { - this.delay = delay; - this.scheduler = scheduler; +var RetryOperator = /*@__PURE__*/ (function () { + function RetryOperator(count, source) { + this.count = count; + this.source = source; } - DelayOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new DelaySubscriber(subscriber, this.delay, this.scheduler)); + RetryOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new RetrySubscriber(subscriber, this.count, this.source)); }; - return DelayOperator; + return RetryOperator; }()); -var DelaySubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](DelaySubscriber, _super); - function DelaySubscriber(destination, delay, scheduler) { +var RetrySubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](RetrySubscriber, _super); + function RetrySubscriber(destination, count, source) { var _this = _super.call(this, destination) || this; - _this.delay = delay; - _this.scheduler = scheduler; - _this.queue = []; - _this.active = false; - _this.errored = false; + _this.count = count; + _this.source = source; return _this; } - DelaySubscriber.dispatch = function (state) { - var source = state.source; - var queue = source.queue; - var scheduler = state.scheduler; - var destination = state.destination; - while (queue.length > 0 && (queue[0].time - scheduler.now()) <= 0) { - queue.shift().notification.observe(destination); - } - if (queue.length > 0) { - var delay_1 = Math.max(0, queue[0].time - scheduler.now()); - this.schedule(state, delay_1); - } - else { - this.unsubscribe(); - source.active = false; - } - }; - DelaySubscriber.prototype._schedule = function (scheduler) { - this.active = true; - var destination = this.destination; - destination.add(scheduler.schedule(DelaySubscriber.dispatch, this.delay, { - source: this, destination: this.destination, scheduler: scheduler - })); - }; - DelaySubscriber.prototype.scheduleNotification = function (notification) { - if (this.errored === true) { - return; - } - var scheduler = this.scheduler; - var message = new DelayMessage(scheduler.now() + this.delay, notification); - this.queue.push(message); - if (this.active === false) { - this._schedule(scheduler); + RetrySubscriber.prototype.error = function (err) { + if (!this.isStopped) { + var _a = this, source = _a.source, count = _a.count; + if (count === 0) { + return _super.prototype.error.call(this, err); + } + else if (count > -1) { + this.count = count - 1; + } + source.subscribe(this._unsubscribeAndRecycle()); } }; - DelaySubscriber.prototype._next = function (value) { - this.scheduleNotification(_Notification__WEBPACK_IMPORTED_MODULE_4__["Notification"].createNext(value)); - }; - DelaySubscriber.prototype._error = function (err) { - this.errored = true; - this.queue = []; - this.destination.error(err); - this.unsubscribe(); - }; - DelaySubscriber.prototype._complete = function () { - this.scheduleNotification(_Notification__WEBPACK_IMPORTED_MODULE_4__["Notification"].createComplete()); - this.unsubscribe(); - }; - return DelaySubscriber; -}(_Subscriber__WEBPACK_IMPORTED_MODULE_3__["Subscriber"])); -var DelayMessage = /*@__PURE__*/ (function () { - function DelayMessage(time, notification) { - this.time = time; - this.notification = notification; - } - return DelayMessage; -}()); -//# sourceMappingURL=delay.js.map - - -/***/ }), -/* 437 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { - -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isDate", function() { return isDate; }); -/** PURE_IMPORTS_START PURE_IMPORTS_END */ -function isDate(value) { - return value instanceof Date && !isNaN(+value); -} -//# sourceMappingURL=isDate.js.map + return RetrySubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); +//# sourceMappingURL=retry.js.map /***/ }), -/* 438 */ +/* 487 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "delayWhen", function() { return delayWhen; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "retryWhen", function() { return retryWhen; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); -/* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(10); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(70); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(71); -/** PURE_IMPORTS_START tslib,_Subscriber,_Observable,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ - - +/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(28); +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(91); +/** PURE_IMPORTS_START tslib,_Subject,_innerSubscribe PURE_IMPORTS_END */ -function delayWhen(delayDurationSelector, subscriptionDelay) { - if (subscriptionDelay) { - return function (source) { - return new SubscriptionDelayObservable(source, subscriptionDelay) - .lift(new DelayWhenOperator(delayDurationSelector)); - }; - } - return function (source) { return source.lift(new DelayWhenOperator(delayDurationSelector)); }; +function retryWhen(notifier) { + return function (source) { return source.lift(new RetryWhenOperator(notifier, source)); }; } -var DelayWhenOperator = /*@__PURE__*/ (function () { - function DelayWhenOperator(delayDurationSelector) { - this.delayDurationSelector = delayDurationSelector; +var RetryWhenOperator = /*@__PURE__*/ (function () { + function RetryWhenOperator(notifier, source) { + this.notifier = notifier; + this.source = source; } - DelayWhenOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new DelayWhenSubscriber(subscriber, this.delayDurationSelector)); + RetryWhenOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new RetryWhenSubscriber(subscriber, this.notifier, this.source)); }; - return DelayWhenOperator; + return RetryWhenOperator; }()); -var DelayWhenSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](DelayWhenSubscriber, _super); - function DelayWhenSubscriber(destination, delayDurationSelector) { +var RetryWhenSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](RetryWhenSubscriber, _super); + function RetryWhenSubscriber(destination, notifier, source) { var _this = _super.call(this, destination) || this; - _this.delayDurationSelector = delayDurationSelector; - _this.completed = false; - _this.delayNotifierSubscriptions = []; - _this.index = 0; + _this.notifier = notifier; + _this.source = source; return _this; } - DelayWhenSubscriber.prototype.notifyNext = function (outerValue, _innerValue, _outerIndex, _innerIndex, innerSub) { - this.destination.next(outerValue); - this.removeSubscription(innerSub); - this.tryComplete(); - }; - DelayWhenSubscriber.prototype.notifyError = function (error, innerSub) { - this._error(error); - }; - DelayWhenSubscriber.prototype.notifyComplete = function (innerSub) { - var value = this.removeSubscription(innerSub); - if (value) { - this.destination.next(value); - } - this.tryComplete(); - }; - DelayWhenSubscriber.prototype._next = function (value) { - var index = this.index++; - try { - var delayNotifier = this.delayDurationSelector(value, index); - if (delayNotifier) { - this.tryDelay(delayNotifier, value); + RetryWhenSubscriber.prototype.error = function (err) { + if (!this.isStopped) { + var errors = this.errors; + var retries = this.retries; + var retriesSubscription = this.retriesSubscription; + if (!retries) { + errors = new _Subject__WEBPACK_IMPORTED_MODULE_1__["Subject"](); + try { + var notifier = this.notifier; + retries = notifier(errors); + } + catch (e) { + return _super.prototype.error.call(this, e); + } + retriesSubscription = Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_2__["innerSubscribe"])(retries, new _innerSubscribe__WEBPACK_IMPORTED_MODULE_2__["SimpleInnerSubscriber"](this)); } + else { + this.errors = undefined; + this.retriesSubscription = undefined; + } + this._unsubscribeAndRecycle(); + this.errors = errors; + this.retries = retries; + this.retriesSubscription = retriesSubscription; + errors.next(err); } - catch (err) { - this.destination.error(err); - } - }; - DelayWhenSubscriber.prototype._complete = function () { - this.completed = true; - this.tryComplete(); - this.unsubscribe(); - }; - DelayWhenSubscriber.prototype.removeSubscription = function (subscription) { - subscription.unsubscribe(); - var subscriptionIdx = this.delayNotifierSubscriptions.indexOf(subscription); - if (subscriptionIdx !== -1) { - this.delayNotifierSubscriptions.splice(subscriptionIdx, 1); - } - return subscription.outerValue; }; - DelayWhenSubscriber.prototype.tryDelay = function (delayNotifier, value) { - var notifierSubscription = Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__["subscribeToResult"])(this, delayNotifier, value); - if (notifierSubscription && !notifierSubscription.closed) { - var destination = this.destination; - destination.add(notifierSubscription); - this.delayNotifierSubscriptions.push(notifierSubscription); + RetryWhenSubscriber.prototype._unsubscribe = function () { + var _a = this, errors = _a.errors, retriesSubscription = _a.retriesSubscription; + if (errors) { + errors.unsubscribe(); + this.errors = undefined; } - }; - DelayWhenSubscriber.prototype.tryComplete = function () { - if (this.completed && this.delayNotifierSubscriptions.length === 0) { - this.destination.complete(); + if (retriesSubscription) { + retriesSubscription.unsubscribe(); + this.retriesSubscription = undefined; } + this.retries = undefined; }; - return DelayWhenSubscriber; -}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__["OuterSubscriber"])); -var SubscriptionDelayObservable = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](SubscriptionDelayObservable, _super); - function SubscriptionDelayObservable(source, subscriptionDelay) { - var _this = _super.call(this) || this; - _this.source = source; - _this.subscriptionDelay = subscriptionDelay; - return _this; - } - SubscriptionDelayObservable.prototype._subscribe = function (subscriber) { - this.subscriptionDelay.subscribe(new SubscriptionDelaySubscriber(subscriber, this.source)); - }; - return SubscriptionDelayObservable; -}(_Observable__WEBPACK_IMPORTED_MODULE_2__["Observable"])); -var SubscriptionDelaySubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](SubscriptionDelaySubscriber, _super); - function SubscriptionDelaySubscriber(parent, source) { - var _this = _super.call(this) || this; - _this.parent = parent; - _this.source = source; - _this.sourceSubscribed = false; - return _this; - } - SubscriptionDelaySubscriber.prototype._next = function (unused) { - this.subscribeToSource(); - }; - SubscriptionDelaySubscriber.prototype._error = function (err) { - this.unsubscribe(); - this.parent.error(err); - }; - SubscriptionDelaySubscriber.prototype._complete = function () { - this.unsubscribe(); - this.subscribeToSource(); - }; - SubscriptionDelaySubscriber.prototype.subscribeToSource = function () { - if (!this.sourceSubscribed) { - this.sourceSubscribed = true; - this.unsubscribe(); - this.source.subscribe(this.parent); - } + RetryWhenSubscriber.prototype.notifyNext = function () { + var _unsubscribe = this._unsubscribe; + this._unsubscribe = null; + this._unsubscribeAndRecycle(); + this._unsubscribe = _unsubscribe; + this.source.subscribe(this); }; - return SubscriptionDelaySubscriber; -}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -//# sourceMappingURL=delayWhen.js.map + return RetryWhenSubscriber; +}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_2__["SimpleOuterSubscriber"])); +//# sourceMappingURL=retryWhen.js.map /***/ }), -/* 439 */ +/* 488 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "dematerialize", function() { return dematerialize; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "sample", function() { return sample; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); -/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(91); +/** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ -function dematerialize() { - return function dematerializeOperatorFunction(source) { - return source.lift(new DeMaterializeOperator()); - }; +function sample(notifier) { + return function (source) { return source.lift(new SampleOperator(notifier)); }; } -var DeMaterializeOperator = /*@__PURE__*/ (function () { - function DeMaterializeOperator() { +var SampleOperator = /*@__PURE__*/ (function () { + function SampleOperator(notifier) { + this.notifier = notifier; } - DeMaterializeOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new DeMaterializeSubscriber(subscriber)); + SampleOperator.prototype.call = function (subscriber, source) { + var sampleSubscriber = new SampleSubscriber(subscriber); + var subscription = source.subscribe(sampleSubscriber); + subscription.add(Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["innerSubscribe"])(this.notifier, new _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleInnerSubscriber"](sampleSubscriber))); + return subscription; }; - return DeMaterializeOperator; + return SampleOperator; }()); -var DeMaterializeSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](DeMaterializeSubscriber, _super); - function DeMaterializeSubscriber(destination) { - return _super.call(this, destination) || this; +var SampleSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](SampleSubscriber, _super); + function SampleSubscriber() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.hasValue = false; + return _this; } - DeMaterializeSubscriber.prototype._next = function (value) { - value.observe(this.destination); + SampleSubscriber.prototype._next = function (value) { + this.value = value; + this.hasValue = true; }; - return DeMaterializeSubscriber; -}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -//# sourceMappingURL=dematerialize.js.map + SampleSubscriber.prototype.notifyNext = function () { + this.emitValue(); + }; + SampleSubscriber.prototype.notifyComplete = function () { + this.emitValue(); + }; + SampleSubscriber.prototype.emitValue = function () { + if (this.hasValue) { + this.hasValue = false; + this.destination.next(this.value); + } + }; + return SampleSubscriber; +}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleOuterSubscriber"])); +//# sourceMappingURL=sample.js.map /***/ }), -/* 440 */ +/* 489 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "distinct", function() { return distinct; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "DistinctSubscriber", function() { return DistinctSubscriber; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "sampleTime", function() { return sampleTime; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(91); -/** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); +/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(56); +/** PURE_IMPORTS_START tslib,_Subscriber,_scheduler_async PURE_IMPORTS_END */ -function distinct(keySelector, flushes) { - return function (source) { return source.lift(new DistinctOperator(keySelector, flushes)); }; + +function sampleTime(period, scheduler) { + if (scheduler === void 0) { + scheduler = _scheduler_async__WEBPACK_IMPORTED_MODULE_2__["async"]; + } + return function (source) { return source.lift(new SampleTimeOperator(period, scheduler)); }; } -var DistinctOperator = /*@__PURE__*/ (function () { - function DistinctOperator(keySelector, flushes) { - this.keySelector = keySelector; - this.flushes = flushes; +var SampleTimeOperator = /*@__PURE__*/ (function () { + function SampleTimeOperator(period, scheduler) { + this.period = period; + this.scheduler = scheduler; } - DistinctOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new DistinctSubscriber(subscriber, this.keySelector, this.flushes)); + SampleTimeOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new SampleTimeSubscriber(subscriber, this.period, this.scheduler)); }; - return DistinctOperator; + return SampleTimeOperator; }()); -var DistinctSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](DistinctSubscriber, _super); - function DistinctSubscriber(destination, keySelector, flushes) { +var SampleTimeSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](SampleTimeSubscriber, _super); + function SampleTimeSubscriber(destination, period, scheduler) { var _this = _super.call(this, destination) || this; - _this.keySelector = keySelector; - _this.values = new Set(); - if (flushes) { - _this.add(Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["innerSubscribe"])(flushes, new _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleInnerSubscriber"](_this))); - } + _this.period = period; + _this.scheduler = scheduler; + _this.hasValue = false; + _this.add(scheduler.schedule(dispatchNotification, period, { subscriber: _this, period: period })); return _this; } - DistinctSubscriber.prototype.notifyNext = function () { - this.values.clear(); - }; - DistinctSubscriber.prototype.notifyError = function (error) { - this._error(error); - }; - DistinctSubscriber.prototype._next = function (value) { - if (this.keySelector) { - this._useKeySelector(value); - } - else { - this._finalizeNext(value, value); - } - }; - DistinctSubscriber.prototype._useKeySelector = function (value) { - var key; - var destination = this.destination; - try { - key = this.keySelector(value); - } - catch (err) { - destination.error(err); - return; - } - this._finalizeNext(key, value); + SampleTimeSubscriber.prototype._next = function (value) { + this.lastValue = value; + this.hasValue = true; }; - DistinctSubscriber.prototype._finalizeNext = function (key, value) { - var values = this.values; - if (!values.has(key)) { - values.add(key); - this.destination.next(value); + SampleTimeSubscriber.prototype.notifyNext = function () { + if (this.hasValue) { + this.hasValue = false; + this.destination.next(this.lastValue); } }; - return DistinctSubscriber; -}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleOuterSubscriber"])); - -//# sourceMappingURL=distinct.js.map + return SampleTimeSubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); +function dispatchNotification(state) { + var subscriber = state.subscriber, period = state.period; + subscriber.notifyNext(); + this.schedule(state, period); +} +//# sourceMappingURL=sampleTime.js.map /***/ }), -/* 441 */ +/* 490 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "distinctUntilChanged", function() { return distinctUntilChanged; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "sequenceEqual", function() { return sequenceEqual; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SequenceEqualOperator", function() { return SequenceEqualOperator; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SequenceEqualSubscriber", function() { return SequenceEqualSubscriber; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ -function distinctUntilChanged(compare, keySelector) { - return function (source) { return source.lift(new DistinctUntilChangedOperator(compare, keySelector)); }; +function sequenceEqual(compareTo, comparator) { + return function (source) { return source.lift(new SequenceEqualOperator(compareTo, comparator)); }; } -var DistinctUntilChangedOperator = /*@__PURE__*/ (function () { - function DistinctUntilChangedOperator(compare, keySelector) { - this.compare = compare; - this.keySelector = keySelector; +var SequenceEqualOperator = /*@__PURE__*/ (function () { + function SequenceEqualOperator(compareTo, comparator) { + this.compareTo = compareTo; + this.comparator = comparator; } - DistinctUntilChangedOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new DistinctUntilChangedSubscriber(subscriber, this.compare, this.keySelector)); + SequenceEqualOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new SequenceEqualSubscriber(subscriber, this.compareTo, this.comparator)); }; - return DistinctUntilChangedOperator; + return SequenceEqualOperator; }()); -var DistinctUntilChangedSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](DistinctUntilChangedSubscriber, _super); - function DistinctUntilChangedSubscriber(destination, compare, keySelector) { + +var SequenceEqualSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](SequenceEqualSubscriber, _super); + function SequenceEqualSubscriber(destination, compareTo, comparator) { var _this = _super.call(this, destination) || this; - _this.keySelector = keySelector; - _this.hasKey = false; - if (typeof compare === 'function') { - _this.compare = compare; - } + _this.compareTo = compareTo; + _this.comparator = comparator; + _this._a = []; + _this._b = []; + _this._oneComplete = false; + _this.destination.add(compareTo.subscribe(new SequenceEqualCompareToSubscriber(destination, _this))); return _this; } - DistinctUntilChangedSubscriber.prototype.compare = function (x, y) { - return x === y; + SequenceEqualSubscriber.prototype._next = function (value) { + if (this._oneComplete && this._b.length === 0) { + this.emit(false); + } + else { + this._a.push(value); + this.checkValues(); + } }; - DistinctUntilChangedSubscriber.prototype._next = function (value) { - var key; - try { - var keySelector = this.keySelector; - key = keySelector ? keySelector(value) : value; + SequenceEqualSubscriber.prototype._complete = function () { + if (this._oneComplete) { + this.emit(this._a.length === 0 && this._b.length === 0); } - catch (err) { - return this.destination.error(err); + else { + this._oneComplete = true; } - var result = false; - if (this.hasKey) { + this.unsubscribe(); + }; + SequenceEqualSubscriber.prototype.checkValues = function () { + var _c = this, _a = _c._a, _b = _c._b, comparator = _c.comparator; + while (_a.length > 0 && _b.length > 0) { + var a = _a.shift(); + var b = _b.shift(); + var areEqual = false; try { - var compare = this.compare; - result = compare(this.key, key); + areEqual = comparator ? comparator(a, b) : a === b; } - catch (err) { - return this.destination.error(err); + catch (e) { + this.destination.error(e); + } + if (!areEqual) { + this.emit(false); } } + }; + SequenceEqualSubscriber.prototype.emit = function (value) { + var destination = this.destination; + destination.next(value); + destination.complete(); + }; + SequenceEqualSubscriber.prototype.nextB = function (value) { + if (this._oneComplete && this._a.length === 0) { + this.emit(false); + } else { - this.hasKey = true; + this._b.push(value); + this.checkValues(); } - if (!result) { - this.key = key; - this.destination.next(value); + }; + SequenceEqualSubscriber.prototype.completeB = function () { + if (this._oneComplete) { + this.emit(this._a.length === 0 && this._b.length === 0); + } + else { + this._oneComplete = true; } }; - return DistinctUntilChangedSubscriber; + return SequenceEqualSubscriber; }(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -//# sourceMappingURL=distinctUntilChanged.js.map + +var SequenceEqualCompareToSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](SequenceEqualCompareToSubscriber, _super); + function SequenceEqualCompareToSubscriber(destination, parent) { + var _this = _super.call(this, destination) || this; + _this.parent = parent; + return _this; + } + SequenceEqualCompareToSubscriber.prototype._next = function (value) { + this.parent.nextB(value); + }; + SequenceEqualCompareToSubscriber.prototype._error = function (err) { + this.parent.error(err); + this.unsubscribe(); + }; + SequenceEqualCompareToSubscriber.prototype._complete = function () { + this.parent.completeB(); + this.unsubscribe(); + }; + return SequenceEqualCompareToSubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); +//# sourceMappingURL=sequenceEqual.js.map /***/ }), -/* 442 */ +/* 491 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "distinctUntilKeyChanged", function() { return distinctUntilKeyChanged; }); -/* harmony import */ var _distinctUntilChanged__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(441); -/** PURE_IMPORTS_START _distinctUntilChanged PURE_IMPORTS_END */ +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "share", function() { return share; }); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(474); +/* harmony import */ var _refCount__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(31); +/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(28); +/** PURE_IMPORTS_START _multicast,_refCount,_Subject PURE_IMPORTS_END */ -function distinctUntilKeyChanged(key, compare) { - return Object(_distinctUntilChanged__WEBPACK_IMPORTED_MODULE_0__["distinctUntilChanged"])(function (x, y) { return compare ? compare(x[key], y[key]) : x[key] === y[key]; }); + + +function shareSubjectFactory() { + return new _Subject__WEBPACK_IMPORTED_MODULE_2__["Subject"](); } -//# sourceMappingURL=distinctUntilKeyChanged.js.map +function share() { + return function (source) { return Object(_refCount__WEBPACK_IMPORTED_MODULE_1__["refCount"])()(Object(_multicast__WEBPACK_IMPORTED_MODULE_0__["multicast"])(shareSubjectFactory)(source)); }; +} +//# sourceMappingURL=share.js.map /***/ }), -/* 443 */ +/* 492 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "elementAt", function() { return elementAt; }); -/* harmony import */ var _util_ArgumentOutOfRangeError__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(63); -/* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(106); -/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(444); -/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(435); -/* harmony import */ var _take__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(445); -/** PURE_IMPORTS_START _util_ArgumentOutOfRangeError,_filter,_throwIfEmpty,_defaultIfEmpty,_take PURE_IMPORTS_END */ - - - - +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "shareReplay", function() { return shareReplay; }); +/* harmony import */ var _ReplaySubject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(34); +/** PURE_IMPORTS_START _ReplaySubject PURE_IMPORTS_END */ -function elementAt(index, defaultValue) { - if (index < 0) { - throw new _util_ArgumentOutOfRangeError__WEBPACK_IMPORTED_MODULE_0__["ArgumentOutOfRangeError"](); +function shareReplay(configOrBufferSize, windowTime, scheduler) { + var config; + if (configOrBufferSize && typeof configOrBufferSize === 'object') { + config = configOrBufferSize; } - var hasDefaultValue = arguments.length >= 2; - return function (source) { - return source.pipe(Object(_filter__WEBPACK_IMPORTED_MODULE_1__["filter"])(function (v, i) { return i === index; }), Object(_take__WEBPACK_IMPORTED_MODULE_4__["take"])(1), hasDefaultValue - ? Object(_defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__["defaultIfEmpty"])(defaultValue) - : Object(_throwIfEmpty__WEBPACK_IMPORTED_MODULE_2__["throwIfEmpty"])(function () { return new _util_ArgumentOutOfRangeError__WEBPACK_IMPORTED_MODULE_0__["ArgumentOutOfRangeError"](); })); + else { + config = { + bufferSize: configOrBufferSize, + windowTime: windowTime, + refCount: false, + scheduler: scheduler + }; + } + return function (source) { return source.lift(shareReplayOperator(config)); }; +} +function shareReplayOperator(_a) { + var _b = _a.bufferSize, bufferSize = _b === void 0 ? Number.POSITIVE_INFINITY : _b, _c = _a.windowTime, windowTime = _c === void 0 ? Number.POSITIVE_INFINITY : _c, useRefCount = _a.refCount, scheduler = _a.scheduler; + var subject; + var refCount = 0; + var subscription; + var hasError = false; + var isComplete = false; + return function shareReplayOperation(source) { + refCount++; + var innerSub; + if (!subject || hasError) { + hasError = false; + subject = new _ReplaySubject__WEBPACK_IMPORTED_MODULE_0__["ReplaySubject"](bufferSize, windowTime, scheduler); + innerSub = subject.subscribe(this); + subscription = source.subscribe({ + next: function (value) { subject.next(value); }, + error: function (err) { + hasError = true; + subject.error(err); + }, + complete: function () { + isComplete = true; + subscription = undefined; + subject.complete(); + }, + }); + } + else { + innerSub = subject.subscribe(this); + } + this.add(function () { + refCount--; + innerSub.unsubscribe(); + if (subscription && !isComplete && useRefCount && refCount === 0) { + subscription.unsubscribe(); + subscription = undefined; + subject = undefined; + } + }); }; } -//# sourceMappingURL=elementAt.js.map +//# sourceMappingURL=shareReplay.js.map /***/ }), -/* 444 */ +/* 493 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "throwIfEmpty", function() { return throwIfEmpty; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "single", function() { return single; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _util_EmptyError__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(64); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(12); -/** PURE_IMPORTS_START tslib,_util_EmptyError,_Subscriber PURE_IMPORTS_END */ +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); +/* harmony import */ var _util_EmptyError__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(64); +/** PURE_IMPORTS_START tslib,_Subscriber,_util_EmptyError PURE_IMPORTS_END */ -function throwIfEmpty(errorFactory) { - if (errorFactory === void 0) { - errorFactory = defaultErrorFactory; - } - return function (source) { - return source.lift(new ThrowIfEmptyOperator(errorFactory)); - }; +function single(predicate) { + return function (source) { return source.lift(new SingleOperator(predicate, source)); }; } -var ThrowIfEmptyOperator = /*@__PURE__*/ (function () { - function ThrowIfEmptyOperator(errorFactory) { - this.errorFactory = errorFactory; +var SingleOperator = /*@__PURE__*/ (function () { + function SingleOperator(predicate, source) { + this.predicate = predicate; + this.source = source; } - ThrowIfEmptyOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new ThrowIfEmptySubscriber(subscriber, this.errorFactory)); + SingleOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new SingleSubscriber(subscriber, this.predicate, this.source)); }; - return ThrowIfEmptyOperator; + return SingleOperator; }()); -var ThrowIfEmptySubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](ThrowIfEmptySubscriber, _super); - function ThrowIfEmptySubscriber(destination, errorFactory) { +var SingleSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](SingleSubscriber, _super); + function SingleSubscriber(destination, predicate, source) { var _this = _super.call(this, destination) || this; - _this.errorFactory = errorFactory; - _this.hasValue = false; + _this.predicate = predicate; + _this.source = source; + _this.seenValue = false; + _this.index = 0; return _this; } - ThrowIfEmptySubscriber.prototype._next = function (value) { - this.hasValue = true; - this.destination.next(value); + SingleSubscriber.prototype.applySingleValue = function (value) { + if (this.seenValue) { + this.destination.error('Sequence contains more than one element'); + } + else { + this.seenValue = true; + this.singleValue = value; + } }; - ThrowIfEmptySubscriber.prototype._complete = function () { - if (!this.hasValue) { - var err = void 0; - try { - err = this.errorFactory(); - } - catch (e) { - err = e; + SingleSubscriber.prototype._next = function (value) { + var index = this.index++; + if (this.predicate) { + this.tryNext(value, index); + } + else { + this.applySingleValue(value); + } + }; + SingleSubscriber.prototype.tryNext = function (value, index) { + try { + if (this.predicate(value, index, this.source)) { + this.applySingleValue(value); } + } + catch (err) { this.destination.error(err); } + }; + SingleSubscriber.prototype._complete = function () { + var destination = this.destination; + if (this.index > 0) { + destination.next(this.seenValue ? this.singleValue : undefined); + destination.complete(); + } else { - return this.destination.complete(); + destination.error(new _util_EmptyError__WEBPACK_IMPORTED_MODULE_2__["EmptyError"]); + } + }; + return SingleSubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); +//# sourceMappingURL=single.js.map + + +/***/ }), +/* 494 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "skip", function() { return skip; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); +/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ + + +function skip(count) { + return function (source) { return source.lift(new SkipOperator(count)); }; +} +var SkipOperator = /*@__PURE__*/ (function () { + function SkipOperator(total) { + this.total = total; + } + SkipOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new SkipSubscriber(subscriber, this.total)); + }; + return SkipOperator; +}()); +var SkipSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](SkipSubscriber, _super); + function SkipSubscriber(destination, total) { + var _this = _super.call(this, destination) || this; + _this.total = total; + _this.count = 0; + return _this; + } + SkipSubscriber.prototype._next = function (x) { + if (++this.count > this.total) { + this.destination.next(x); } }; - return ThrowIfEmptySubscriber; -}(_Subscriber__WEBPACK_IMPORTED_MODULE_2__["Subscriber"])); -function defaultErrorFactory() { - return new _util_EmptyError__WEBPACK_IMPORTED_MODULE_1__["EmptyError"](); -} -//# sourceMappingURL=throwIfEmpty.js.map + return SkipSubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); +//# sourceMappingURL=skip.js.map /***/ }), -/* 445 */ +/* 495 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "take", function() { return take; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "skipLast", function() { return skipLast; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); /* harmony import */ var _util_ArgumentOutOfRangeError__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(63); -/* harmony import */ var _observable_empty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(44); -/** PURE_IMPORTS_START tslib,_Subscriber,_util_ArgumentOutOfRangeError,_observable_empty PURE_IMPORTS_END */ - +/** PURE_IMPORTS_START tslib,_Subscriber,_util_ArgumentOutOfRangeError PURE_IMPORTS_END */ -function take(count) { - return function (source) { - if (count === 0) { - return Object(_observable_empty__WEBPACK_IMPORTED_MODULE_3__["empty"])(); - } - else { - return source.lift(new TakeOperator(count)); - } - }; +function skipLast(count) { + return function (source) { return source.lift(new SkipLastOperator(count)); }; } -var TakeOperator = /*@__PURE__*/ (function () { - function TakeOperator(total) { - this.total = total; - if (this.total < 0) { +var SkipLastOperator = /*@__PURE__*/ (function () { + function SkipLastOperator(_skipCount) { + this._skipCount = _skipCount; + if (this._skipCount < 0) { throw new _util_ArgumentOutOfRangeError__WEBPACK_IMPORTED_MODULE_2__["ArgumentOutOfRangeError"]; } } - TakeOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new TakeSubscriber(subscriber, this.total)); + SkipLastOperator.prototype.call = function (subscriber, source) { + if (this._skipCount === 0) { + return source.subscribe(new _Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"](subscriber)); + } + else { + return source.subscribe(new SkipLastSubscriber(subscriber, this._skipCount)); + } }; - return TakeOperator; + return SkipLastOperator; }()); -var TakeSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](TakeSubscriber, _super); - function TakeSubscriber(destination, total) { +var SkipLastSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](SkipLastSubscriber, _super); + function SkipLastSubscriber(destination, _skipCount) { var _this = _super.call(this, destination) || this; - _this.total = total; - _this.count = 0; + _this._skipCount = _skipCount; + _this._count = 0; + _this._ring = new Array(_skipCount); return _this; } - TakeSubscriber.prototype._next = function (value) { - var total = this.total; - var count = ++this.count; - if (count <= total) { - this.destination.next(value); - if (count === total) { - this.destination.complete(); - this.unsubscribe(); - } + SkipLastSubscriber.prototype._next = function (value) { + var skipCount = this._skipCount; + var count = this._count++; + if (count < skipCount) { + this._ring[count] = value; + } + else { + var currentIndex = count % skipCount; + var ring = this._ring; + var oldValue = ring[currentIndex]; + ring[currentIndex] = value; + this.destination.next(oldValue); } }; - return TakeSubscriber; + return SkipLastSubscriber; }(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -//# sourceMappingURL=take.js.map +//# sourceMappingURL=skipLast.js.map /***/ }), -/* 446 */ +/* 496 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "endWith", function() { return endWith; }); -/* harmony import */ var _observable_concat__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(80); -/* harmony import */ var _observable_of__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(45); -/** PURE_IMPORTS_START _observable_concat,_observable_of PURE_IMPORTS_END */ +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "skipUntil", function() { return skipUntil; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(91); +/** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ -function endWith() { - var array = []; - for (var _i = 0; _i < arguments.length; _i++) { - array[_i] = arguments[_i]; - } - return function (source) { return Object(_observable_concat__WEBPACK_IMPORTED_MODULE_0__["concat"])(source, _observable_of__WEBPACK_IMPORTED_MODULE_1__["of"].apply(void 0, array)); }; +function skipUntil(notifier) { + return function (source) { return source.lift(new SkipUntilOperator(notifier)); }; } -//# sourceMappingURL=endWith.js.map +var SkipUntilOperator = /*@__PURE__*/ (function () { + function SkipUntilOperator(notifier) { + this.notifier = notifier; + } + SkipUntilOperator.prototype.call = function (destination, source) { + return source.subscribe(new SkipUntilSubscriber(destination, this.notifier)); + }; + return SkipUntilOperator; +}()); +var SkipUntilSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](SkipUntilSubscriber, _super); + function SkipUntilSubscriber(destination, notifier) { + var _this = _super.call(this, destination) || this; + _this.hasValue = false; + var innerSubscriber = new _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleInnerSubscriber"](_this); + _this.add(innerSubscriber); + _this.innerSubscription = innerSubscriber; + var innerSubscription = Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["innerSubscribe"])(notifier, innerSubscriber); + if (innerSubscription !== innerSubscriber) { + _this.add(innerSubscription); + _this.innerSubscription = innerSubscription; + } + return _this; + } + SkipUntilSubscriber.prototype._next = function (value) { + if (this.hasValue) { + _super.prototype._next.call(this, value); + } + }; + SkipUntilSubscriber.prototype.notifyNext = function () { + this.hasValue = true; + if (this.innerSubscription) { + this.innerSubscription.unsubscribe(); + } + }; + SkipUntilSubscriber.prototype.notifyComplete = function () { + }; + return SkipUntilSubscriber; +}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleOuterSubscriber"])); +//# sourceMappingURL=skipUntil.js.map /***/ }), -/* 447 */ +/* 497 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "every", function() { return every; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "skipWhile", function() { return skipWhile; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ -function every(predicate, thisArg) { - return function (source) { return source.lift(new EveryOperator(predicate, thisArg, source)); }; +function skipWhile(predicate) { + return function (source) { return source.lift(new SkipWhileOperator(predicate)); }; } -var EveryOperator = /*@__PURE__*/ (function () { - function EveryOperator(predicate, thisArg, source) { +var SkipWhileOperator = /*@__PURE__*/ (function () { + function SkipWhileOperator(predicate) { this.predicate = predicate; - this.thisArg = thisArg; - this.source = source; } - EveryOperator.prototype.call = function (observer, source) { - return source.subscribe(new EverySubscriber(observer, this.predicate, this.thisArg, this.source)); + SkipWhileOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new SkipWhileSubscriber(subscriber, this.predicate)); }; - return EveryOperator; + return SkipWhileOperator; }()); -var EverySubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](EverySubscriber, _super); - function EverySubscriber(destination, predicate, thisArg, source) { +var SkipWhileSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](SkipWhileSubscriber, _super); + function SkipWhileSubscriber(destination, predicate) { var _this = _super.call(this, destination) || this; _this.predicate = predicate; - _this.thisArg = thisArg; - _this.source = source; + _this.skipping = true; _this.index = 0; - _this.thisArg = thisArg || _this; return _this; } - EverySubscriber.prototype.notifyComplete = function (everyValueMatch) { - this.destination.next(everyValueMatch); - this.destination.complete(); + SkipWhileSubscriber.prototype._next = function (value) { + var destination = this.destination; + if (this.skipping) { + this.tryCallPredicate(value); + } + if (!this.skipping) { + destination.next(value); + } }; - EverySubscriber.prototype._next = function (value) { - var result = false; + SkipWhileSubscriber.prototype.tryCallPredicate = function (value) { try { - result = this.predicate.call(this.thisArg, value, this.index++, this.source); + var result = this.predicate(value, this.index++); + this.skipping = Boolean(result); } catch (err) { this.destination.error(err); - return; - } - if (!result) { - this.notifyComplete(false); } }; - EverySubscriber.prototype._complete = function () { - this.notifyComplete(true); - }; - return EverySubscriber; + return SkipWhileSubscriber; }(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -//# sourceMappingURL=every.js.map +//# sourceMappingURL=skipWhile.js.map /***/ }), -/* 448 */ +/* 498 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "exhaust", function() { return exhaust; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(91); -/** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "startWith", function() { return startWith; }); +/* harmony import */ var _observable_concat__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(80); +/* harmony import */ var _util_isScheduler__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(46); +/** PURE_IMPORTS_START _observable_concat,_util_isScheduler PURE_IMPORTS_END */ -function exhaust() { - return function (source) { return source.lift(new SwitchFirstOperator()); }; +function startWith() { + var array = []; + for (var _i = 0; _i < arguments.length; _i++) { + array[_i] = arguments[_i]; + } + var scheduler = array[array.length - 1]; + if (Object(_util_isScheduler__WEBPACK_IMPORTED_MODULE_1__["isScheduler"])(scheduler)) { + array.pop(); + return function (source) { return Object(_observable_concat__WEBPACK_IMPORTED_MODULE_0__["concat"])(array, source, scheduler); }; + } + else { + return function (source) { return Object(_observable_concat__WEBPACK_IMPORTED_MODULE_0__["concat"])(array, source); }; + } } -var SwitchFirstOperator = /*@__PURE__*/ (function () { - function SwitchFirstOperator() { +//# sourceMappingURL=startWith.js.map + + +/***/ }), +/* 499 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "subscribeOn", function() { return subscribeOn; }); +/* harmony import */ var _observable_SubscribeOnObservable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(500); +/** PURE_IMPORTS_START _observable_SubscribeOnObservable PURE_IMPORTS_END */ + +function subscribeOn(scheduler, delay) { + if (delay === void 0) { + delay = 0; } - SwitchFirstOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new SwitchFirstSubscriber(subscriber)); + return function subscribeOnOperatorFunction(source) { + return source.lift(new SubscribeOnOperator(scheduler, delay)); }; - return SwitchFirstOperator; -}()); -var SwitchFirstSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](SwitchFirstSubscriber, _super); - function SwitchFirstSubscriber(destination) { - var _this = _super.call(this, destination) || this; - _this.hasCompleted = false; - _this.hasSubscription = false; - return _this; +} +var SubscribeOnOperator = /*@__PURE__*/ (function () { + function SubscribeOnOperator(scheduler, delay) { + this.scheduler = scheduler; + this.delay = delay; } - SwitchFirstSubscriber.prototype._next = function (value) { - if (!this.hasSubscription) { - this.hasSubscription = true; - this.add(Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["innerSubscribe"])(value, new _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleInnerSubscriber"](this))); - } - }; - SwitchFirstSubscriber.prototype._complete = function () { - this.hasCompleted = true; - if (!this.hasSubscription) { - this.destination.complete(); - } - }; - SwitchFirstSubscriber.prototype.notifyComplete = function () { - this.hasSubscription = false; - if (this.hasCompleted) { - this.destination.complete(); - } + SubscribeOnOperator.prototype.call = function (subscriber, source) { + return new _observable_SubscribeOnObservable__WEBPACK_IMPORTED_MODULE_0__["SubscribeOnObservable"](source, this.delay, this.scheduler).subscribe(subscriber); }; - return SwitchFirstSubscriber; -}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleOuterSubscriber"])); -//# sourceMappingURL=exhaust.js.map + return SubscribeOnOperator; +}()); +//# sourceMappingURL=subscribeOn.js.map /***/ }), -/* 449 */ +/* 500 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "exhaustMap", function() { return exhaustMap; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SubscribeOnObservable", function() { return SubscribeOnObservable; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(67); -/* harmony import */ var _observable_from__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(84); -/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(91); -/** PURE_IMPORTS_START tslib,_map,_observable_from,_innerSubscribe PURE_IMPORTS_END */ +/* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(10); +/* harmony import */ var _scheduler_asap__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(52); +/* harmony import */ var _util_isNumeric__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(99); +/** PURE_IMPORTS_START tslib,_Observable,_scheduler_asap,_util_isNumeric PURE_IMPORTS_END */ -function exhaustMap(project, resultSelector) { - if (resultSelector) { - return function (source) { return source.pipe(exhaustMap(function (a, i) { return Object(_observable_from__WEBPACK_IMPORTED_MODULE_2__["from"])(project(a, i)).pipe(Object(_map__WEBPACK_IMPORTED_MODULE_1__["map"])(function (b, ii) { return resultSelector(a, b, i, ii); })); })); }; - } - return function (source) { - return source.lift(new ExhaustMapOperator(project)); - }; -} -var ExhaustMapOperator = /*@__PURE__*/ (function () { - function ExhaustMapOperator(project) { - this.project = project; - } - ExhaustMapOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new ExhaustMapSubscriber(subscriber, this.project)); - }; - return ExhaustMapOperator; -}()); -var ExhaustMapSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](ExhaustMapSubscriber, _super); - function ExhaustMapSubscriber(destination, project) { - var _this = _super.call(this, destination) || this; - _this.project = project; - _this.hasSubscription = false; - _this.hasCompleted = false; - _this.index = 0; - return _this; - } - ExhaustMapSubscriber.prototype._next = function (value) { - if (!this.hasSubscription) { - this.tryNext(value); +var SubscribeOnObservable = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](SubscribeOnObservable, _super); + function SubscribeOnObservable(source, delayTime, scheduler) { + if (delayTime === void 0) { + delayTime = 0; } - }; - ExhaustMapSubscriber.prototype.tryNext = function (value) { - var result; - var index = this.index++; - try { - result = this.project(value, index); + if (scheduler === void 0) { + scheduler = _scheduler_asap__WEBPACK_IMPORTED_MODULE_2__["asap"]; } - catch (err) { - this.destination.error(err); - return; + var _this = _super.call(this) || this; + _this.source = source; + _this.delayTime = delayTime; + _this.scheduler = scheduler; + if (!Object(_util_isNumeric__WEBPACK_IMPORTED_MODULE_3__["isNumeric"])(delayTime) || delayTime < 0) { + _this.delayTime = 0; } - this.hasSubscription = true; - this._innerSub(result); - }; - ExhaustMapSubscriber.prototype._innerSub = function (result) { - var innerSubscriber = new _innerSubscribe__WEBPACK_IMPORTED_MODULE_3__["SimpleInnerSubscriber"](this); - var destination = this.destination; - destination.add(innerSubscriber); - var innerSubscription = Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_3__["innerSubscribe"])(result, innerSubscriber); - if (innerSubscription !== innerSubscriber) { - destination.add(innerSubscription); + if (!scheduler || typeof scheduler.schedule !== 'function') { + _this.scheduler = _scheduler_asap__WEBPACK_IMPORTED_MODULE_2__["asap"]; } - }; - ExhaustMapSubscriber.prototype._complete = function () { - this.hasCompleted = true; - if (!this.hasSubscription) { - this.destination.complete(); + return _this; + } + SubscribeOnObservable.create = function (source, delay, scheduler) { + if (delay === void 0) { + delay = 0; } - this.unsubscribe(); - }; - ExhaustMapSubscriber.prototype.notifyNext = function (innerValue) { - this.destination.next(innerValue); + if (scheduler === void 0) { + scheduler = _scheduler_asap__WEBPACK_IMPORTED_MODULE_2__["asap"]; + } + return new SubscribeOnObservable(source, delay, scheduler); }; - ExhaustMapSubscriber.prototype.notifyError = function (err) { - this.destination.error(err); + SubscribeOnObservable.dispatch = function (arg) { + var source = arg.source, subscriber = arg.subscriber; + return this.add(source.subscribe(subscriber)); }; - ExhaustMapSubscriber.prototype.notifyComplete = function () { - this.hasSubscription = false; - if (this.hasCompleted) { - this.destination.complete(); - } + SubscribeOnObservable.prototype._subscribe = function (subscriber) { + var delay = this.delayTime; + var source = this.source; + var scheduler = this.scheduler; + return scheduler.schedule(SubscribeOnObservable.dispatch, delay, { + source: source, subscriber: subscriber + }); }; - return ExhaustMapSubscriber; -}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_3__["SimpleOuterSubscriber"])); -//# sourceMappingURL=exhaustMap.js.map + return SubscribeOnObservable; +}(_Observable__WEBPACK_IMPORTED_MODULE_1__["Observable"])); + +//# sourceMappingURL=SubscribeOnObservable.js.map /***/ }), -/* 450 */ +/* 501 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "expand", function() { return expand; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ExpandOperator", function() { return ExpandOperator; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ExpandSubscriber", function() { return ExpandSubscriber; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "switchAll", function() { return switchAll; }); +/* harmony import */ var _switchMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(502); +/* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(26); +/** PURE_IMPORTS_START _switchMap,_util_identity PURE_IMPORTS_END */ + + +function switchAll() { + return Object(_switchMap__WEBPACK_IMPORTED_MODULE_0__["switchMap"])(_util_identity__WEBPACK_IMPORTED_MODULE_1__["identity"]); +} +//# sourceMappingURL=switchAll.js.map + + +/***/ }), +/* 502 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "switchMap", function() { return switchMap; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(91); -/** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ +/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(67); +/* harmony import */ var _observable_from__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(84); +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(91); +/** PURE_IMPORTS_START tslib,_map,_observable_from,_innerSubscribe PURE_IMPORTS_END */ -function expand(project, concurrent, scheduler) { - if (concurrent === void 0) { - concurrent = Number.POSITIVE_INFINITY; + + +function switchMap(project, resultSelector) { + if (typeof resultSelector === 'function') { + return function (source) { return source.pipe(switchMap(function (a, i) { return Object(_observable_from__WEBPACK_IMPORTED_MODULE_2__["from"])(project(a, i)).pipe(Object(_map__WEBPACK_IMPORTED_MODULE_1__["map"])(function (b, ii) { return resultSelector(a, b, i, ii); })); })); }; } - concurrent = (concurrent || 0) < 1 ? Number.POSITIVE_INFINITY : concurrent; - return function (source) { return source.lift(new ExpandOperator(project, concurrent, scheduler)); }; + return function (source) { return source.lift(new SwitchMapOperator(project)); }; } -var ExpandOperator = /*@__PURE__*/ (function () { - function ExpandOperator(project, concurrent, scheduler) { +var SwitchMapOperator = /*@__PURE__*/ (function () { + function SwitchMapOperator(project) { this.project = project; - this.concurrent = concurrent; - this.scheduler = scheduler; - } - ExpandOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new ExpandSubscriber(subscriber, this.project, this.concurrent, this.scheduler)); - }; - return ExpandOperator; -}()); - -var ExpandSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](ExpandSubscriber, _super); - function ExpandSubscriber(destination, project, concurrent, scheduler) { - var _this = _super.call(this, destination) || this; - _this.project = project; - _this.concurrent = concurrent; - _this.scheduler = scheduler; - _this.index = 0; - _this.active = 0; - _this.hasCompleted = false; - if (concurrent < Number.POSITIVE_INFINITY) { - _this.buffer = []; - } - return _this; } - ExpandSubscriber.dispatch = function (arg) { - var subscriber = arg.subscriber, result = arg.result, value = arg.value, index = arg.index; - subscriber.subscribeToProjection(result, value, index); - }; - ExpandSubscriber.prototype._next = function (value) { - var destination = this.destination; - if (destination.closed) { - this._complete(); - return; - } - var index = this.index++; - if (this.active < this.concurrent) { - destination.next(value); - try { - var project = this.project; - var result = project(value, index); - if (!this.scheduler) { - this.subscribeToProjection(result, value, index); - } - else { - var state = { subscriber: this, result: result, value: value, index: index }; - var destination_1 = this.destination; - destination_1.add(this.scheduler.schedule(ExpandSubscriber.dispatch, 0, state)); - } - } - catch (e) { - destination.error(e); - } + SwitchMapOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new SwitchMapSubscriber(subscriber, this.project)); + }; + return SwitchMapOperator; +}()); +var SwitchMapSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](SwitchMapSubscriber, _super); + function SwitchMapSubscriber(destination, project) { + var _this = _super.call(this, destination) || this; + _this.project = project; + _this.index = 0; + return _this; + } + SwitchMapSubscriber.prototype._next = function (value) { + var result; + var index = this.index++; + try { + result = this.project(value, index); } - else { - this.buffer.push(value); + catch (error) { + this.destination.error(error); + return; } + this._innerSub(result); }; - ExpandSubscriber.prototype.subscribeToProjection = function (result, value, index) { - this.active++; + SwitchMapSubscriber.prototype._innerSub = function (result) { + var innerSubscription = this.innerSubscription; + if (innerSubscription) { + innerSubscription.unsubscribe(); + } + var innerSubscriber = new _innerSubscribe__WEBPACK_IMPORTED_MODULE_3__["SimpleInnerSubscriber"](this); var destination = this.destination; - destination.add(Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["innerSubscribe"])(result, new _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleInnerSubscriber"](this))); + destination.add(innerSubscriber); + this.innerSubscription = Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_3__["innerSubscribe"])(result, innerSubscriber); + if (this.innerSubscription !== innerSubscriber) { + destination.add(this.innerSubscription); + } }; - ExpandSubscriber.prototype._complete = function () { - this.hasCompleted = true; - if (this.hasCompleted && this.active === 0) { - this.destination.complete(); + SwitchMapSubscriber.prototype._complete = function () { + var innerSubscription = this.innerSubscription; + if (!innerSubscription || innerSubscription.closed) { + _super.prototype._complete.call(this); } this.unsubscribe(); }; - ExpandSubscriber.prototype.notifyNext = function (innerValue) { - this._next(innerValue); + SwitchMapSubscriber.prototype._unsubscribe = function () { + this.innerSubscription = undefined; }; - ExpandSubscriber.prototype.notifyComplete = function () { - var buffer = this.buffer; - this.active--; - if (buffer && buffer.length > 0) { - this._next(buffer.shift()); - } - if (this.hasCompleted && this.active === 0) { - this.destination.complete(); + SwitchMapSubscriber.prototype.notifyComplete = function () { + this.innerSubscription = undefined; + if (this.isStopped) { + _super.prototype._complete.call(this); } }; - return ExpandSubscriber; -}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleOuterSubscriber"])); + SwitchMapSubscriber.prototype.notifyNext = function (innerValue) { + this.destination.next(innerValue); + }; + return SwitchMapSubscriber; +}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_3__["SimpleOuterSubscriber"])); +//# sourceMappingURL=switchMap.js.map -//# sourceMappingURL=expand.js.map + +/***/ }), +/* 503 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "switchMapTo", function() { return switchMapTo; }); +/* harmony import */ var _switchMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(502); +/** PURE_IMPORTS_START _switchMap PURE_IMPORTS_END */ + +function switchMapTo(innerObservable, resultSelector) { + return resultSelector ? Object(_switchMap__WEBPACK_IMPORTED_MODULE_0__["switchMap"])(function () { return innerObservable; }, resultSelector) : Object(_switchMap__WEBPACK_IMPORTED_MODULE_0__["switchMap"])(function () { return innerObservable; }); +} +//# sourceMappingURL=switchMapTo.js.map /***/ }), -/* 451 */ +/* 504 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "finalize", function() { return finalize; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "takeUntil", function() { return takeUntil; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); -/* harmony import */ var _Subscription__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(18); -/** PURE_IMPORTS_START tslib,_Subscriber,_Subscription PURE_IMPORTS_END */ - +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(91); +/** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ -function finalize(callback) { - return function (source) { return source.lift(new FinallyOperator(callback)); }; +function takeUntil(notifier) { + return function (source) { return source.lift(new TakeUntilOperator(notifier)); }; } -var FinallyOperator = /*@__PURE__*/ (function () { - function FinallyOperator(callback) { - this.callback = callback; +var TakeUntilOperator = /*@__PURE__*/ (function () { + function TakeUntilOperator(notifier) { + this.notifier = notifier; } - FinallyOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new FinallySubscriber(subscriber, this.callback)); + TakeUntilOperator.prototype.call = function (subscriber, source) { + var takeUntilSubscriber = new TakeUntilSubscriber(subscriber); + var notifierSubscription = Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["innerSubscribe"])(this.notifier, new _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleInnerSubscriber"](takeUntilSubscriber)); + if (notifierSubscription && !takeUntilSubscriber.seenValue) { + takeUntilSubscriber.add(notifierSubscription); + return source.subscribe(takeUntilSubscriber); + } + return takeUntilSubscriber; }; - return FinallyOperator; + return TakeUntilOperator; }()); -var FinallySubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](FinallySubscriber, _super); - function FinallySubscriber(destination, callback) { +var TakeUntilSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](TakeUntilSubscriber, _super); + function TakeUntilSubscriber(destination) { var _this = _super.call(this, destination) || this; - _this.add(new _Subscription__WEBPACK_IMPORTED_MODULE_2__["Subscription"](callback)); + _this.seenValue = false; return _this; } - return FinallySubscriber; -}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -//# sourceMappingURL=finalize.js.map + TakeUntilSubscriber.prototype.notifyNext = function () { + this.seenValue = true; + this.complete(); + }; + TakeUntilSubscriber.prototype.notifyComplete = function () { + }; + return TakeUntilSubscriber; +}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleOuterSubscriber"])); +//# sourceMappingURL=takeUntil.js.map /***/ }), -/* 452 */ +/* 505 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "find", function() { return find; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "FindValueOperator", function() { return FindValueOperator; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "FindValueSubscriber", function() { return FindValueSubscriber; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "takeWhile", function() { return takeWhile; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ -function find(predicate, thisArg) { - if (typeof predicate !== 'function') { - throw new TypeError('predicate is not a function'); +function takeWhile(predicate, inclusive) { + if (inclusive === void 0) { + inclusive = false; } - return function (source) { return source.lift(new FindValueOperator(predicate, source, false, thisArg)); }; + return function (source) { + return source.lift(new TakeWhileOperator(predicate, inclusive)); + }; } -var FindValueOperator = /*@__PURE__*/ (function () { - function FindValueOperator(predicate, source, yieldIndex, thisArg) { +var TakeWhileOperator = /*@__PURE__*/ (function () { + function TakeWhileOperator(predicate, inclusive) { this.predicate = predicate; - this.source = source; - this.yieldIndex = yieldIndex; - this.thisArg = thisArg; + this.inclusive = inclusive; } - FindValueOperator.prototype.call = function (observer, source) { - return source.subscribe(new FindValueSubscriber(observer, this.predicate, this.source, this.yieldIndex, this.thisArg)); + TakeWhileOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new TakeWhileSubscriber(subscriber, this.predicate, this.inclusive)); }; - return FindValueOperator; + return TakeWhileOperator; }()); - -var FindValueSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](FindValueSubscriber, _super); - function FindValueSubscriber(destination, predicate, source, yieldIndex, thisArg) { +var TakeWhileSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](TakeWhileSubscriber, _super); + function TakeWhileSubscriber(destination, predicate, inclusive) { var _this = _super.call(this, destination) || this; _this.predicate = predicate; - _this.source = source; - _this.yieldIndex = yieldIndex; - _this.thisArg = thisArg; + _this.inclusive = inclusive; _this.index = 0; return _this; } - FindValueSubscriber.prototype.notifyComplete = function (value) { + TakeWhileSubscriber.prototype._next = function (value) { var destination = this.destination; - destination.next(value); - destination.complete(); - this.unsubscribe(); - }; - FindValueSubscriber.prototype._next = function (value) { - var _a = this, predicate = _a.predicate, thisArg = _a.thisArg; - var index = this.index++; + var result; try { - var result = predicate.call(thisArg || this, value, index, this.source); - if (result) { - this.notifyComplete(this.yieldIndex ? index : value); - } + result = this.predicate(value, this.index++); } catch (err) { - this.destination.error(err); + destination.error(err); + return; } + this.nextOrComplete(value, result); }; - FindValueSubscriber.prototype._complete = function () { - this.notifyComplete(this.yieldIndex ? -1 : undefined); + TakeWhileSubscriber.prototype.nextOrComplete = function (value, predicateResult) { + var destination = this.destination; + if (Boolean(predicateResult)) { + destination.next(value); + } + else { + if (this.inclusive) { + destination.next(value); + } + destination.complete(); + } }; - return FindValueSubscriber; + return TakeWhileSubscriber; }(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); - -//# sourceMappingURL=find.js.map +//# sourceMappingURL=takeWhile.js.map /***/ }), -/* 453 */ +/* 506 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "findIndex", function() { return findIndex; }); -/* harmony import */ var _operators_find__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(452); -/** PURE_IMPORTS_START _operators_find PURE_IMPORTS_END */ +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "tap", function() { return tap; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); +/* harmony import */ var _util_noop__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(61); +/* harmony import */ var _util_isFunction__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(14); +/** PURE_IMPORTS_START tslib,_Subscriber,_util_noop,_util_isFunction PURE_IMPORTS_END */ -function findIndex(predicate, thisArg) { - return function (source) { return source.lift(new _operators_find__WEBPACK_IMPORTED_MODULE_0__["FindValueOperator"](predicate, source, true, thisArg)); }; + + + +function tap(nextOrObserver, error, complete) { + return function tapOperatorFunction(source) { + return source.lift(new DoOperator(nextOrObserver, error, complete)); + }; } -//# sourceMappingURL=findIndex.js.map +var DoOperator = /*@__PURE__*/ (function () { + function DoOperator(nextOrObserver, error, complete) { + this.nextOrObserver = nextOrObserver; + this.error = error; + this.complete = complete; + } + DoOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new TapSubscriber(subscriber, this.nextOrObserver, this.error, this.complete)); + }; + return DoOperator; +}()); +var TapSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](TapSubscriber, _super); + function TapSubscriber(destination, observerOrNext, error, complete) { + var _this = _super.call(this, destination) || this; + _this._tapNext = _util_noop__WEBPACK_IMPORTED_MODULE_2__["noop"]; + _this._tapError = _util_noop__WEBPACK_IMPORTED_MODULE_2__["noop"]; + _this._tapComplete = _util_noop__WEBPACK_IMPORTED_MODULE_2__["noop"]; + _this._tapError = error || _util_noop__WEBPACK_IMPORTED_MODULE_2__["noop"]; + _this._tapComplete = complete || _util_noop__WEBPACK_IMPORTED_MODULE_2__["noop"]; + if (Object(_util_isFunction__WEBPACK_IMPORTED_MODULE_3__["isFunction"])(observerOrNext)) { + _this._context = _this; + _this._tapNext = observerOrNext; + } + else if (observerOrNext) { + _this._context = observerOrNext; + _this._tapNext = observerOrNext.next || _util_noop__WEBPACK_IMPORTED_MODULE_2__["noop"]; + _this._tapError = observerOrNext.error || _util_noop__WEBPACK_IMPORTED_MODULE_2__["noop"]; + _this._tapComplete = observerOrNext.complete || _util_noop__WEBPACK_IMPORTED_MODULE_2__["noop"]; + } + return _this; + } + TapSubscriber.prototype._next = function (value) { + try { + this._tapNext.call(this._context, value); + } + catch (err) { + this.destination.error(err); + return; + } + this.destination.next(value); + }; + TapSubscriber.prototype._error = function (err) { + try { + this._tapError.call(this._context, err); + } + catch (err) { + this.destination.error(err); + return; + } + this.destination.error(err); + }; + TapSubscriber.prototype._complete = function () { + try { + this._tapComplete.call(this._context); + } + catch (err) { + this.destination.error(err); + return; + } + return this.destination.complete(); + }; + return TapSubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); +//# sourceMappingURL=tap.js.map /***/ }), -/* 454 */ +/* 507 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "first", function() { return first; }); -/* harmony import */ var _util_EmptyError__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(64); -/* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(106); -/* harmony import */ var _take__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(445); -/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(435); -/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(444); -/* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(26); -/** PURE_IMPORTS_START _util_EmptyError,_filter,_take,_defaultIfEmpty,_throwIfEmpty,_util_identity PURE_IMPORTS_END */ - - - - +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "defaultThrottleConfig", function() { return defaultThrottleConfig; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "throttle", function() { return throttle; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(91); +/** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ -function first(predicate, defaultValue) { - var hasDefaultValue = arguments.length >= 2; - return function (source) { return source.pipe(predicate ? Object(_filter__WEBPACK_IMPORTED_MODULE_1__["filter"])(function (v, i) { return predicate(v, i, source); }) : _util_identity__WEBPACK_IMPORTED_MODULE_5__["identity"], Object(_take__WEBPACK_IMPORTED_MODULE_2__["take"])(1), hasDefaultValue ? Object(_defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__["defaultIfEmpty"])(defaultValue) : Object(_throwIfEmpty__WEBPACK_IMPORTED_MODULE_4__["throwIfEmpty"])(function () { return new _util_EmptyError__WEBPACK_IMPORTED_MODULE_0__["EmptyError"](); })); }; +var defaultThrottleConfig = { + leading: true, + trailing: false +}; +function throttle(durationSelector, config) { + if (config === void 0) { + config = defaultThrottleConfig; + } + return function (source) { return source.lift(new ThrottleOperator(durationSelector, !!config.leading, !!config.trailing)); }; } -//# sourceMappingURL=first.js.map +var ThrottleOperator = /*@__PURE__*/ (function () { + function ThrottleOperator(durationSelector, leading, trailing) { + this.durationSelector = durationSelector; + this.leading = leading; + this.trailing = trailing; + } + ThrottleOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new ThrottleSubscriber(subscriber, this.durationSelector, this.leading, this.trailing)); + }; + return ThrottleOperator; +}()); +var ThrottleSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](ThrottleSubscriber, _super); + function ThrottleSubscriber(destination, durationSelector, _leading, _trailing) { + var _this = _super.call(this, destination) || this; + _this.destination = destination; + _this.durationSelector = durationSelector; + _this._leading = _leading; + _this._trailing = _trailing; + _this._hasValue = false; + return _this; + } + ThrottleSubscriber.prototype._next = function (value) { + this._hasValue = true; + this._sendValue = value; + if (!this._throttled) { + if (this._leading) { + this.send(); + } + else { + this.throttle(value); + } + } + }; + ThrottleSubscriber.prototype.send = function () { + var _a = this, _hasValue = _a._hasValue, _sendValue = _a._sendValue; + if (_hasValue) { + this.destination.next(_sendValue); + this.throttle(_sendValue); + } + this._hasValue = false; + this._sendValue = undefined; + }; + ThrottleSubscriber.prototype.throttle = function (value) { + var duration = this.tryDurationSelector(value); + if (!!duration) { + this.add(this._throttled = Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["innerSubscribe"])(duration, new _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleInnerSubscriber"](this))); + } + }; + ThrottleSubscriber.prototype.tryDurationSelector = function (value) { + try { + return this.durationSelector(value); + } + catch (err) { + this.destination.error(err); + return null; + } + }; + ThrottleSubscriber.prototype.throttlingDone = function () { + var _a = this, _throttled = _a._throttled, _trailing = _a._trailing; + if (_throttled) { + _throttled.unsubscribe(); + } + this._throttled = undefined; + if (_trailing) { + this.send(); + } + }; + ThrottleSubscriber.prototype.notifyNext = function () { + this.throttlingDone(); + }; + ThrottleSubscriber.prototype.notifyComplete = function () { + this.throttlingDone(); + }; + return ThrottleSubscriber; +}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleOuterSubscriber"])); +//# sourceMappingURL=throttle.js.map /***/ }), -/* 455 */ +/* 508 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ignoreElements", function() { return ignoreElements; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "throttleTime", function() { return throttleTime; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); -/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ +/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(56); +/* harmony import */ var _throttle__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(507); +/** PURE_IMPORTS_START tslib,_Subscriber,_scheduler_async,_throttle PURE_IMPORTS_END */ -function ignoreElements() { - return function ignoreElementsOperatorFunction(source) { - return source.lift(new IgnoreElementsOperator()); - }; + + +function throttleTime(duration, scheduler, config) { + if (scheduler === void 0) { + scheduler = _scheduler_async__WEBPACK_IMPORTED_MODULE_2__["async"]; + } + if (config === void 0) { + config = _throttle__WEBPACK_IMPORTED_MODULE_3__["defaultThrottleConfig"]; + } + return function (source) { return source.lift(new ThrottleTimeOperator(duration, scheduler, config.leading, config.trailing)); }; } -var IgnoreElementsOperator = /*@__PURE__*/ (function () { - function IgnoreElementsOperator() { +var ThrottleTimeOperator = /*@__PURE__*/ (function () { + function ThrottleTimeOperator(duration, scheduler, leading, trailing) { + this.duration = duration; + this.scheduler = scheduler; + this.leading = leading; + this.trailing = trailing; } - IgnoreElementsOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new IgnoreElementsSubscriber(subscriber)); + ThrottleTimeOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new ThrottleTimeSubscriber(subscriber, this.duration, this.scheduler, this.leading, this.trailing)); }; - return IgnoreElementsOperator; + return ThrottleTimeOperator; }()); -var IgnoreElementsSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](IgnoreElementsSubscriber, _super); - function IgnoreElementsSubscriber() { - return _super !== null && _super.apply(this, arguments) || this; +var ThrottleTimeSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](ThrottleTimeSubscriber, _super); + function ThrottleTimeSubscriber(destination, duration, scheduler, leading, trailing) { + var _this = _super.call(this, destination) || this; + _this.duration = duration; + _this.scheduler = scheduler; + _this.leading = leading; + _this.trailing = trailing; + _this._hasTrailingValue = false; + _this._trailingValue = null; + return _this; } - IgnoreElementsSubscriber.prototype._next = function (unused) { + ThrottleTimeSubscriber.prototype._next = function (value) { + if (this.throttled) { + if (this.trailing) { + this._trailingValue = value; + this._hasTrailingValue = true; + } + } + else { + this.add(this.throttled = this.scheduler.schedule(dispatchNext, this.duration, { subscriber: this })); + if (this.leading) { + this.destination.next(value); + } + else if (this.trailing) { + this._trailingValue = value; + this._hasTrailingValue = true; + } + } }; - return IgnoreElementsSubscriber; + ThrottleTimeSubscriber.prototype._complete = function () { + if (this._hasTrailingValue) { + this.destination.next(this._trailingValue); + this.destination.complete(); + } + else { + this.destination.complete(); + } + }; + ThrottleTimeSubscriber.prototype.clearThrottle = function () { + var throttled = this.throttled; + if (throttled) { + if (this.trailing && this._hasTrailingValue) { + this.destination.next(this._trailingValue); + this._trailingValue = null; + this._hasTrailingValue = false; + } + throttled.unsubscribe(); + this.remove(throttled); + this.throttled = null; + } + }; + return ThrottleTimeSubscriber; }(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -//# sourceMappingURL=ignoreElements.js.map +function dispatchNext(arg) { + var subscriber = arg.subscriber; + subscriber.clearThrottle(); +} +//# sourceMappingURL=throttleTime.js.map /***/ }), -/* 456 */ +/* 509 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isEmpty", function() { return isEmpty; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); -/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "timeInterval", function() { return timeInterval; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TimeInterval", function() { return TimeInterval; }); +/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(56); +/* harmony import */ var _scan__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(469); +/* harmony import */ var _observable_defer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(92); +/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(67); +/** PURE_IMPORTS_START _scheduler_async,_scan,_observable_defer,_map PURE_IMPORTS_END */ -function isEmpty() { - return function (source) { return source.lift(new IsEmptyOperator()); }; -} -var IsEmptyOperator = /*@__PURE__*/ (function () { - function IsEmptyOperator() { + + +function timeInterval(scheduler) { + if (scheduler === void 0) { + scheduler = _scheduler_async__WEBPACK_IMPORTED_MODULE_0__["async"]; } - IsEmptyOperator.prototype.call = function (observer, source) { - return source.subscribe(new IsEmptySubscriber(observer)); + return function (source) { + return Object(_observable_defer__WEBPACK_IMPORTED_MODULE_2__["defer"])(function () { + return source.pipe(Object(_scan__WEBPACK_IMPORTED_MODULE_1__["scan"])(function (_a, value) { + var current = _a.current; + return ({ value: value, current: scheduler.now(), last: current }); + }, { current: scheduler.now(), value: undefined, last: undefined }), Object(_map__WEBPACK_IMPORTED_MODULE_3__["map"])(function (_a) { + var current = _a.current, last = _a.last, value = _a.value; + return new TimeInterval(value, current - last); + })); + }); }; - return IsEmptyOperator; -}()); -var IsEmptySubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](IsEmptySubscriber, _super); - function IsEmptySubscriber(destination) { - return _super.call(this, destination) || this; +} +var TimeInterval = /*@__PURE__*/ (function () { + function TimeInterval(value, interval) { + this.value = value; + this.interval = interval; } - IsEmptySubscriber.prototype.notifyComplete = function (isEmpty) { - var destination = this.destination; - destination.next(isEmpty); - destination.complete(); - }; - IsEmptySubscriber.prototype._next = function (value) { - this.notifyComplete(false); - }; - IsEmptySubscriber.prototype._complete = function () { - this.notifyComplete(true); - }; - return IsEmptySubscriber; -}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -//# sourceMappingURL=isEmpty.js.map + return TimeInterval; +}()); + +//# sourceMappingURL=timeInterval.js.map /***/ }), -/* 457 */ +/* 510 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "last", function() { return last; }); -/* harmony import */ var _util_EmptyError__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(64); -/* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(106); -/* harmony import */ var _takeLast__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(458); -/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(444); -/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(435); -/* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(26); -/** PURE_IMPORTS_START _util_EmptyError,_filter,_takeLast,_throwIfEmpty,_defaultIfEmpty,_util_identity PURE_IMPORTS_END */ - - +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "timeout", function() { return timeout; }); +/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(56); +/* harmony import */ var _util_TimeoutError__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(65); +/* harmony import */ var _timeoutWith__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(511); +/* harmony import */ var _observable_throwError__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(50); +/** PURE_IMPORTS_START _scheduler_async,_util_TimeoutError,_timeoutWith,_observable_throwError PURE_IMPORTS_END */ -function last(predicate, defaultValue) { - var hasDefaultValue = arguments.length >= 2; - return function (source) { return source.pipe(predicate ? Object(_filter__WEBPACK_IMPORTED_MODULE_1__["filter"])(function (v, i) { return predicate(v, i, source); }) : _util_identity__WEBPACK_IMPORTED_MODULE_5__["identity"], Object(_takeLast__WEBPACK_IMPORTED_MODULE_2__["takeLast"])(1), hasDefaultValue ? Object(_defaultIfEmpty__WEBPACK_IMPORTED_MODULE_4__["defaultIfEmpty"])(defaultValue) : Object(_throwIfEmpty__WEBPACK_IMPORTED_MODULE_3__["throwIfEmpty"])(function () { return new _util_EmptyError__WEBPACK_IMPORTED_MODULE_0__["EmptyError"](); })); }; +function timeout(due, scheduler) { + if (scheduler === void 0) { + scheduler = _scheduler_async__WEBPACK_IMPORTED_MODULE_0__["async"]; + } + return Object(_timeoutWith__WEBPACK_IMPORTED_MODULE_2__["timeoutWith"])(due, Object(_observable_throwError__WEBPACK_IMPORTED_MODULE_3__["throwError"])(new _util_TimeoutError__WEBPACK_IMPORTED_MODULE_1__["TimeoutError"]()), scheduler); } -//# sourceMappingURL=last.js.map +//# sourceMappingURL=timeout.js.map /***/ }), -/* 458 */ +/* 511 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "takeLast", function() { return takeLast; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "timeoutWith", function() { return timeoutWith; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); -/* harmony import */ var _util_ArgumentOutOfRangeError__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(63); -/* harmony import */ var _observable_empty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(44); -/** PURE_IMPORTS_START tslib,_Subscriber,_util_ArgumentOutOfRangeError,_observable_empty PURE_IMPORTS_END */ +/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(56); +/* harmony import */ var _util_isDate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(443); +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(91); +/** PURE_IMPORTS_START tslib,_scheduler_async,_util_isDate,_innerSubscribe PURE_IMPORTS_END */ -function takeLast(count) { - return function takeLastOperatorFunction(source) { - if (count === 0) { - return Object(_observable_empty__WEBPACK_IMPORTED_MODULE_3__["empty"])(); - } - else { - return source.lift(new TakeLastOperator(count)); - } +function timeoutWith(due, withObservable, scheduler) { + if (scheduler === void 0) { + scheduler = _scheduler_async__WEBPACK_IMPORTED_MODULE_1__["async"]; + } + return function (source) { + var absoluteTimeout = Object(_util_isDate__WEBPACK_IMPORTED_MODULE_2__["isDate"])(due); + var waitFor = absoluteTimeout ? (+due - scheduler.now()) : Math.abs(due); + return source.lift(new TimeoutWithOperator(waitFor, absoluteTimeout, withObservable, scheduler)); }; } -var TakeLastOperator = /*@__PURE__*/ (function () { - function TakeLastOperator(total) { - this.total = total; - if (this.total < 0) { - throw new _util_ArgumentOutOfRangeError__WEBPACK_IMPORTED_MODULE_2__["ArgumentOutOfRangeError"]; - } +var TimeoutWithOperator = /*@__PURE__*/ (function () { + function TimeoutWithOperator(waitFor, absoluteTimeout, withObservable, scheduler) { + this.waitFor = waitFor; + this.absoluteTimeout = absoluteTimeout; + this.withObservable = withObservable; + this.scheduler = scheduler; } - TakeLastOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new TakeLastSubscriber(subscriber, this.total)); + TimeoutWithOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new TimeoutWithSubscriber(subscriber, this.absoluteTimeout, this.waitFor, this.withObservable, this.scheduler)); }; - return TakeLastOperator; + return TimeoutWithOperator; }()); -var TakeLastSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](TakeLastSubscriber, _super); - function TakeLastSubscriber(destination, total) { +var TimeoutWithSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](TimeoutWithSubscriber, _super); + function TimeoutWithSubscriber(destination, absoluteTimeout, waitFor, withObservable, scheduler) { var _this = _super.call(this, destination) || this; - _this.total = total; - _this.ring = new Array(); - _this.count = 0; + _this.absoluteTimeout = absoluteTimeout; + _this.waitFor = waitFor; + _this.withObservable = withObservable; + _this.scheduler = scheduler; + _this.scheduleTimeout(); return _this; } - TakeLastSubscriber.prototype._next = function (value) { - var ring = this.ring; - var total = this.total; - var count = this.count++; - if (ring.length < total) { - ring.push(value); + TimeoutWithSubscriber.dispatchTimeout = function (subscriber) { + var withObservable = subscriber.withObservable; + subscriber._unsubscribeAndRecycle(); + subscriber.add(Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_3__["innerSubscribe"])(withObservable, new _innerSubscribe__WEBPACK_IMPORTED_MODULE_3__["SimpleInnerSubscriber"](subscriber))); + }; + TimeoutWithSubscriber.prototype.scheduleTimeout = function () { + var action = this.action; + if (action) { + this.action = action.schedule(this, this.waitFor); } else { - var index = count % total; - ring[index] = value; + this.add(this.action = this.scheduler.schedule(TimeoutWithSubscriber.dispatchTimeout, this.waitFor, this)); } }; - TakeLastSubscriber.prototype._complete = function () { - var destination = this.destination; - var count = this.count; - if (count > 0) { - var total = this.count >= this.total ? this.total : this.count; - var ring = this.ring; - for (var i = 0; i < total; i++) { - var idx = (count++) % total; - destination.next(ring[idx]); - } + TimeoutWithSubscriber.prototype._next = function (value) { + if (!this.absoluteTimeout) { + this.scheduleTimeout(); } - destination.complete(); + _super.prototype._next.call(this, value); }; - return TakeLastSubscriber; -}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -//# sourceMappingURL=takeLast.js.map + TimeoutWithSubscriber.prototype._unsubscribe = function () { + this.action = undefined; + this.scheduler = null; + this.withObservable = null; + }; + return TimeoutWithSubscriber; +}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_3__["SimpleOuterSubscriber"])); +//# sourceMappingURL=timeoutWith.js.map /***/ }), -/* 459 */ +/* 512 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "mapTo", function() { return mapTo; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); -/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "timestamp", function() { return timestamp; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Timestamp", function() { return Timestamp; }); +/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(56); +/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(67); +/** PURE_IMPORTS_START _scheduler_async,_map PURE_IMPORTS_END */ -function mapTo(value) { - return function (source) { return source.lift(new MapToOperator(value)); }; +function timestamp(scheduler) { + if (scheduler === void 0) { + scheduler = _scheduler_async__WEBPACK_IMPORTED_MODULE_0__["async"]; + } + return Object(_map__WEBPACK_IMPORTED_MODULE_1__["map"])(function (value) { return new Timestamp(value, scheduler.now()); }); } -var MapToOperator = /*@__PURE__*/ (function () { - function MapToOperator(value) { +var Timestamp = /*@__PURE__*/ (function () { + function Timestamp(value, timestamp) { this.value = value; + this.timestamp = timestamp; } - MapToOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new MapToSubscriber(subscriber, this.value)); - }; - return MapToOperator; + return Timestamp; }()); -var MapToSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](MapToSubscriber, _super); - function MapToSubscriber(destination, value) { - var _this = _super.call(this, destination) || this; - _this.value = value; - return _this; + +//# sourceMappingURL=timestamp.js.map + + +/***/ }), +/* 513 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "toArray", function() { return toArray; }); +/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(468); +/** PURE_IMPORTS_START _reduce PURE_IMPORTS_END */ + +function toArrayReducer(arr, item, index) { + if (index === 0) { + return [item]; } - MapToSubscriber.prototype._next = function (x) { - this.destination.next(this.value); - }; - return MapToSubscriber; -}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -//# sourceMappingURL=mapTo.js.map + arr.push(item); + return arr; +} +function toArray() { + return Object(_reduce__WEBPACK_IMPORTED_MODULE_0__["reduce"])(toArrayReducer, []); +} +//# sourceMappingURL=toArray.js.map /***/ }), -/* 460 */ +/* 514 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "materialize", function() { return materialize; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "window", function() { return window; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); -/* harmony import */ var _Notification__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(43); -/** PURE_IMPORTS_START tslib,_Subscriber,_Notification PURE_IMPORTS_END */ +/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(28); +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(91); +/** PURE_IMPORTS_START tslib,_Subject,_innerSubscribe PURE_IMPORTS_END */ -function materialize() { - return function materializeOperatorFunction(source) { - return source.lift(new MaterializeOperator()); +function window(windowBoundaries) { + return function windowOperatorFunction(source) { + return source.lift(new WindowOperator(windowBoundaries)); }; } -var MaterializeOperator = /*@__PURE__*/ (function () { - function MaterializeOperator() { +var WindowOperator = /*@__PURE__*/ (function () { + function WindowOperator(windowBoundaries) { + this.windowBoundaries = windowBoundaries; } - MaterializeOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new MaterializeSubscriber(subscriber)); + WindowOperator.prototype.call = function (subscriber, source) { + var windowSubscriber = new WindowSubscriber(subscriber); + var sourceSubscription = source.subscribe(windowSubscriber); + if (!sourceSubscription.closed) { + windowSubscriber.add(Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_2__["innerSubscribe"])(this.windowBoundaries, new _innerSubscribe__WEBPACK_IMPORTED_MODULE_2__["SimpleInnerSubscriber"](windowSubscriber))); + } + return sourceSubscription; }; - return MaterializeOperator; + return WindowOperator; }()); -var MaterializeSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](MaterializeSubscriber, _super); - function MaterializeSubscriber(destination) { - return _super.call(this, destination) || this; +var WindowSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](WindowSubscriber, _super); + function WindowSubscriber(destination) { + var _this = _super.call(this, destination) || this; + _this.window = new _Subject__WEBPACK_IMPORTED_MODULE_1__["Subject"](); + destination.next(_this.window); + return _this; } - MaterializeSubscriber.prototype._next = function (value) { - this.destination.next(_Notification__WEBPACK_IMPORTED_MODULE_2__["Notification"].createNext(value)); + WindowSubscriber.prototype.notifyNext = function () { + this.openWindow(); }; - MaterializeSubscriber.prototype._error = function (err) { - var destination = this.destination; - destination.next(_Notification__WEBPACK_IMPORTED_MODULE_2__["Notification"].createError(err)); - destination.complete(); + WindowSubscriber.prototype.notifyError = function (error) { + this._error(error); }; - MaterializeSubscriber.prototype._complete = function () { + WindowSubscriber.prototype.notifyComplete = function () { + this._complete(); + }; + WindowSubscriber.prototype._next = function (value) { + this.window.next(value); + }; + WindowSubscriber.prototype._error = function (err) { + this.window.error(err); + this.destination.error(err); + }; + WindowSubscriber.prototype._complete = function () { + this.window.complete(); + this.destination.complete(); + }; + WindowSubscriber.prototype._unsubscribe = function () { + this.window = null; + }; + WindowSubscriber.prototype.openWindow = function () { + var prevWindow = this.window; + if (prevWindow) { + prevWindow.complete(); + } var destination = this.destination; - destination.next(_Notification__WEBPACK_IMPORTED_MODULE_2__["Notification"].createComplete()); - destination.complete(); + var newWindow = this.window = new _Subject__WEBPACK_IMPORTED_MODULE_1__["Subject"](); + destination.next(newWindow); }; - return MaterializeSubscriber; -}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -//# sourceMappingURL=materialize.js.map - - -/***/ }), -/* 461 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { - -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "max", function() { return max; }); -/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(462); -/** PURE_IMPORTS_START _reduce PURE_IMPORTS_END */ - -function max(comparer) { - var max = (typeof comparer === 'function') - ? function (x, y) { return comparer(x, y) > 0 ? x : y; } - : function (x, y) { return x > y ? x : y; }; - return Object(_reduce__WEBPACK_IMPORTED_MODULE_0__["reduce"])(max); -} -//# sourceMappingURL=max.js.map + return WindowSubscriber; +}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_2__["SimpleOuterSubscriber"])); +//# sourceMappingURL=window.js.map /***/ }), -/* 462 */ +/* 515 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "reduce", function() { return reduce; }); -/* harmony import */ var _scan__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(463); -/* harmony import */ var _takeLast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(458); -/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(435); -/* harmony import */ var _util_pipe__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(25); -/** PURE_IMPORTS_START _scan,_takeLast,_defaultIfEmpty,_util_pipe PURE_IMPORTS_END */ - +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "windowCount", function() { return windowCount; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); +/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(28); +/** PURE_IMPORTS_START tslib,_Subscriber,_Subject PURE_IMPORTS_END */ -function reduce(accumulator, seed) { - if (arguments.length >= 2) { - return function reduceOperatorFunctionWithSeed(source) { - return Object(_util_pipe__WEBPACK_IMPORTED_MODULE_3__["pipe"])(Object(_scan__WEBPACK_IMPORTED_MODULE_0__["scan"])(accumulator, seed), Object(_takeLast__WEBPACK_IMPORTED_MODULE_1__["takeLast"])(1), Object(_defaultIfEmpty__WEBPACK_IMPORTED_MODULE_2__["defaultIfEmpty"])(seed))(source); - }; +function windowCount(windowSize, startWindowEvery) { + if (startWindowEvery === void 0) { + startWindowEvery = 0; } - return function reduceOperatorFunction(source) { - return Object(_util_pipe__WEBPACK_IMPORTED_MODULE_3__["pipe"])(Object(_scan__WEBPACK_IMPORTED_MODULE_0__["scan"])(function (acc, value, index) { return accumulator(acc, value, index + 1); }), Object(_takeLast__WEBPACK_IMPORTED_MODULE_1__["takeLast"])(1))(source); + return function windowCountOperatorFunction(source) { + return source.lift(new WindowCountOperator(windowSize, startWindowEvery)); }; } -//# sourceMappingURL=reduce.js.map +var WindowCountOperator = /*@__PURE__*/ (function () { + function WindowCountOperator(windowSize, startWindowEvery) { + this.windowSize = windowSize; + this.startWindowEvery = startWindowEvery; + } + WindowCountOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new WindowCountSubscriber(subscriber, this.windowSize, this.startWindowEvery)); + }; + return WindowCountOperator; +}()); +var WindowCountSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](WindowCountSubscriber, _super); + function WindowCountSubscriber(destination, windowSize, startWindowEvery) { + var _this = _super.call(this, destination) || this; + _this.destination = destination; + _this.windowSize = windowSize; + _this.startWindowEvery = startWindowEvery; + _this.windows = [new _Subject__WEBPACK_IMPORTED_MODULE_2__["Subject"]()]; + _this.count = 0; + destination.next(_this.windows[0]); + return _this; + } + WindowCountSubscriber.prototype._next = function (value) { + var startWindowEvery = (this.startWindowEvery > 0) ? this.startWindowEvery : this.windowSize; + var destination = this.destination; + var windowSize = this.windowSize; + var windows = this.windows; + var len = windows.length; + for (var i = 0; i < len && !this.closed; i++) { + windows[i].next(value); + } + var c = this.count - windowSize + 1; + if (c >= 0 && c % startWindowEvery === 0 && !this.closed) { + windows.shift().complete(); + } + if (++this.count % startWindowEvery === 0 && !this.closed) { + var window_1 = new _Subject__WEBPACK_IMPORTED_MODULE_2__["Subject"](); + windows.push(window_1); + destination.next(window_1); + } + }; + WindowCountSubscriber.prototype._error = function (err) { + var windows = this.windows; + if (windows) { + while (windows.length > 0 && !this.closed) { + windows.shift().error(err); + } + } + this.destination.error(err); + }; + WindowCountSubscriber.prototype._complete = function () { + var windows = this.windows; + if (windows) { + while (windows.length > 0 && !this.closed) { + windows.shift().complete(); + } + } + this.destination.complete(); + }; + WindowCountSubscriber.prototype._unsubscribe = function () { + this.count = 0; + this.windows = null; + }; + return WindowCountSubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); +//# sourceMappingURL=windowCount.js.map /***/ }), -/* 463 */ +/* 516 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "scan", function() { return scan; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "windowTime", function() { return windowTime; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); -/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ +/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(28); +/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(56); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(12); +/* harmony import */ var _util_isNumeric__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(99); +/* harmony import */ var _util_isScheduler__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(46); +/** PURE_IMPORTS_START tslib,_Subject,_scheduler_async,_Subscriber,_util_isNumeric,_util_isScheduler PURE_IMPORTS_END */ -function scan(accumulator, seed) { - var hasSeed = false; - if (arguments.length >= 2) { - hasSeed = true; + + + + +function windowTime(windowTimeSpan) { + var scheduler = _scheduler_async__WEBPACK_IMPORTED_MODULE_2__["async"]; + var windowCreationInterval = null; + var maxWindowSize = Number.POSITIVE_INFINITY; + if (Object(_util_isScheduler__WEBPACK_IMPORTED_MODULE_5__["isScheduler"])(arguments[3])) { + scheduler = arguments[3]; } - return function scanOperatorFunction(source) { - return source.lift(new ScanOperator(accumulator, seed, hasSeed)); + if (Object(_util_isScheduler__WEBPACK_IMPORTED_MODULE_5__["isScheduler"])(arguments[2])) { + scheduler = arguments[2]; + } + else if (Object(_util_isNumeric__WEBPACK_IMPORTED_MODULE_4__["isNumeric"])(arguments[2])) { + maxWindowSize = Number(arguments[2]); + } + if (Object(_util_isScheduler__WEBPACK_IMPORTED_MODULE_5__["isScheduler"])(arguments[1])) { + scheduler = arguments[1]; + } + else if (Object(_util_isNumeric__WEBPACK_IMPORTED_MODULE_4__["isNumeric"])(arguments[1])) { + windowCreationInterval = Number(arguments[1]); + } + return function windowTimeOperatorFunction(source) { + return source.lift(new WindowTimeOperator(windowTimeSpan, windowCreationInterval, maxWindowSize, scheduler)); }; } -var ScanOperator = /*@__PURE__*/ (function () { - function ScanOperator(accumulator, seed, hasSeed) { - if (hasSeed === void 0) { - hasSeed = false; - } - this.accumulator = accumulator; - this.seed = seed; - this.hasSeed = hasSeed; +var WindowTimeOperator = /*@__PURE__*/ (function () { + function WindowTimeOperator(windowTimeSpan, windowCreationInterval, maxWindowSize, scheduler) { + this.windowTimeSpan = windowTimeSpan; + this.windowCreationInterval = windowCreationInterval; + this.maxWindowSize = maxWindowSize; + this.scheduler = scheduler; } - ScanOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new ScanSubscriber(subscriber, this.accumulator, this.seed, this.hasSeed)); + WindowTimeOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new WindowTimeSubscriber(subscriber, this.windowTimeSpan, this.windowCreationInterval, this.maxWindowSize, this.scheduler)); }; - return ScanOperator; + return WindowTimeOperator; }()); -var ScanSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](ScanSubscriber, _super); - function ScanSubscriber(destination, accumulator, _seed, hasSeed) { - var _this = _super.call(this, destination) || this; - _this.accumulator = accumulator; - _this._seed = _seed; - _this.hasSeed = hasSeed; - _this.index = 0; +var CountedSubject = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](CountedSubject, _super); + function CountedSubject() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this._numberOfNextedValues = 0; return _this; } - Object.defineProperty(ScanSubscriber.prototype, "seed", { + CountedSubject.prototype.next = function (value) { + this._numberOfNextedValues++; + _super.prototype.next.call(this, value); + }; + Object.defineProperty(CountedSubject.prototype, "numberOfNextedValues", { get: function () { - return this._seed; - }, - set: function (value) { - this.hasSeed = true; - this._seed = value; + return this._numberOfNextedValues; }, enumerable: true, configurable: true }); - ScanSubscriber.prototype._next = function (value) { - if (!this.hasSeed) { - this.seed = value; - this.destination.next(value); + return CountedSubject; +}(_Subject__WEBPACK_IMPORTED_MODULE_1__["Subject"])); +var WindowTimeSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](WindowTimeSubscriber, _super); + function WindowTimeSubscriber(destination, windowTimeSpan, windowCreationInterval, maxWindowSize, scheduler) { + var _this = _super.call(this, destination) || this; + _this.destination = destination; + _this.windowTimeSpan = windowTimeSpan; + _this.windowCreationInterval = windowCreationInterval; + _this.maxWindowSize = maxWindowSize; + _this.scheduler = scheduler; + _this.windows = []; + var window = _this.openWindow(); + if (windowCreationInterval !== null && windowCreationInterval >= 0) { + var closeState = { subscriber: _this, window: window, context: null }; + var creationState = { windowTimeSpan: windowTimeSpan, windowCreationInterval: windowCreationInterval, subscriber: _this, scheduler: scheduler }; + _this.add(scheduler.schedule(dispatchWindowClose, windowTimeSpan, closeState)); + _this.add(scheduler.schedule(dispatchWindowCreation, windowCreationInterval, creationState)); } else { - return this._tryNext(value); + var timeSpanOnlyState = { subscriber: _this, window: window, windowTimeSpan: windowTimeSpan }; + _this.add(scheduler.schedule(dispatchWindowTimeSpanOnly, windowTimeSpan, timeSpanOnlyState)); + } + return _this; + } + WindowTimeSubscriber.prototype._next = function (value) { + var windows = this.windows; + var len = windows.length; + for (var i = 0; i < len; i++) { + var window_1 = windows[i]; + if (!window_1.closed) { + window_1.next(value); + if (window_1.numberOfNextedValues >= this.maxWindowSize) { + this.closeWindow(window_1); + } + } } }; - ScanSubscriber.prototype._tryNext = function (value) { - var index = this.index++; - var result; - try { - result = this.accumulator(this.seed, value, index); + WindowTimeSubscriber.prototype._error = function (err) { + var windows = this.windows; + while (windows.length > 0) { + windows.shift().error(err); } - catch (err) { - this.destination.error(err); + this.destination.error(err); + }; + WindowTimeSubscriber.prototype._complete = function () { + var windows = this.windows; + while (windows.length > 0) { + var window_2 = windows.shift(); + if (!window_2.closed) { + window_2.complete(); + } } - this.seed = result; - this.destination.next(result); + this.destination.complete(); }; - return ScanSubscriber; -}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -//# sourceMappingURL=scan.js.map - - -/***/ }), -/* 464 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { - -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "merge", function() { return merge; }); -/* harmony import */ var _observable_merge__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(100); -/** PURE_IMPORTS_START _observable_merge PURE_IMPORTS_END */ - -function merge() { - var observables = []; - for (var _i = 0; _i < arguments.length; _i++) { - observables[_i] = arguments[_i]; + WindowTimeSubscriber.prototype.openWindow = function () { + var window = new CountedSubject(); + this.windows.push(window); + var destination = this.destination; + destination.next(window); + return window; + }; + WindowTimeSubscriber.prototype.closeWindow = function (window) { + window.complete(); + var windows = this.windows; + windows.splice(windows.indexOf(window), 1); + }; + return WindowTimeSubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_3__["Subscriber"])); +function dispatchWindowTimeSpanOnly(state) { + var subscriber = state.subscriber, windowTimeSpan = state.windowTimeSpan, window = state.window; + if (window) { + subscriber.closeWindow(window); } - return function (source) { return source.lift.call(_observable_merge__WEBPACK_IMPORTED_MODULE_0__["merge"].apply(void 0, [source].concat(observables))); }; + state.window = subscriber.openWindow(); + this.schedule(state, windowTimeSpan); } -//# sourceMappingURL=merge.js.map - - -/***/ }), -/* 465 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { - -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "mergeMapTo", function() { return mergeMapTo; }); -/* harmony import */ var _mergeMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(83); -/** PURE_IMPORTS_START _mergeMap PURE_IMPORTS_END */ - -function mergeMapTo(innerObservable, resultSelector, concurrent) { - if (concurrent === void 0) { - concurrent = Number.POSITIVE_INFINITY; - } - if (typeof resultSelector === 'function') { - return Object(_mergeMap__WEBPACK_IMPORTED_MODULE_0__["mergeMap"])(function () { return innerObservable; }, resultSelector, concurrent); - } - if (typeof resultSelector === 'number') { - concurrent = resultSelector; +function dispatchWindowCreation(state) { + var windowTimeSpan = state.windowTimeSpan, subscriber = state.subscriber, scheduler = state.scheduler, windowCreationInterval = state.windowCreationInterval; + var window = subscriber.openWindow(); + var action = this; + var context = { action: action, subscription: null }; + var timeSpanState = { subscriber: subscriber, window: window, context: context }; + context.subscription = scheduler.schedule(dispatchWindowClose, windowTimeSpan, timeSpanState); + action.add(context.subscription); + action.schedule(state, windowCreationInterval); +} +function dispatchWindowClose(state) { + var subscriber = state.subscriber, window = state.window, context = state.context; + if (context && context.action && context.subscription) { + context.action.remove(context.subscription); } - return Object(_mergeMap__WEBPACK_IMPORTED_MODULE_0__["mergeMap"])(function () { return innerObservable; }, concurrent); + subscriber.closeWindow(window); } -//# sourceMappingURL=mergeMapTo.js.map +//# sourceMappingURL=windowTime.js.map /***/ }), -/* 466 */ +/* 517 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "mergeScan", function() { return mergeScan; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MergeScanOperator", function() { return MergeScanOperator; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MergeScanSubscriber", function() { return MergeScanSubscriber; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "windowToggle", function() { return windowToggle; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(91); -/** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ +/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(28); +/* harmony import */ var _Subscription__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(18); +/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(70); +/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(71); +/** PURE_IMPORTS_START tslib,_Subject,_Subscription,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ -function mergeScan(accumulator, seed, concurrent) { - if (concurrent === void 0) { - concurrent = Number.POSITIVE_INFINITY; - } - return function (source) { return source.lift(new MergeScanOperator(accumulator, seed, concurrent)); }; + + + +function windowToggle(openings, closingSelector) { + return function (source) { return source.lift(new WindowToggleOperator(openings, closingSelector)); }; } -var MergeScanOperator = /*@__PURE__*/ (function () { - function MergeScanOperator(accumulator, seed, concurrent) { - this.accumulator = accumulator; - this.seed = seed; - this.concurrent = concurrent; +var WindowToggleOperator = /*@__PURE__*/ (function () { + function WindowToggleOperator(openings, closingSelector) { + this.openings = openings; + this.closingSelector = closingSelector; } - MergeScanOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new MergeScanSubscriber(subscriber, this.accumulator, this.seed, this.concurrent)); + WindowToggleOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new WindowToggleSubscriber(subscriber, this.openings, this.closingSelector)); }; - return MergeScanOperator; + return WindowToggleOperator; }()); - -var MergeScanSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](MergeScanSubscriber, _super); - function MergeScanSubscriber(destination, accumulator, acc, concurrent) { +var WindowToggleSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](WindowToggleSubscriber, _super); + function WindowToggleSubscriber(destination, openings, closingSelector) { var _this = _super.call(this, destination) || this; - _this.accumulator = accumulator; - _this.acc = acc; - _this.concurrent = concurrent; - _this.hasValue = false; - _this.hasCompleted = false; - _this.buffer = []; - _this.active = 0; - _this.index = 0; + _this.openings = openings; + _this.closingSelector = closingSelector; + _this.contexts = []; + _this.add(_this.openSubscription = Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__["subscribeToResult"])(_this, openings, openings)); return _this; } - MergeScanSubscriber.prototype._next = function (value) { - if (this.active < this.concurrent) { - var index = this.index++; - var destination = this.destination; - var ish = void 0; - try { - var accumulator = this.accumulator; - ish = accumulator(this.acc, value, index); - } - catch (e) { - return destination.error(e); + WindowToggleSubscriber.prototype._next = function (value) { + var contexts = this.contexts; + if (contexts) { + var len = contexts.length; + for (var i = 0; i < len; i++) { + contexts[i].window.next(value); } - this.active++; - this._innerSub(ish); - } - else { - this.buffer.push(value); } }; - MergeScanSubscriber.prototype._innerSub = function (ish) { - var innerSubscriber = new _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleInnerSubscriber"](this); - var destination = this.destination; - destination.add(innerSubscriber); - var innerSubscription = Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["innerSubscribe"])(ish, innerSubscriber); - if (innerSubscription !== innerSubscriber) { - destination.add(innerSubscription); + WindowToggleSubscriber.prototype._error = function (err) { + var contexts = this.contexts; + this.contexts = null; + if (contexts) { + var len = contexts.length; + var index = -1; + while (++index < len) { + var context_1 = contexts[index]; + context_1.window.error(err); + context_1.subscription.unsubscribe(); + } } + _super.prototype._error.call(this, err); }; - MergeScanSubscriber.prototype._complete = function () { - this.hasCompleted = true; - if (this.active === 0 && this.buffer.length === 0) { - if (this.hasValue === false) { - this.destination.next(this.acc); + WindowToggleSubscriber.prototype._complete = function () { + var contexts = this.contexts; + this.contexts = null; + if (contexts) { + var len = contexts.length; + var index = -1; + while (++index < len) { + var context_2 = contexts[index]; + context_2.window.complete(); + context_2.subscription.unsubscribe(); } - this.destination.complete(); } - this.unsubscribe(); - }; - MergeScanSubscriber.prototype.notifyNext = function (innerValue) { - var destination = this.destination; - this.acc = innerValue; - this.hasValue = true; - destination.next(innerValue); + _super.prototype._complete.call(this); }; - MergeScanSubscriber.prototype.notifyComplete = function () { - var buffer = this.buffer; - this.active--; - if (buffer.length > 0) { - this._next(buffer.shift()); - } - else if (this.active === 0 && this.hasCompleted) { - if (this.hasValue === false) { - this.destination.next(this.acc); + WindowToggleSubscriber.prototype._unsubscribe = function () { + var contexts = this.contexts; + this.contexts = null; + if (contexts) { + var len = contexts.length; + var index = -1; + while (++index < len) { + var context_3 = contexts[index]; + context_3.window.unsubscribe(); + context_3.subscription.unsubscribe(); } - this.destination.complete(); } }; - return MergeScanSubscriber; -}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleOuterSubscriber"])); - -//# sourceMappingURL=mergeScan.js.map - - -/***/ }), -/* 467 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { - -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "min", function() { return min; }); -/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(462); -/** PURE_IMPORTS_START _reduce PURE_IMPORTS_END */ - -function min(comparer) { - var min = (typeof comparer === 'function') - ? function (x, y) { return comparer(x, y) < 0 ? x : y; } - : function (x, y) { return x < y ? x : y; }; - return Object(_reduce__WEBPACK_IMPORTED_MODULE_0__["reduce"])(min); -} -//# sourceMappingURL=min.js.map - - -/***/ }), -/* 468 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { - -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "multicast", function() { return multicast; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MulticastOperator", function() { return MulticastOperator; }); -/* harmony import */ var _observable_ConnectableObservable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(27); -/** PURE_IMPORTS_START _observable_ConnectableObservable PURE_IMPORTS_END */ - -function multicast(subjectOrSubjectFactory, selector) { - return function multicastOperatorFunction(source) { - var subjectFactory; - if (typeof subjectOrSubjectFactory === 'function') { - subjectFactory = subjectOrSubjectFactory; + WindowToggleSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) { + if (outerValue === this.openings) { + var closingNotifier = void 0; + try { + var closingSelector = this.closingSelector; + closingNotifier = closingSelector(innerValue); + } + catch (e) { + return this.error(e); + } + var window_1 = new _Subject__WEBPACK_IMPORTED_MODULE_1__["Subject"](); + var subscription = new _Subscription__WEBPACK_IMPORTED_MODULE_2__["Subscription"](); + var context_4 = { window: window_1, subscription: subscription }; + this.contexts.push(context_4); + var innerSubscription = Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__["subscribeToResult"])(this, closingNotifier, context_4); + if (innerSubscription.closed) { + this.closeWindow(this.contexts.length - 1); + } + else { + innerSubscription.context = context_4; + subscription.add(innerSubscription); + } + this.destination.next(window_1); } else { - subjectFactory = function subjectFactory() { - return subjectOrSubjectFactory; - }; + this.closeWindow(this.contexts.indexOf(outerValue)); } - if (typeof selector === 'function') { - return source.lift(new MulticastOperator(subjectFactory, selector)); + }; + WindowToggleSubscriber.prototype.notifyError = function (err) { + this.error(err); + }; + WindowToggleSubscriber.prototype.notifyComplete = function (inner) { + if (inner !== this.openSubscription) { + this.closeWindow(this.contexts.indexOf(inner.context)); } - var connectable = Object.create(source, _observable_ConnectableObservable__WEBPACK_IMPORTED_MODULE_0__["connectableObservableDescriptor"]); - connectable.source = source; - connectable.subjectFactory = subjectFactory; - return connectable; }; -} -var MulticastOperator = /*@__PURE__*/ (function () { - function MulticastOperator(subjectFactory, selector) { - this.subjectFactory = subjectFactory; - this.selector = selector; - } - MulticastOperator.prototype.call = function (subscriber, source) { - var selector = this.selector; - var subject = this.subjectFactory(); - var subscription = selector(subject).subscribe(subscriber); - subscription.add(source.subscribe(subject)); - return subscription; + WindowToggleSubscriber.prototype.closeWindow = function (index) { + if (index === -1) { + return; + } + var contexts = this.contexts; + var context = contexts[index]; + var window = context.window, subscription = context.subscription; + contexts.splice(index, 1); + window.complete(); + subscription.unsubscribe(); }; - return MulticastOperator; -}()); - -//# sourceMappingURL=multicast.js.map + return WindowToggleSubscriber; +}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__["OuterSubscriber"])); +//# sourceMappingURL=windowToggle.js.map /***/ }), -/* 469 */ +/* 518 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "onErrorResumeNext", function() { return onErrorResumeNext; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "onErrorResumeNextStatic", function() { return onErrorResumeNextStatic; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "windowWhen", function() { return windowWhen; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _observable_from__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(84); -/* harmony import */ var _util_isArray__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(19); -/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(91); -/** PURE_IMPORTS_START tslib,_observable_from,_util_isArray,_innerSubscribe PURE_IMPORTS_END */ +/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(28); +/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(70); +/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(71); +/** PURE_IMPORTS_START tslib,_Subject,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ -function onErrorResumeNext() { - var nextSources = []; - for (var _i = 0; _i < arguments.length; _i++) { - nextSources[_i] = arguments[_i]; - } - if (nextSources.length === 1 && Object(_util_isArray__WEBPACK_IMPORTED_MODULE_2__["isArray"])(nextSources[0])) { - nextSources = nextSources[0]; - } - return function (source) { return source.lift(new OnErrorResumeNextOperator(nextSources)); }; -} -function onErrorResumeNextStatic() { - var nextSources = []; - for (var _i = 0; _i < arguments.length; _i++) { - nextSources[_i] = arguments[_i]; - } - var source = undefined; - if (nextSources.length === 1 && Object(_util_isArray__WEBPACK_IMPORTED_MODULE_2__["isArray"])(nextSources[0])) { - nextSources = nextSources[0]; - } - source = nextSources.shift(); - return Object(_observable_from__WEBPACK_IMPORTED_MODULE_1__["from"])(source).lift(new OnErrorResumeNextOperator(nextSources)); +function windowWhen(closingSelector) { + return function windowWhenOperatorFunction(source) { + return source.lift(new WindowOperator(closingSelector)); + }; } -var OnErrorResumeNextOperator = /*@__PURE__*/ (function () { - function OnErrorResumeNextOperator(nextSources) { - this.nextSources = nextSources; +var WindowOperator = /*@__PURE__*/ (function () { + function WindowOperator(closingSelector) { + this.closingSelector = closingSelector; } - OnErrorResumeNextOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new OnErrorResumeNextSubscriber(subscriber, this.nextSources)); + WindowOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new WindowSubscriber(subscriber, this.closingSelector)); }; - return OnErrorResumeNextOperator; + return WindowOperator; }()); -var OnErrorResumeNextSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](OnErrorResumeNextSubscriber, _super); - function OnErrorResumeNextSubscriber(destination, nextSources) { +var WindowSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](WindowSubscriber, _super); + function WindowSubscriber(destination, closingSelector) { var _this = _super.call(this, destination) || this; _this.destination = destination; - _this.nextSources = nextSources; + _this.closingSelector = closingSelector; + _this.openWindow(); return _this; } - OnErrorResumeNextSubscriber.prototype.notifyError = function () { - this.subscribeToNextSource(); + WindowSubscriber.prototype.notifyNext = function (_outerValue, _innerValue, _outerIndex, _innerIndex, innerSub) { + this.openWindow(innerSub); }; - OnErrorResumeNextSubscriber.prototype.notifyComplete = function () { - this.subscribeToNextSource(); + WindowSubscriber.prototype.notifyError = function (error) { + this._error(error); }; - OnErrorResumeNextSubscriber.prototype._error = function (err) { - this.subscribeToNextSource(); - this.unsubscribe(); + WindowSubscriber.prototype.notifyComplete = function (innerSub) { + this.openWindow(innerSub); }; - OnErrorResumeNextSubscriber.prototype._complete = function () { - this.subscribeToNextSource(); - this.unsubscribe(); + WindowSubscriber.prototype._next = function (value) { + this.window.next(value); }; - OnErrorResumeNextSubscriber.prototype.subscribeToNextSource = function () { - var next = this.nextSources.shift(); - if (!!next) { - var innerSubscriber = new _innerSubscribe__WEBPACK_IMPORTED_MODULE_3__["SimpleInnerSubscriber"](this); - var destination = this.destination; - destination.add(innerSubscriber); - var innerSubscription = Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_3__["innerSubscribe"])(next, innerSubscriber); - if (innerSubscription !== innerSubscriber) { - destination.add(innerSubscription); - } + WindowSubscriber.prototype._error = function (err) { + this.window.error(err); + this.destination.error(err); + this.unsubscribeClosingNotification(); + }; + WindowSubscriber.prototype._complete = function () { + this.window.complete(); + this.destination.complete(); + this.unsubscribeClosingNotification(); + }; + WindowSubscriber.prototype.unsubscribeClosingNotification = function () { + if (this.closingNotification) { + this.closingNotification.unsubscribe(); + } + }; + WindowSubscriber.prototype.openWindow = function (innerSub) { + if (innerSub === void 0) { + innerSub = null; + } + if (innerSub) { + this.remove(innerSub); + innerSub.unsubscribe(); } - else { - this.destination.complete(); + var prevWindow = this.window; + if (prevWindow) { + prevWindow.complete(); + } + var window = this.window = new _Subject__WEBPACK_IMPORTED_MODULE_1__["Subject"](); + this.destination.next(window); + var closingNotifier; + try { + var closingSelector = this.closingSelector; + closingNotifier = closingSelector(); + } + catch (e) { + this.destination.error(e); + this.window.error(e); + return; } + this.add(this.closingNotification = Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__["subscribeToResult"])(this, closingNotifier)); }; - return OnErrorResumeNextSubscriber; -}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_3__["SimpleOuterSubscriber"])); -//# sourceMappingURL=onErrorResumeNext.js.map + return WindowSubscriber; +}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_2__["OuterSubscriber"])); +//# sourceMappingURL=windowWhen.js.map /***/ }), -/* 470 */ +/* 519 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "pairwise", function() { return pairwise; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "withLatestFrom", function() { return withLatestFrom; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); -/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ +/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(70); +/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(71); +/** PURE_IMPORTS_START tslib,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ -function pairwise() { - return function (source) { return source.lift(new PairwiseOperator()); }; + +function withLatestFrom() { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + return function (source) { + var project; + if (typeof args[args.length - 1] === 'function') { + project = args.pop(); + } + var observables = args; + return source.lift(new WithLatestFromOperator(observables, project)); + }; } -var PairwiseOperator = /*@__PURE__*/ (function () { - function PairwiseOperator() { +var WithLatestFromOperator = /*@__PURE__*/ (function () { + function WithLatestFromOperator(observables, project) { + this.observables = observables; + this.project = project; } - PairwiseOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new PairwiseSubscriber(subscriber)); + WithLatestFromOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new WithLatestFromSubscriber(subscriber, this.observables, this.project)); }; - return PairwiseOperator; + return WithLatestFromOperator; }()); -var PairwiseSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](PairwiseSubscriber, _super); - function PairwiseSubscriber(destination) { +var WithLatestFromSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](WithLatestFromSubscriber, _super); + function WithLatestFromSubscriber(destination, observables, project) { var _this = _super.call(this, destination) || this; - _this.hasPrev = false; + _this.observables = observables; + _this.project = project; + _this.toRespond = []; + var len = observables.length; + _this.values = new Array(len); + for (var i = 0; i < len; i++) { + _this.toRespond.push(i); + } + for (var i = 0; i < len; i++) { + var observable = observables[i]; + _this.add(Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__["subscribeToResult"])(_this, observable, undefined, i)); + } return _this; } - PairwiseSubscriber.prototype._next = function (value) { - var pair; - if (this.hasPrev) { - pair = [this.prev, value]; + WithLatestFromSubscriber.prototype.notifyNext = function (_outerValue, innerValue, outerIndex) { + this.values[outerIndex] = innerValue; + var toRespond = this.toRespond; + if (toRespond.length > 0) { + var found = toRespond.indexOf(outerIndex); + if (found !== -1) { + toRespond.splice(found, 1); + } } - else { - this.hasPrev = true; + }; + WithLatestFromSubscriber.prototype.notifyComplete = function () { + }; + WithLatestFromSubscriber.prototype._next = function (value) { + if (this.toRespond.length === 0) { + var args = [value].concat(this.values); + if (this.project) { + this._tryProject(args); + } + else { + this.destination.next(args); + } } - this.prev = value; - if (pair) { - this.destination.next(pair); + }; + WithLatestFromSubscriber.prototype._tryProject = function (args) { + var result; + try { + result = this.project.apply(this, args); + } + catch (err) { + this.destination.error(err); + return; } + this.destination.next(result); }; - return PairwiseSubscriber; -}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -//# sourceMappingURL=pairwise.js.map + return WithLatestFromSubscriber; +}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__["OuterSubscriber"])); +//# sourceMappingURL=withLatestFrom.js.map /***/ }), -/* 471 */ +/* 520 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "partition", function() { return partition; }); -/* harmony import */ var _util_not__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(105); -/* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(106); -/** PURE_IMPORTS_START _util_not,_filter PURE_IMPORTS_END */ - +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "zip", function() { return zip; }); +/* harmony import */ var _observable_zip__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(111); +/** PURE_IMPORTS_START _observable_zip PURE_IMPORTS_END */ -function partition(predicate, thisArg) { - return function (source) { - return [ - Object(_filter__WEBPACK_IMPORTED_MODULE_1__["filter"])(predicate, thisArg)(source), - Object(_filter__WEBPACK_IMPORTED_MODULE_1__["filter"])(Object(_util_not__WEBPACK_IMPORTED_MODULE_0__["not"])(predicate, thisArg))(source) - ]; +function zip() { + var observables = []; + for (var _i = 0; _i < arguments.length; _i++) { + observables[_i] = arguments[_i]; + } + return function zipOperatorFunction(source) { + return source.lift.call(_observable_zip__WEBPACK_IMPORTED_MODULE_0__["zip"].apply(void 0, [source].concat(observables))); }; } -//# sourceMappingURL=partition.js.map +//# sourceMappingURL=zip.js.map /***/ }), -/* 472 */ +/* 521 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "pluck", function() { return pluck; }); -/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(67); -/** PURE_IMPORTS_START _map PURE_IMPORTS_END */ +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "zipAll", function() { return zipAll; }); +/* harmony import */ var _observable_zip__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(111); +/** PURE_IMPORTS_START _observable_zip PURE_IMPORTS_END */ -function pluck() { - var properties = []; - for (var _i = 0; _i < arguments.length; _i++) { - properties[_i] = arguments[_i]; - } - var length = properties.length; - if (length === 0) { - throw new Error('list of properties cannot be empty.'); - } - return function (source) { return Object(_map__WEBPACK_IMPORTED_MODULE_0__["map"])(plucker(properties, length))(source); }; -} -function plucker(props, length) { - var mapper = function (x) { - var currentProp = x; - for (var i = 0; i < length; i++) { - var p = currentProp != null ? currentProp[props[i]] : undefined; - if (p !== void 0) { - currentProp = p; - } - else { - return undefined; - } - } - return currentProp; - }; - return mapper; +function zipAll(project) { + return function (source) { return source.lift(new _observable_zip__WEBPACK_IMPORTED_MODULE_0__["ZipOperator"](project)); }; } -//# sourceMappingURL=pluck.js.map +//# sourceMappingURL=zipAll.js.map /***/ }), -/* 473 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* 522 */ +/***/ (function(module, exports, __webpack_require__) { "use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "publish", function() { return publish; }); -/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(28); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(468); -/** PURE_IMPORTS_START _Subject,_multicast PURE_IMPORTS_END */ - -function publish(selector) { - return selector ? - Object(_multicast__WEBPACK_IMPORTED_MODULE_1__["multicast"])(function () { return new _Subject__WEBPACK_IMPORTED_MODULE_0__["Subject"](); }, selector) : - Object(_multicast__WEBPACK_IMPORTED_MODULE_1__["multicast"])(new _Subject__WEBPACK_IMPORTED_MODULE_0__["Subject"]()); -} -//# sourceMappingURL=publish.js.map +Object.defineProperty(exports, "__esModule", { + value: true +}); -/***/ }), -/* 474 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { - -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "publishBehavior", function() { return publishBehavior; }); -/* harmony import */ var _BehaviorSubject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(33); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(468); -/** PURE_IMPORTS_START _BehaviorSubject,_multicast PURE_IMPORTS_END */ +var _observe_lines = __webpack_require__(523); +Object.keys(_observe_lines).forEach(function (key) { + if (key === "default" || key === "__esModule") return; + if (key in exports && exports[key] === _observe_lines[key]) return; + Object.defineProperty(exports, key, { + enumerable: true, + get: function () { + return _observe_lines[key]; + } + }); +}); -function publishBehavior(value) { - return function (source) { return Object(_multicast__WEBPACK_IMPORTED_MODULE_1__["multicast"])(new _BehaviorSubject__WEBPACK_IMPORTED_MODULE_0__["BehaviorSubject"](value))(source); }; -} -//# sourceMappingURL=publishBehavior.js.map +var _observe_readable = __webpack_require__(524); +Object.keys(_observe_readable).forEach(function (key) { + if (key === "default" || key === "__esModule") return; + if (key in exports && exports[key] === _observe_readable[key]) return; + Object.defineProperty(exports, key, { + enumerable: true, + get: function () { + return _observe_readable[key]; + } + }); +}); /***/ }), -/* 475 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* 523 */ +/***/ (function(module, exports, __webpack_require__) { "use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "publishLast", function() { return publishLast; }); -/* harmony import */ var _AsyncSubject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(51); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(468); -/** PURE_IMPORTS_START _AsyncSubject,_multicast PURE_IMPORTS_END */ -function publishLast() { - return function (source) { return Object(_multicast__WEBPACK_IMPORTED_MODULE_1__["multicast"])(new _AsyncSubject__WEBPACK_IMPORTED_MODULE_0__["AsyncSubject"]())(source); }; -} -//# sourceMappingURL=publishLast.js.map +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.observeLines = observeLines; +var Rx = _interopRequireWildcard(__webpack_require__(9)); -/***/ }), -/* 476 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +var _operators = __webpack_require__(424); -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "publishReplay", function() { return publishReplay; }); -/* harmony import */ var _ReplaySubject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(34); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(468); -/** PURE_IMPORTS_START _ReplaySubject,_multicast PURE_IMPORTS_END */ +var _observe_readable = __webpack_require__(524); +function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } -function publishReplay(bufferSize, windowTime, selectorOrScheduler, scheduler) { - if (selectorOrScheduler && typeof selectorOrScheduler !== 'function') { - scheduler = selectorOrScheduler; - } - var selector = typeof selectorOrScheduler === 'function' ? selectorOrScheduler : undefined; - var subject = new _ReplaySubject__WEBPACK_IMPORTED_MODULE_0__["ReplaySubject"](bufferSize, windowTime, scheduler); - return function (source) { return Object(_multicast__WEBPACK_IMPORTED_MODULE_1__["multicast"])(function () { return subject; }, selector)(source); }; -} -//# sourceMappingURL=publishReplay.js.map +function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +const SEP = /\r?\n/; -/***/ }), -/* 477 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/** + * Creates an Observable from a Readable Stream that: + * - splits data from `readable` into lines + * - completes when `readable` emits "end" + * - fails if `readable` emits "errors" + * + * @param {ReadableStream} readable + * @return {Rx.Observable} + */ +function observeLines(readable) { + const done$ = (0, _observe_readable.observeReadable)(readable).pipe((0, _operators.share)()); + const scan$ = Rx.fromEvent(readable, 'data').pipe((0, _operators.scan)(({ + buffer + }, chunk) => { + buffer += chunk; + const lines = []; -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "race", function() { return race; }); -/* harmony import */ var _util_isArray__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(19); -/* harmony import */ var _observable_race__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(107); -/** PURE_IMPORTS_START _util_isArray,_observable_race PURE_IMPORTS_END */ + while (true) { + const match = buffer.match(SEP); + if (!match || match.index === undefined) { + break; + } -function race() { - var observables = []; - for (var _i = 0; _i < arguments.length; _i++) { - observables[_i] = arguments[_i]; + lines.push(buffer.slice(0, match.index)); + buffer = buffer.slice(match.index + match[0].length); } - return function raceOperatorFunction(source) { - if (observables.length === 1 && Object(_util_isArray__WEBPACK_IMPORTED_MODULE_0__["isArray"])(observables[0])) { - observables = observables[0]; - } - return source.lift.call(_observable_race__WEBPACK_IMPORTED_MODULE_1__["race"].apply(void 0, [source].concat(observables))); + + return { + buffer, + lines }; + }, { + buffer: '' + }), // stop if done completes or errors + (0, _operators.takeUntil)(done$.pipe((0, _operators.materialize)())), (0, _operators.share)()); + return Rx.merge( // use done$ to provide completion/errors + done$, // merge in the "lines" from each step + scan$.pipe((0, _operators.mergeMap)(({ + lines + }) => lines || [])), // inject the "unsplit" data at the end + scan$.pipe((0, _operators.last)(), (0, _operators.mergeMap)(({ + buffer + }) => buffer ? [buffer] : []), // if there were no lines, last() will error, so catch and complete + (0, _operators.catchError)(() => Rx.empty()))); } -//# sourceMappingURL=race.js.map - /***/ }), -/* 478 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* 524 */ +/***/ (function(module, exports, __webpack_require__) { "use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "repeat", function() { return repeat; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); -/* harmony import */ var _observable_empty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(44); -/** PURE_IMPORTS_START tslib,_Subscriber,_observable_empty PURE_IMPORTS_END */ +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.observeReadable = observeReadable; -function repeat(count) { - if (count === void 0) { - count = -1; - } - return function (source) { - if (count === 0) { - return Object(_observable_empty__WEBPACK_IMPORTED_MODULE_2__["empty"])(); - } - else if (count < 0) { - return source.lift(new RepeatOperator(-1, source)); - } - else { - return source.lift(new RepeatOperator(count - 1, source)); - } - }; -} -var RepeatOperator = /*@__PURE__*/ (function () { - function RepeatOperator(count, source) { - this.count = count; - this.source = source; - } - RepeatOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new RepeatSubscriber(subscriber, this.count, this.source)); - }; - return RepeatOperator; -}()); -var RepeatSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](RepeatSubscriber, _super); - function RepeatSubscriber(destination, count, source) { - var _this = _super.call(this, destination) || this; - _this.count = count; - _this.source = source; - return _this; - } - RepeatSubscriber.prototype.complete = function () { - if (!this.isStopped) { - var _a = this, source = _a.source, count = _a.count; - if (count === 0) { - return _super.prototype.complete.call(this); - } - else if (count > -1) { - this.count = count - 1; - } - source.subscribe(this._unsubscribeAndRecycle()); - } - }; - return RepeatSubscriber; -}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -//# sourceMappingURL=repeat.js.map - - -/***/ }), -/* 479 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +var Rx = _interopRequireWildcard(__webpack_require__(9)); -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "repeatWhen", function() { return repeatWhen; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(28); -/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(91); -/** PURE_IMPORTS_START tslib,_Subject,_innerSubscribe PURE_IMPORTS_END */ +var _operators = __webpack_require__(424); +function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } +function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } -function repeatWhen(notifier) { - return function (source) { return source.lift(new RepeatWhenOperator(notifier)); }; -} -var RepeatWhenOperator = /*@__PURE__*/ (function () { - function RepeatWhenOperator(notifier) { - this.notifier = notifier; - } - RepeatWhenOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new RepeatWhenSubscriber(subscriber, this.notifier, source)); - }; - return RepeatWhenOperator; -}()); -var RepeatWhenSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](RepeatWhenSubscriber, _super); - function RepeatWhenSubscriber(destination, notifier, source) { - var _this = _super.call(this, destination) || this; - _this.notifier = notifier; - _this.source = source; - _this.sourceIsBeingSubscribedTo = true; - return _this; - } - RepeatWhenSubscriber.prototype.notifyNext = function () { - this.sourceIsBeingSubscribedTo = true; - this.source.subscribe(this); - }; - RepeatWhenSubscriber.prototype.notifyComplete = function () { - if (this.sourceIsBeingSubscribedTo === false) { - return _super.prototype.complete.call(this); - } - }; - RepeatWhenSubscriber.prototype.complete = function () { - this.sourceIsBeingSubscribedTo = false; - if (!this.isStopped) { - if (!this.retries) { - this.subscribeToRetries(); - } - if (!this.retriesSubscription || this.retriesSubscription.closed) { - return _super.prototype.complete.call(this); - } - this._unsubscribeAndRecycle(); - this.notifications.next(undefined); - } - }; - RepeatWhenSubscriber.prototype._unsubscribe = function () { - var _a = this, notifications = _a.notifications, retriesSubscription = _a.retriesSubscription; - if (notifications) { - notifications.unsubscribe(); - this.notifications = undefined; - } - if (retriesSubscription) { - retriesSubscription.unsubscribe(); - this.retriesSubscription = undefined; - } - this.retries = undefined; - }; - RepeatWhenSubscriber.prototype._unsubscribeAndRecycle = function () { - var _unsubscribe = this._unsubscribe; - this._unsubscribe = null; - _super.prototype._unsubscribeAndRecycle.call(this); - this._unsubscribe = _unsubscribe; - return this; - }; - RepeatWhenSubscriber.prototype.subscribeToRetries = function () { - this.notifications = new _Subject__WEBPACK_IMPORTED_MODULE_1__["Subject"](); - var retries; - try { - var notifier = this.notifier; - retries = notifier(this.notifications); - } - catch (e) { - return _super.prototype.complete.call(this); - } - this.retries = retries; - this.retriesSubscription = Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_2__["innerSubscribe"])(retries, new _innerSubscribe__WEBPACK_IMPORTED_MODULE_2__["SimpleInnerSubscriber"](this)); - }; - return RepeatWhenSubscriber; -}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_2__["SimpleOuterSubscriber"])); -//# sourceMappingURL=repeatWhen.js.map +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +/** + * Produces an Observable from a ReadableSteam that: + * - completes on the first "end" event + * - fails on the first "error" event + */ +function observeReadable(readable) { + return Rx.race(Rx.fromEvent(readable, 'end').pipe((0, _operators.first)(), (0, _operators.ignoreElements)()), Rx.fromEvent(readable, 'error').pipe((0, _operators.first)(), (0, _operators.mergeMap)(err => Rx.throwError(err)))); +} /***/ }), -/* 480 */ +/* 525 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "retry", function() { return retry; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); -/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BuildCommand", function() { return BuildCommand; }); +/* harmony import */ var _utils_bazel__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(419); +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +const BuildCommand = { + description: 'Runs a build in the Bazel built packages', + name: 'build', + reportTiming: { + group: 'scripts/kbn build', + id: 'total' + }, -function retry(count) { - if (count === void 0) { - count = -1; - } - return function (source) { return source.lift(new RetryOperator(count, source)); }; -} -var RetryOperator = /*@__PURE__*/ (function () { - function RetryOperator(count, source) { - this.count = count; - this.source = source; - } - RetryOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new RetrySubscriber(subscriber, this.count, this.source)); - }; - return RetryOperator; -}()); -var RetrySubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](RetrySubscriber, _super); - function RetrySubscriber(destination, count, source) { - var _this = _super.call(this, destination) || this; - _this.count = count; - _this.source = source; - return _this; - } - RetrySubscriber.prototype.error = function (err) { - if (!this.isStopped) { - var _a = this, source = _a.source, count = _a.count; - if (count === 0) { - return _super.prototype.error.call(this, err); - } - else if (count > -1) { - this.count = count - 1; - } - source.subscribe(this._unsubscribeAndRecycle()); - } - }; - return RetrySubscriber; -}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -//# sourceMappingURL=retry.js.map + async run(projects, projectGraph, { + options + }) { + const runOffline = (options === null || options === void 0 ? void 0 : options.offline) === true; // Call bazel with the target to build all available packages + + await Object(_utils_bazel__WEBPACK_IMPORTED_MODULE_0__["runBazel"])(['build', '//packages:build', '--show_result=1'], runOffline); + } +}; /***/ }), -/* 481 */ +/* 526 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "retryWhen", function() { return retryWhen; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(28); -/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(91); -/** PURE_IMPORTS_START tslib,_Subject,_innerSubscribe PURE_IMPORTS_END */ +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "CleanCommand", function() { return CleanCommand; }); +/* harmony import */ var dedent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2); +/* harmony import */ var dedent__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(dedent__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(240); +/* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(del__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var ora__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(527); +/* harmony import */ var ora__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(ora__WEBPACK_IMPORTED_MODULE_2__); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_3__); +/* harmony import */ var _utils_bazel__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(419); +/* harmony import */ var _utils_fs__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(231); +/* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(220); +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ -function retryWhen(notifier) { - return function (source) { return source.lift(new RetryWhenOperator(notifier, source)); }; -} -var RetryWhenOperator = /*@__PURE__*/ (function () { - function RetryWhenOperator(notifier, source) { - this.notifier = notifier; - this.source = source; - } - RetryWhenOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new RetryWhenSubscriber(subscriber, this.notifier, this.source)); - }; - return RetryWhenOperator; -}()); -var RetryWhenSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](RetryWhenSubscriber, _super); - function RetryWhenSubscriber(destination, notifier, source) { - var _this = _super.call(this, destination) || this; - _this.notifier = notifier; - _this.source = source; - return _this; - } - RetryWhenSubscriber.prototype.error = function (err) { - if (!this.isStopped) { - var errors = this.errors; - var retries = this.retries; - var retriesSubscription = this.retriesSubscription; - if (!retries) { - errors = new _Subject__WEBPACK_IMPORTED_MODULE_1__["Subject"](); - try { - var notifier = this.notifier; - retries = notifier(errors); - } - catch (e) { - return _super.prototype.error.call(this, e); - } - retriesSubscription = Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_2__["innerSubscribe"])(retries, new _innerSubscribe__WEBPACK_IMPORTED_MODULE_2__["SimpleInnerSubscriber"](this)); - } - else { - this.errors = undefined; - this.retriesSubscription = undefined; - } - this._unsubscribeAndRecycle(); - this.errors = errors; - this.retries = retries; - this.retriesSubscription = retriesSubscription; - errors.next(err); - } - }; - RetryWhenSubscriber.prototype._unsubscribe = function () { - var _a = this, errors = _a.errors, retriesSubscription = _a.retriesSubscription; - if (errors) { - errors.unsubscribe(); - this.errors = undefined; - } - if (retriesSubscription) { - retriesSubscription.unsubscribe(); - this.retriesSubscription = undefined; - } - this.retries = undefined; - }; - RetryWhenSubscriber.prototype.notifyNext = function () { - var _unsubscribe = this._unsubscribe; - this._unsubscribe = null; - this._unsubscribeAndRecycle(); - this._unsubscribe = _unsubscribe; - this.source.subscribe(this); - }; - return RetryWhenSubscriber; -}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_2__["SimpleOuterSubscriber"])); -//# sourceMappingURL=retryWhen.js.map -/***/ }), -/* 482 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "sample", function() { return sample; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(91); -/** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ +const CleanCommand = { + description: 'Deletes output directories, node_modules and resets internal caches.', + name: 'clean', + reportTiming: { + group: 'scripts/kbn clean', + id: 'total' + }, -function sample(notifier) { - return function (source) { return source.lift(new SampleOperator(notifier)); }; -} -var SampleOperator = /*@__PURE__*/ (function () { - function SampleOperator(notifier) { - this.notifier = notifier; - } - SampleOperator.prototype.call = function (subscriber, source) { - var sampleSubscriber = new SampleSubscriber(subscriber); - var subscription = source.subscribe(sampleSubscriber); - subscription.add(Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["innerSubscribe"])(this.notifier, new _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleInnerSubscriber"](sampleSubscriber))); - return subscription; - }; - return SampleOperator; -}()); -var SampleSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](SampleSubscriber, _super); - function SampleSubscriber() { - var _this = _super !== null && _super.apply(this, arguments) || this; - _this.hasValue = false; - return _this; - } - SampleSubscriber.prototype._next = function (value) { - this.value = value; - this.hasValue = true; - }; - SampleSubscriber.prototype.notifyNext = function () { - this.emitValue(); - }; - SampleSubscriber.prototype.notifyComplete = function () { - this.emitValue(); - }; - SampleSubscriber.prototype.emitValue = function () { - if (this.hasValue) { - this.hasValue = false; - this.destination.next(this.value); - } - }; - return SampleSubscriber; -}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleOuterSubscriber"])); -//# sourceMappingURL=sample.js.map + async run(projects) { + _utils_log__WEBPACK_IMPORTED_MODULE_6__["log"].warning(dedent__WEBPACK_IMPORTED_MODULE_0___default.a` + This command is only necessary for the rare circumstance where you need to recover a consistent + state when problems arise. If you need to run this command often, please let us know by + filling out this form: https://ela.st/yarn-kbn-clean + `); + const toDelete = []; + for (const project of projects.values()) { + if (await Object(_utils_fs__WEBPACK_IMPORTED_MODULE_5__["isDirectory"])(project.nodeModulesLocation)) { + toDelete.push({ + cwd: project.path, + pattern: Object(path__WEBPACK_IMPORTED_MODULE_3__["relative"])(project.path, project.nodeModulesLocation) + }); + } -/***/ }), -/* 483 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { + if (await Object(_utils_fs__WEBPACK_IMPORTED_MODULE_5__["isDirectory"])(project.targetLocation)) { + toDelete.push({ + cwd: project.path, + pattern: Object(path__WEBPACK_IMPORTED_MODULE_3__["relative"])(project.path, project.targetLocation) + }); + } -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "sampleTime", function() { return sampleTime; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); -/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(56); -/** PURE_IMPORTS_START tslib,_Subscriber,_scheduler_async PURE_IMPORTS_END */ + const { + extraPatterns + } = project.getCleanConfig(); + if (extraPatterns) { + toDelete.push({ + cwd: project.path, + pattern: extraPatterns + }); + } + } // Runs Bazel soft clean -function sampleTime(period, scheduler) { - if (scheduler === void 0) { - scheduler = _scheduler_async__WEBPACK_IMPORTED_MODULE_2__["async"]; - } - return function (source) { return source.lift(new SampleTimeOperator(period, scheduler)); }; -} -var SampleTimeOperator = /*@__PURE__*/ (function () { - function SampleTimeOperator(period, scheduler) { - this.period = period; - this.scheduler = scheduler; - } - SampleTimeOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new SampleTimeSubscriber(subscriber, this.period, this.scheduler)); - }; - return SampleTimeOperator; -}()); -var SampleTimeSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](SampleTimeSubscriber, _super); - function SampleTimeSubscriber(destination, period, scheduler) { - var _this = _super.call(this, destination) || this; - _this.period = period; - _this.scheduler = scheduler; - _this.hasValue = false; - _this.add(scheduler.schedule(dispatchNotification, period, { subscriber: _this, period: period })); - return _this; + if (await Object(_utils_bazel__WEBPACK_IMPORTED_MODULE_4__["isBazelBinAvailable"])()) { + await Object(_utils_bazel__WEBPACK_IMPORTED_MODULE_4__["runBazel"])(['clean']); + _utils_log__WEBPACK_IMPORTED_MODULE_6__["log"].success('Soft cleaned bazel'); } - SampleTimeSubscriber.prototype._next = function (value) { - this.lastValue = value; - this.hasValue = true; - }; - SampleTimeSubscriber.prototype.notifyNext = function () { - if (this.hasValue) { - this.hasValue = false; - this.destination.next(this.lastValue); + + if (toDelete.length === 0) { + _utils_log__WEBPACK_IMPORTED_MODULE_6__["log"].success('Nothing to delete'); + } else { + /** + * In order to avoid patterns like `/build` in packages from accidentally + * impacting files outside the package we use `process.chdir()` to change + * the cwd to the package and execute `del()` without the `force` option + * so it will check that each file being deleted is within the package. + * + * `del()` does support a `cwd` option, but it's only for resolving the + * patterns and does not impact the cwd check. + */ + const originalCwd = process.cwd(); + + try { + for (const { + pattern, + cwd + } of toDelete) { + process.chdir(cwd); + const promise = del__WEBPACK_IMPORTED_MODULE_1___default()(pattern); + + if (_utils_log__WEBPACK_IMPORTED_MODULE_6__["log"].wouldLogLevel('info')) { + ora__WEBPACK_IMPORTED_MODULE_2___default.a.promise(promise, Object(path__WEBPACK_IMPORTED_MODULE_3__["relative"])(originalCwd, Object(path__WEBPACK_IMPORTED_MODULE_3__["join"])(cwd, String(pattern)))); + } + + await promise; } - }; - return SampleTimeSubscriber; -}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -function dispatchNotification(state) { - var subscriber = state.subscriber, period = state.period; - subscriber.notifyNext(); - this.schedule(state, period); -} -//# sourceMappingURL=sampleTime.js.map + } finally { + process.chdir(originalCwd); + } + } + } +}; /***/ }), -/* 484 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* 527 */ +/***/ (function(module, exports, __webpack_require__) { "use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "sequenceEqual", function() { return sequenceEqual; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SequenceEqualOperator", function() { return SequenceEqualOperator; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SequenceEqualSubscriber", function() { return SequenceEqualSubscriber; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); -/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ +const readline = __webpack_require__(528); +const chalk = __webpack_require__(529); +const cliCursor = __webpack_require__(532); +const cliSpinners = __webpack_require__(534); +const logSymbols = __webpack_require__(536); +const stripAnsi = __webpack_require__(542); +const wcwidth = __webpack_require__(544); +const isInteractive = __webpack_require__(548); +const MuteStream = __webpack_require__(549); -function sequenceEqual(compareTo, comparator) { - return function (source) { return source.lift(new SequenceEqualOperator(compareTo, comparator)); }; -} -var SequenceEqualOperator = /*@__PURE__*/ (function () { - function SequenceEqualOperator(compareTo, comparator) { - this.compareTo = compareTo; - this.comparator = comparator; - } - SequenceEqualOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new SequenceEqualSubscriber(subscriber, this.compareTo, this.comparator)); - }; - return SequenceEqualOperator; -}()); +const TEXT = Symbol('text'); +const PREFIX_TEXT = Symbol('prefixText'); -var SequenceEqualSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](SequenceEqualSubscriber, _super); - function SequenceEqualSubscriber(destination, compareTo, comparator) { - var _this = _super.call(this, destination) || this; - _this.compareTo = compareTo; - _this.comparator = comparator; - _this._a = []; - _this._b = []; - _this._oneComplete = false; - _this.destination.add(compareTo.subscribe(new SequenceEqualCompareToSubscriber(destination, _this))); - return _this; - } - SequenceEqualSubscriber.prototype._next = function (value) { - if (this._oneComplete && this._b.length === 0) { - this.emit(false); - } - else { - this._a.push(value); - this.checkValues(); - } - }; - SequenceEqualSubscriber.prototype._complete = function () { - if (this._oneComplete) { - this.emit(this._a.length === 0 && this._b.length === 0); - } - else { - this._oneComplete = true; - } - this.unsubscribe(); - }; - SequenceEqualSubscriber.prototype.checkValues = function () { - var _c = this, _a = _c._a, _b = _c._b, comparator = _c.comparator; - while (_a.length > 0 && _b.length > 0) { - var a = _a.shift(); - var b = _b.shift(); - var areEqual = false; - try { - areEqual = comparator ? comparator(a, b) : a === b; - } - catch (e) { - this.destination.error(e); - } - if (!areEqual) { - this.emit(false); - } - } - }; - SequenceEqualSubscriber.prototype.emit = function (value) { - var destination = this.destination; - destination.next(value); - destination.complete(); - }; - SequenceEqualSubscriber.prototype.nextB = function (value) { - if (this._oneComplete && this._a.length === 0) { - this.emit(false); - } - else { - this._b.push(value); - this.checkValues(); - } - }; - SequenceEqualSubscriber.prototype.completeB = function () { - if (this._oneComplete) { - this.emit(this._a.length === 0 && this._b.length === 0); - } - else { - this._oneComplete = true; - } - }; - return SequenceEqualSubscriber; -}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); +const ASCII_ETX_CODE = 0x03; // Ctrl+C emits this code + +class StdinDiscarder { + constructor() { + this.requests = 0; + + this.mutedStream = new MuteStream(); + this.mutedStream.pipe(process.stdout); + this.mutedStream.mute(); + + const self = this; + this.ourEmit = function (event, data, ...args) { + const {stdin} = process; + if (self.requests > 0 || stdin.emit === self.ourEmit) { + if (event === 'keypress') { // Fixes readline behavior + return; + } + + if (event === 'data' && data.includes(ASCII_ETX_CODE)) { + process.emit('SIGINT'); + } + + Reflect.apply(self.oldEmit, this, [event, data, ...args]); + } else { + Reflect.apply(process.stdin.emit, this, [event, data, ...args]); + } + }; + } + + start() { + this.requests++; + + if (this.requests === 1) { + this.realStart(); + } + } + + stop() { + if (this.requests <= 0) { + throw new Error('`stop` called more times than `start`'); + } + + this.requests--; + + if (this.requests === 0) { + this.realStop(); + } + } + + realStart() { + // No known way to make it work reliably on Windows + if (process.platform === 'win32') { + return; + } + + this.rl = readline.createInterface({ + input: process.stdin, + output: this.mutedStream + }); + + this.rl.on('SIGINT', () => { + if (process.listenerCount('SIGINT') === 0) { + process.emit('SIGINT'); + } else { + this.rl.close(); + process.kill(process.pid, 'SIGINT'); + } + }); + } + + realStop() { + if (process.platform === 'win32') { + return; + } -var SequenceEqualCompareToSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](SequenceEqualCompareToSubscriber, _super); - function SequenceEqualCompareToSubscriber(destination, parent) { - var _this = _super.call(this, destination) || this; - _this.parent = parent; - return _this; - } - SequenceEqualCompareToSubscriber.prototype._next = function (value) { - this.parent.nextB(value); - }; - SequenceEqualCompareToSubscriber.prototype._error = function (err) { - this.parent.error(err); - this.unsubscribe(); - }; - SequenceEqualCompareToSubscriber.prototype._complete = function () { - this.parent.completeB(); - this.unsubscribe(); - }; - return SequenceEqualCompareToSubscriber; -}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -//# sourceMappingURL=sequenceEqual.js.map + this.rl.close(); + this.rl = undefined; + } +} +let stdinDiscarder; -/***/ }), -/* 485 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +class Ora { + constructor(options) { + if (!stdinDiscarder) { + stdinDiscarder = new StdinDiscarder(); + } -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "share", function() { return share; }); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(468); -/* harmony import */ var _refCount__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(31); -/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(28); -/** PURE_IMPORTS_START _multicast,_refCount,_Subject PURE_IMPORTS_END */ + if (typeof options === 'string') { + options = { + text: options + }; + } + this.options = { + text: '', + color: 'cyan', + stream: process.stderr, + discardStdin: true, + ...options + }; + this.spinner = this.options.spinner; -function shareSubjectFactory() { - return new _Subject__WEBPACK_IMPORTED_MODULE_2__["Subject"](); -} -function share() { - return function (source) { return Object(_refCount__WEBPACK_IMPORTED_MODULE_1__["refCount"])()(Object(_multicast__WEBPACK_IMPORTED_MODULE_0__["multicast"])(shareSubjectFactory)(source)); }; -} -//# sourceMappingURL=share.js.map + this.color = this.options.color; + this.hideCursor = this.options.hideCursor !== false; + this.interval = this.options.interval || this.spinner.interval || 100; + this.stream = this.options.stream; + this.id = undefined; + this.isEnabled = typeof this.options.isEnabled === 'boolean' ? this.options.isEnabled : isInteractive({stream: this.stream}); + // Set *after* `this.stream` + this.text = this.options.text; + this.prefixText = this.options.prefixText; + this.linesToClear = 0; + this.indent = this.options.indent; + this.discardStdin = this.options.discardStdin; + this.isDiscardingStdin = false; + } -/***/ }), -/* 486 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { + get indent() { + return this._indent; + } -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "shareReplay", function() { return shareReplay; }); -/* harmony import */ var _ReplaySubject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(34); -/** PURE_IMPORTS_START _ReplaySubject PURE_IMPORTS_END */ + set indent(indent = 0) { + if (!(indent >= 0 && Number.isInteger(indent))) { + throw new Error('The `indent` option must be an integer from 0 and up'); + } -function shareReplay(configOrBufferSize, windowTime, scheduler) { - var config; - if (configOrBufferSize && typeof configOrBufferSize === 'object') { - config = configOrBufferSize; - } - else { - config = { - bufferSize: configOrBufferSize, - windowTime: windowTime, - refCount: false, - scheduler: scheduler - }; - } - return function (source) { return source.lift(shareReplayOperator(config)); }; -} -function shareReplayOperator(_a) { - var _b = _a.bufferSize, bufferSize = _b === void 0 ? Number.POSITIVE_INFINITY : _b, _c = _a.windowTime, windowTime = _c === void 0 ? Number.POSITIVE_INFINITY : _c, useRefCount = _a.refCount, scheduler = _a.scheduler; - var subject; - var refCount = 0; - var subscription; - var hasError = false; - var isComplete = false; - return function shareReplayOperation(source) { - refCount++; - var innerSub; - if (!subject || hasError) { - hasError = false; - subject = new _ReplaySubject__WEBPACK_IMPORTED_MODULE_0__["ReplaySubject"](bufferSize, windowTime, scheduler); - innerSub = subject.subscribe(this); - subscription = source.subscribe({ - next: function (value) { subject.next(value); }, - error: function (err) { - hasError = true; - subject.error(err); - }, - complete: function () { - isComplete = true; - subscription = undefined; - subject.complete(); - }, - }); - } - else { - innerSub = subject.subscribe(this); - } - this.add(function () { - refCount--; - innerSub.unsubscribe(); - if (subscription && !isComplete && useRefCount && refCount === 0) { - subscription.unsubscribe(); - subscription = undefined; - subject = undefined; - } - }); - }; -} -//# sourceMappingURL=shareReplay.js.map + this._indent = indent; + } + _updateInterval(interval) { + if (interval !== undefined) { + this.interval = interval; + } + } -/***/ }), -/* 487 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { + get spinner() { + return this._spinner; + } -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "single", function() { return single; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); -/* harmony import */ var _util_EmptyError__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(64); -/** PURE_IMPORTS_START tslib,_Subscriber,_util_EmptyError PURE_IMPORTS_END */ + set spinner(spinner) { + this.frameIndex = 0; + if (typeof spinner === 'object') { + if (spinner.frames === undefined) { + throw new Error('The given spinner must have a `frames` property'); + } + this._spinner = spinner; + } else if (process.platform === 'win32') { + this._spinner = cliSpinners.line; + } else if (spinner === undefined) { + // Set default spinner + this._spinner = cliSpinners.dots; + } else if (cliSpinners[spinner]) { + this._spinner = cliSpinners[spinner]; + } else { + throw new Error(`There is no built-in spinner named '${spinner}'. See https://github.com/sindresorhus/cli-spinners/blob/master/spinners.json for a full list.`); + } -function single(predicate) { - return function (source) { return source.lift(new SingleOperator(predicate, source)); }; -} -var SingleOperator = /*@__PURE__*/ (function () { - function SingleOperator(predicate, source) { - this.predicate = predicate; - this.source = source; - } - SingleOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new SingleSubscriber(subscriber, this.predicate, this.source)); - }; - return SingleOperator; -}()); -var SingleSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](SingleSubscriber, _super); - function SingleSubscriber(destination, predicate, source) { - var _this = _super.call(this, destination) || this; - _this.predicate = predicate; - _this.source = source; - _this.seenValue = false; - _this.index = 0; - return _this; - } - SingleSubscriber.prototype.applySingleValue = function (value) { - if (this.seenValue) { - this.destination.error('Sequence contains more than one element'); - } - else { - this.seenValue = true; - this.singleValue = value; - } - }; - SingleSubscriber.prototype._next = function (value) { - var index = this.index++; - if (this.predicate) { - this.tryNext(value, index); - } - else { - this.applySingleValue(value); - } - }; - SingleSubscriber.prototype.tryNext = function (value, index) { - try { - if (this.predicate(value, index, this.source)) { - this.applySingleValue(value); - } - } - catch (err) { - this.destination.error(err); - } - }; - SingleSubscriber.prototype._complete = function () { - var destination = this.destination; - if (this.index > 0) { - destination.next(this.seenValue ? this.singleValue : undefined); - destination.complete(); - } - else { - destination.error(new _util_EmptyError__WEBPACK_IMPORTED_MODULE_2__["EmptyError"]); - } - }; - return SingleSubscriber; -}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -//# sourceMappingURL=single.js.map + this._updateInterval(this._spinner.interval); + } + get text() { + return this[TEXT]; + } -/***/ }), -/* 488 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { + get prefixText() { + return this[PREFIX_TEXT]; + } -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "skip", function() { return skip; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); -/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ + get isSpinning() { + return this.id !== undefined; + } + updateLineCount() { + const columns = this.stream.columns || 80; + const fullPrefixText = (typeof this[PREFIX_TEXT] === 'string') ? this[PREFIX_TEXT] + '-' : ''; + this.lineCount = stripAnsi(fullPrefixText + '--' + this[TEXT]).split('\n').reduce((count, line) => { + return count + Math.max(1, Math.ceil(wcwidth(line) / columns)); + }, 0); + } -function skip(count) { - return function (source) { return source.lift(new SkipOperator(count)); }; -} -var SkipOperator = /*@__PURE__*/ (function () { - function SkipOperator(total) { - this.total = total; - } - SkipOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new SkipSubscriber(subscriber, this.total)); - }; - return SkipOperator; -}()); -var SkipSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](SkipSubscriber, _super); - function SkipSubscriber(destination, total) { - var _this = _super.call(this, destination) || this; - _this.total = total; - _this.count = 0; - return _this; - } - SkipSubscriber.prototype._next = function (x) { - if (++this.count > this.total) { - this.destination.next(x); - } - }; - return SkipSubscriber; -}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -//# sourceMappingURL=skip.js.map + set text(value) { + this[TEXT] = value; + this.updateLineCount(); + } + set prefixText(value) { + this[PREFIX_TEXT] = value; + this.updateLineCount(); + } -/***/ }), -/* 489 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { + frame() { + const {frames} = this.spinner; + let frame = frames[this.frameIndex]; -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "skipLast", function() { return skipLast; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); -/* harmony import */ var _util_ArgumentOutOfRangeError__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(63); -/** PURE_IMPORTS_START tslib,_Subscriber,_util_ArgumentOutOfRangeError PURE_IMPORTS_END */ + if (this.color) { + frame = chalk[this.color](frame); + } + this.frameIndex = ++this.frameIndex % frames.length; + const fullPrefixText = (typeof this.prefixText === 'string' && this.prefixText !== '') ? this.prefixText + ' ' : ''; + const fullText = typeof this.text === 'string' ? ' ' + this.text : ''; + return fullPrefixText + frame + fullText; + } -function skipLast(count) { - return function (source) { return source.lift(new SkipLastOperator(count)); }; -} -var SkipLastOperator = /*@__PURE__*/ (function () { - function SkipLastOperator(_skipCount) { - this._skipCount = _skipCount; - if (this._skipCount < 0) { - throw new _util_ArgumentOutOfRangeError__WEBPACK_IMPORTED_MODULE_2__["ArgumentOutOfRangeError"]; - } - } - SkipLastOperator.prototype.call = function (subscriber, source) { - if (this._skipCount === 0) { - return source.subscribe(new _Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"](subscriber)); - } - else { - return source.subscribe(new SkipLastSubscriber(subscriber, this._skipCount)); - } - }; - return SkipLastOperator; -}()); -var SkipLastSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](SkipLastSubscriber, _super); - function SkipLastSubscriber(destination, _skipCount) { - var _this = _super.call(this, destination) || this; - _this._skipCount = _skipCount; - _this._count = 0; - _this._ring = new Array(_skipCount); - return _this; - } - SkipLastSubscriber.prototype._next = function (value) { - var skipCount = this._skipCount; - var count = this._count++; - if (count < skipCount) { - this._ring[count] = value; - } - else { - var currentIndex = count % skipCount; - var ring = this._ring; - var oldValue = ring[currentIndex]; - ring[currentIndex] = value; - this.destination.next(oldValue); - } - }; - return SkipLastSubscriber; -}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -//# sourceMappingURL=skipLast.js.map + clear() { + if (!this.isEnabled || !this.stream.isTTY) { + return this; + } + for (let i = 0; i < this.linesToClear; i++) { + if (i > 0) { + this.stream.moveCursor(0, -1); + } -/***/ }), -/* 490 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { + this.stream.clearLine(); + this.stream.cursorTo(this.indent); + } -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "skipUntil", function() { return skipUntil; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(91); -/** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ + this.linesToClear = 0; + return this; + } -function skipUntil(notifier) { - return function (source) { return source.lift(new SkipUntilOperator(notifier)); }; -} -var SkipUntilOperator = /*@__PURE__*/ (function () { - function SkipUntilOperator(notifier) { - this.notifier = notifier; - } - SkipUntilOperator.prototype.call = function (destination, source) { - return source.subscribe(new SkipUntilSubscriber(destination, this.notifier)); - }; - return SkipUntilOperator; -}()); -var SkipUntilSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](SkipUntilSubscriber, _super); - function SkipUntilSubscriber(destination, notifier) { - var _this = _super.call(this, destination) || this; - _this.hasValue = false; - var innerSubscriber = new _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleInnerSubscriber"](_this); - _this.add(innerSubscriber); - _this.innerSubscription = innerSubscriber; - var innerSubscription = Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["innerSubscribe"])(notifier, innerSubscriber); - if (innerSubscription !== innerSubscriber) { - _this.add(innerSubscription); - _this.innerSubscription = innerSubscription; - } - return _this; - } - SkipUntilSubscriber.prototype._next = function (value) { - if (this.hasValue) { - _super.prototype._next.call(this, value); - } - }; - SkipUntilSubscriber.prototype.notifyNext = function () { - this.hasValue = true; - if (this.innerSubscription) { - this.innerSubscription.unsubscribe(); - } - }; - SkipUntilSubscriber.prototype.notifyComplete = function () { - }; - return SkipUntilSubscriber; -}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleOuterSubscriber"])); -//# sourceMappingURL=skipUntil.js.map + render() { + this.clear(); + this.stream.write(this.frame()); + this.linesToClear = this.lineCount; + return this; + } -/***/ }), -/* 491 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { + start(text) { + if (text) { + this.text = text; + } -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "skipWhile", function() { return skipWhile; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); -/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ + if (!this.isEnabled) { + if (this.text) { + this.stream.write(`- ${this.text}\n`); + } + return this; + } -function skipWhile(predicate) { - return function (source) { return source.lift(new SkipWhileOperator(predicate)); }; -} -var SkipWhileOperator = /*@__PURE__*/ (function () { - function SkipWhileOperator(predicate) { - this.predicate = predicate; - } - SkipWhileOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new SkipWhileSubscriber(subscriber, this.predicate)); - }; - return SkipWhileOperator; -}()); -var SkipWhileSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](SkipWhileSubscriber, _super); - function SkipWhileSubscriber(destination, predicate) { - var _this = _super.call(this, destination) || this; - _this.predicate = predicate; - _this.skipping = true; - _this.index = 0; - return _this; - } - SkipWhileSubscriber.prototype._next = function (value) { - var destination = this.destination; - if (this.skipping) { - this.tryCallPredicate(value); - } - if (!this.skipping) { - destination.next(value); - } - }; - SkipWhileSubscriber.prototype.tryCallPredicate = function (value) { - try { - var result = this.predicate(value, this.index++); - this.skipping = Boolean(result); - } - catch (err) { - this.destination.error(err); - } - }; - return SkipWhileSubscriber; -}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -//# sourceMappingURL=skipWhile.js.map + if (this.isSpinning) { + return this; + } + if (this.hideCursor) { + cliCursor.hide(this.stream); + } -/***/ }), -/* 492 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { + if (this.discardStdin && process.stdin.isTTY) { + this.isDiscardingStdin = true; + stdinDiscarder.start(); + } -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "startWith", function() { return startWith; }); -/* harmony import */ var _observable_concat__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(80); -/* harmony import */ var _util_isScheduler__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(46); -/** PURE_IMPORTS_START _observable_concat,_util_isScheduler PURE_IMPORTS_END */ + this.render(); + this.id = setInterval(this.render.bind(this), this.interval); + return this; + } -function startWith() { - var array = []; - for (var _i = 0; _i < arguments.length; _i++) { - array[_i] = arguments[_i]; - } - var scheduler = array[array.length - 1]; - if (Object(_util_isScheduler__WEBPACK_IMPORTED_MODULE_1__["isScheduler"])(scheduler)) { - array.pop(); - return function (source) { return Object(_observable_concat__WEBPACK_IMPORTED_MODULE_0__["concat"])(array, source, scheduler); }; - } - else { - return function (source) { return Object(_observable_concat__WEBPACK_IMPORTED_MODULE_0__["concat"])(array, source); }; - } -} -//# sourceMappingURL=startWith.js.map + stop() { + if (!this.isEnabled) { + return this; + } + clearInterval(this.id); + this.id = undefined; + this.frameIndex = 0; + this.clear(); + if (this.hideCursor) { + cliCursor.show(this.stream); + } -/***/ }), -/* 493 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { + if (this.discardStdin && process.stdin.isTTY && this.isDiscardingStdin) { + stdinDiscarder.stop(); + this.isDiscardingStdin = false; + } -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "subscribeOn", function() { return subscribeOn; }); -/* harmony import */ var _observable_SubscribeOnObservable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(494); -/** PURE_IMPORTS_START _observable_SubscribeOnObservable PURE_IMPORTS_END */ + return this; + } -function subscribeOn(scheduler, delay) { - if (delay === void 0) { - delay = 0; - } - return function subscribeOnOperatorFunction(source) { - return source.lift(new SubscribeOnOperator(scheduler, delay)); - }; -} -var SubscribeOnOperator = /*@__PURE__*/ (function () { - function SubscribeOnOperator(scheduler, delay) { - this.scheduler = scheduler; - this.delay = delay; - } - SubscribeOnOperator.prototype.call = function (subscriber, source) { - return new _observable_SubscribeOnObservable__WEBPACK_IMPORTED_MODULE_0__["SubscribeOnObservable"](source, this.delay, this.scheduler).subscribe(subscriber); - }; - return SubscribeOnOperator; -}()); -//# sourceMappingURL=subscribeOn.js.map + succeed(text) { + return this.stopAndPersist({symbol: logSymbols.success, text}); + } + fail(text) { + return this.stopAndPersist({symbol: logSymbols.error, text}); + } -/***/ }), -/* 494 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { + warn(text) { + return this.stopAndPersist({symbol: logSymbols.warning, text}); + } -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SubscribeOnObservable", function() { return SubscribeOnObservable; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(10); -/* harmony import */ var _scheduler_asap__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(52); -/* harmony import */ var _util_isNumeric__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(99); -/** PURE_IMPORTS_START tslib,_Observable,_scheduler_asap,_util_isNumeric PURE_IMPORTS_END */ + info(text) { + return this.stopAndPersist({symbol: logSymbols.info, text}); + } + stopAndPersist(options = {}) { + const prefixText = options.prefixText || this.prefixText; + const fullPrefixText = (typeof prefixText === 'string' && prefixText !== '') ? prefixText + ' ' : ''; + const text = options.text || this.text; + const fullText = (typeof text === 'string') ? ' ' + text : ''; + this.stop(); + this.stream.write(`${fullPrefixText}${options.symbol || ' '}${fullText}\n`); + return this; + } +} -var SubscribeOnObservable = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](SubscribeOnObservable, _super); - function SubscribeOnObservable(source, delayTime, scheduler) { - if (delayTime === void 0) { - delayTime = 0; - } - if (scheduler === void 0) { - scheduler = _scheduler_asap__WEBPACK_IMPORTED_MODULE_2__["asap"]; - } - var _this = _super.call(this) || this; - _this.source = source; - _this.delayTime = delayTime; - _this.scheduler = scheduler; - if (!Object(_util_isNumeric__WEBPACK_IMPORTED_MODULE_3__["isNumeric"])(delayTime) || delayTime < 0) { - _this.delayTime = 0; - } - if (!scheduler || typeof scheduler.schedule !== 'function') { - _this.scheduler = _scheduler_asap__WEBPACK_IMPORTED_MODULE_2__["asap"]; - } - return _this; - } - SubscribeOnObservable.create = function (source, delay, scheduler) { - if (delay === void 0) { - delay = 0; - } - if (scheduler === void 0) { - scheduler = _scheduler_asap__WEBPACK_IMPORTED_MODULE_2__["asap"]; - } - return new SubscribeOnObservable(source, delay, scheduler); - }; - SubscribeOnObservable.dispatch = function (arg) { - var source = arg.source, subscriber = arg.subscriber; - return this.add(source.subscribe(subscriber)); - }; - SubscribeOnObservable.prototype._subscribe = function (subscriber) { - var delay = this.delayTime; - var source = this.source; - var scheduler = this.scheduler; - return scheduler.schedule(SubscribeOnObservable.dispatch, delay, { - source: source, subscriber: subscriber - }); - }; - return SubscribeOnObservable; -}(_Observable__WEBPACK_IMPORTED_MODULE_1__["Observable"])); +const oraFactory = function (options) { + return new Ora(options); +}; -//# sourceMappingURL=SubscribeOnObservable.js.map +module.exports = oraFactory; +module.exports.promise = (action, options) => { + // eslint-disable-next-line promise/prefer-await-to-then + if (typeof action.then !== 'function') { + throw new TypeError('Parameter `action` must be a Promise'); + } -/***/ }), -/* 495 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { + const spinner = new Ora(options); + spinner.start(); -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "switchAll", function() { return switchAll; }); -/* harmony import */ var _switchMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(496); -/* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(26); -/** PURE_IMPORTS_START _switchMap,_util_identity PURE_IMPORTS_END */ + (async () => { + try { + await action; + spinner.succeed(); + } catch (_) { + spinner.fail(); + } + })(); + return spinner; +}; -function switchAll() { - return Object(_switchMap__WEBPACK_IMPORTED_MODULE_0__["switchMap"])(_util_identity__WEBPACK_IMPORTED_MODULE_1__["identity"]); -} -//# sourceMappingURL=switchAll.js.map +/***/ }), +/* 528 */ +/***/ (function(module, exports) { + +module.exports = require("readline"); /***/ }), -/* 496 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* 529 */ +/***/ (function(module, exports, __webpack_require__) { "use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "switchMap", function() { return switchMap; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(67); -/* harmony import */ var _observable_from__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(84); -/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(91); -/** PURE_IMPORTS_START tslib,_map,_observable_from,_innerSubscribe PURE_IMPORTS_END */ +const ansiStyles = __webpack_require__(115); +const {stdout: stdoutColor, stderr: stderrColor} = __webpack_require__(121); +const { + stringReplaceAll, + stringEncaseCRLFWithFirstIndex +} = __webpack_require__(530); +// `supportsColor.level` → `ansiStyles.color[name]` mapping +const levelMapping = [ + 'ansi', + 'ansi', + 'ansi256', + 'ansi16m' +]; +const styles = Object.create(null); -function switchMap(project, resultSelector) { - if (typeof resultSelector === 'function') { - return function (source) { return source.pipe(switchMap(function (a, i) { return Object(_observable_from__WEBPACK_IMPORTED_MODULE_2__["from"])(project(a, i)).pipe(Object(_map__WEBPACK_IMPORTED_MODULE_1__["map"])(function (b, ii) { return resultSelector(a, b, i, ii); })); })); }; - } - return function (source) { return source.lift(new SwitchMapOperator(project)); }; -} -var SwitchMapOperator = /*@__PURE__*/ (function () { - function SwitchMapOperator(project) { - this.project = project; - } - SwitchMapOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new SwitchMapSubscriber(subscriber, this.project)); - }; - return SwitchMapOperator; -}()); -var SwitchMapSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](SwitchMapSubscriber, _super); - function SwitchMapSubscriber(destination, project) { - var _this = _super.call(this, destination) || this; - _this.project = project; - _this.index = 0; - return _this; - } - SwitchMapSubscriber.prototype._next = function (value) { - var result; - var index = this.index++; - try { - result = this.project(value, index); - } - catch (error) { - this.destination.error(error); - return; - } - this._innerSub(result); - }; - SwitchMapSubscriber.prototype._innerSub = function (result) { - var innerSubscription = this.innerSubscription; - if (innerSubscription) { - innerSubscription.unsubscribe(); - } - var innerSubscriber = new _innerSubscribe__WEBPACK_IMPORTED_MODULE_3__["SimpleInnerSubscriber"](this); - var destination = this.destination; - destination.add(innerSubscriber); - this.innerSubscription = Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_3__["innerSubscribe"])(result, innerSubscriber); - if (this.innerSubscription !== innerSubscriber) { - destination.add(this.innerSubscription); - } - }; - SwitchMapSubscriber.prototype._complete = function () { - var innerSubscription = this.innerSubscription; - if (!innerSubscription || innerSubscription.closed) { - _super.prototype._complete.call(this); - } - this.unsubscribe(); - }; - SwitchMapSubscriber.prototype._unsubscribe = function () { - this.innerSubscription = undefined; - }; - SwitchMapSubscriber.prototype.notifyComplete = function () { - this.innerSubscription = undefined; - if (this.isStopped) { - _super.prototype._complete.call(this); - } - }; - SwitchMapSubscriber.prototype.notifyNext = function (innerValue) { - this.destination.next(innerValue); - }; - return SwitchMapSubscriber; -}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_3__["SimpleOuterSubscriber"])); -//# sourceMappingURL=switchMap.js.map +const applyOptions = (object, options = {}) => { + if (options.level > 3 || options.level < 0) { + throw new Error('The `level` option should be an integer from 0 to 3'); + } + // Detect level if not set manually + const colorLevel = stdoutColor ? stdoutColor.level : 0; + object.level = options.level === undefined ? colorLevel : options.level; +}; -/***/ }), -/* 497 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +class ChalkClass { + constructor(options) { + return chalkFactory(options); + } +} -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "switchMapTo", function() { return switchMapTo; }); -/* harmony import */ var _switchMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(496); -/** PURE_IMPORTS_START _switchMap PURE_IMPORTS_END */ +const chalkFactory = options => { + const chalk = {}; + applyOptions(chalk, options); -function switchMapTo(innerObservable, resultSelector) { - return resultSelector ? Object(_switchMap__WEBPACK_IMPORTED_MODULE_0__["switchMap"])(function () { return innerObservable; }, resultSelector) : Object(_switchMap__WEBPACK_IMPORTED_MODULE_0__["switchMap"])(function () { return innerObservable; }); -} -//# sourceMappingURL=switchMapTo.js.map + chalk.template = (...arguments_) => chalkTag(chalk.template, ...arguments_); + Object.setPrototypeOf(chalk, Chalk.prototype); + Object.setPrototypeOf(chalk.template, chalk); -/***/ }), -/* 498 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { + chalk.template.constructor = () => { + throw new Error('`chalk.constructor()` is deprecated. Use `new chalk.Instance()` instead.'); + }; -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "takeUntil", function() { return takeUntil; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(91); -/** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ + chalk.template.Instance = ChalkClass; + return chalk.template; +}; -function takeUntil(notifier) { - return function (source) { return source.lift(new TakeUntilOperator(notifier)); }; +function Chalk(options) { + return chalkFactory(options); } -var TakeUntilOperator = /*@__PURE__*/ (function () { - function TakeUntilOperator(notifier) { - this.notifier = notifier; - } - TakeUntilOperator.prototype.call = function (subscriber, source) { - var takeUntilSubscriber = new TakeUntilSubscriber(subscriber); - var notifierSubscription = Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["innerSubscribe"])(this.notifier, new _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleInnerSubscriber"](takeUntilSubscriber)); - if (notifierSubscription && !takeUntilSubscriber.seenValue) { - takeUntilSubscriber.add(notifierSubscription); - return source.subscribe(takeUntilSubscriber); - } - return takeUntilSubscriber; - }; - return TakeUntilOperator; -}()); -var TakeUntilSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](TakeUntilSubscriber, _super); - function TakeUntilSubscriber(destination) { - var _this = _super.call(this, destination) || this; - _this.seenValue = false; - return _this; - } - TakeUntilSubscriber.prototype.notifyNext = function () { - this.seenValue = true; - this.complete(); - }; - TakeUntilSubscriber.prototype.notifyComplete = function () { - }; - return TakeUntilSubscriber; -}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleOuterSubscriber"])); -//# sourceMappingURL=takeUntil.js.map +for (const [styleName, style] of Object.entries(ansiStyles)) { + styles[styleName] = { + get() { + const builder = createBuilder(this, createStyler(style.open, style.close, this._styler), this._isEmpty); + Object.defineProperty(this, styleName, {value: builder}); + return builder; + } + }; +} -/***/ }), -/* 499 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +styles.visible = { + get() { + const builder = createBuilder(this, this._styler, true); + Object.defineProperty(this, 'visible', {value: builder}); + return builder; + } +}; -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "takeWhile", function() { return takeWhile; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); -/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ +const usedModels = ['rgb', 'hex', 'keyword', 'hsl', 'hsv', 'hwb', 'ansi', 'ansi256']; +for (const model of usedModels) { + styles[model] = { + get() { + const {level} = this; + return function (...arguments_) { + const styler = createStyler(ansiStyles.color[levelMapping[level]][model](...arguments_), ansiStyles.color.close, this._styler); + return createBuilder(this, styler, this._isEmpty); + }; + } + }; +} -function takeWhile(predicate, inclusive) { - if (inclusive === void 0) { - inclusive = false; - } - return function (source) { - return source.lift(new TakeWhileOperator(predicate, inclusive)); - }; +for (const model of usedModels) { + const bgModel = 'bg' + model[0].toUpperCase() + model.slice(1); + styles[bgModel] = { + get() { + const {level} = this; + return function (...arguments_) { + const styler = createStyler(ansiStyles.bgColor[levelMapping[level]][model](...arguments_), ansiStyles.bgColor.close, this._styler); + return createBuilder(this, styler, this._isEmpty); + }; + } + }; } -var TakeWhileOperator = /*@__PURE__*/ (function () { - function TakeWhileOperator(predicate, inclusive) { - this.predicate = predicate; - this.inclusive = inclusive; - } - TakeWhileOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new TakeWhileSubscriber(subscriber, this.predicate, this.inclusive)); - }; - return TakeWhileOperator; -}()); -var TakeWhileSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](TakeWhileSubscriber, _super); - function TakeWhileSubscriber(destination, predicate, inclusive) { - var _this = _super.call(this, destination) || this; - _this.predicate = predicate; - _this.inclusive = inclusive; - _this.index = 0; - return _this; - } - TakeWhileSubscriber.prototype._next = function (value) { - var destination = this.destination; - var result; - try { - result = this.predicate(value, this.index++); - } - catch (err) { - destination.error(err); - return; - } - this.nextOrComplete(value, result); - }; - TakeWhileSubscriber.prototype.nextOrComplete = function (value, predicateResult) { - var destination = this.destination; - if (Boolean(predicateResult)) { - destination.next(value); - } - else { - if (this.inclusive) { - destination.next(value); - } - destination.complete(); - } - }; - return TakeWhileSubscriber; -}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -//# sourceMappingURL=takeWhile.js.map +const proto = Object.defineProperties(() => {}, { + ...styles, + level: { + enumerable: true, + get() { + return this._generator.level; + }, + set(level) { + this._generator.level = level; + } + } +}); + +const createStyler = (open, close, parent) => { + let openAll; + let closeAll; + if (parent === undefined) { + openAll = open; + closeAll = close; + } else { + openAll = parent.openAll + open; + closeAll = close + parent.closeAll; + } + + return { + open, + close, + openAll, + closeAll, + parent + }; +}; + +const createBuilder = (self, _styler, _isEmpty) => { + const builder = (...arguments_) => { + // Single argument is hot path, implicit coercion is faster than anything + // eslint-disable-next-line no-implicit-coercion + return applyStyle(builder, (arguments_.length === 1) ? ('' + arguments_[0]) : arguments_.join(' ')); + }; + + // `__proto__` is used because we must return a function, but there is + // no way to create a function with a different prototype + builder.__proto__ = proto; // eslint-disable-line no-proto -/***/ }), -/* 500 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { + builder._generator = self; + builder._styler = _styler; + builder._isEmpty = _isEmpty; -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "tap", function() { return tap; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); -/* harmony import */ var _util_noop__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(61); -/* harmony import */ var _util_isFunction__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(14); -/** PURE_IMPORTS_START tslib,_Subscriber,_util_noop,_util_isFunction PURE_IMPORTS_END */ + return builder; +}; +const applyStyle = (self, string) => { + if (self.level <= 0 || !string) { + return self._isEmpty ? '' : string; + } + let styler = self._styler; + if (styler === undefined) { + return string; + } -function tap(nextOrObserver, error, complete) { - return function tapOperatorFunction(source) { - return source.lift(new DoOperator(nextOrObserver, error, complete)); - }; -} -var DoOperator = /*@__PURE__*/ (function () { - function DoOperator(nextOrObserver, error, complete) { - this.nextOrObserver = nextOrObserver; - this.error = error; - this.complete = complete; - } - DoOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new TapSubscriber(subscriber, this.nextOrObserver, this.error, this.complete)); - }; - return DoOperator; -}()); -var TapSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](TapSubscriber, _super); - function TapSubscriber(destination, observerOrNext, error, complete) { - var _this = _super.call(this, destination) || this; - _this._tapNext = _util_noop__WEBPACK_IMPORTED_MODULE_2__["noop"]; - _this._tapError = _util_noop__WEBPACK_IMPORTED_MODULE_2__["noop"]; - _this._tapComplete = _util_noop__WEBPACK_IMPORTED_MODULE_2__["noop"]; - _this._tapError = error || _util_noop__WEBPACK_IMPORTED_MODULE_2__["noop"]; - _this._tapComplete = complete || _util_noop__WEBPACK_IMPORTED_MODULE_2__["noop"]; - if (Object(_util_isFunction__WEBPACK_IMPORTED_MODULE_3__["isFunction"])(observerOrNext)) { - _this._context = _this; - _this._tapNext = observerOrNext; - } - else if (observerOrNext) { - _this._context = observerOrNext; - _this._tapNext = observerOrNext.next || _util_noop__WEBPACK_IMPORTED_MODULE_2__["noop"]; - _this._tapError = observerOrNext.error || _util_noop__WEBPACK_IMPORTED_MODULE_2__["noop"]; - _this._tapComplete = observerOrNext.complete || _util_noop__WEBPACK_IMPORTED_MODULE_2__["noop"]; - } - return _this; - } - TapSubscriber.prototype._next = function (value) { - try { - this._tapNext.call(this._context, value); - } - catch (err) { - this.destination.error(err); - return; - } - this.destination.next(value); - }; - TapSubscriber.prototype._error = function (err) { - try { - this._tapError.call(this._context, err); - } - catch (err) { - this.destination.error(err); - return; - } - this.destination.error(err); - }; - TapSubscriber.prototype._complete = function () { - try { - this._tapComplete.call(this._context); - } - catch (err) { - this.destination.error(err); - return; - } - return this.destination.complete(); - }; - return TapSubscriber; -}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -//# sourceMappingURL=tap.js.map + const {openAll, closeAll} = styler; + if (string.indexOf('\u001B') !== -1) { + while (styler !== undefined) { + // Replace any instances already present with a re-opening code + // otherwise only the part of the string until said closing code + // will be colored, and the rest will simply be 'plain'. + string = stringReplaceAll(string, styler.close, styler.open); + styler = styler.parent; + } + } -/***/ }), -/* 501 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { + // We can move both next actions out of loop, because remaining actions in loop won't have + // any/visible effect on parts we add here. Close the styling before a linebreak and reopen + // after next line to fix a bleed issue on macOS: https://github.com/chalk/chalk/pull/92 + const lfIndex = string.indexOf('\n'); + if (lfIndex !== -1) { + string = stringEncaseCRLFWithFirstIndex(string, closeAll, openAll, lfIndex); + } -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "defaultThrottleConfig", function() { return defaultThrottleConfig; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "throttle", function() { return throttle; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(91); -/** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ + return openAll + string + closeAll; +}; + +let template; +const chalkTag = (chalk, ...strings) => { + const [firstString] = strings; + if (!Array.isArray(firstString)) { + // If chalk() was called by itself or with a string, + // return the string itself as a string. + return strings.join(' '); + } -var defaultThrottleConfig = { - leading: true, - trailing: false + const arguments_ = strings.slice(1); + const parts = [firstString.raw[0]]; + + for (let i = 1; i < firstString.length; i++) { + parts.push( + String(arguments_[i - 1]).replace(/[{}\\]/g, '\\$&'), + String(firstString.raw[i]) + ); + } + + if (template === undefined) { + template = __webpack_require__(531); + } + + return template(chalk, parts.join('')); }; -function throttle(durationSelector, config) { - if (config === void 0) { - config = defaultThrottleConfig; - } - return function (source) { return source.lift(new ThrottleOperator(durationSelector, !!config.leading, !!config.trailing)); }; -} -var ThrottleOperator = /*@__PURE__*/ (function () { - function ThrottleOperator(durationSelector, leading, trailing) { - this.durationSelector = durationSelector; - this.leading = leading; - this.trailing = trailing; - } - ThrottleOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new ThrottleSubscriber(subscriber, this.durationSelector, this.leading, this.trailing)); - }; - return ThrottleOperator; -}()); -var ThrottleSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](ThrottleSubscriber, _super); - function ThrottleSubscriber(destination, durationSelector, _leading, _trailing) { - var _this = _super.call(this, destination) || this; - _this.destination = destination; - _this.durationSelector = durationSelector; - _this._leading = _leading; - _this._trailing = _trailing; - _this._hasValue = false; - return _this; - } - ThrottleSubscriber.prototype._next = function (value) { - this._hasValue = true; - this._sendValue = value; - if (!this._throttled) { - if (this._leading) { - this.send(); - } - else { - this.throttle(value); - } - } - }; - ThrottleSubscriber.prototype.send = function () { - var _a = this, _hasValue = _a._hasValue, _sendValue = _a._sendValue; - if (_hasValue) { - this.destination.next(_sendValue); - this.throttle(_sendValue); - } - this._hasValue = false; - this._sendValue = undefined; - }; - ThrottleSubscriber.prototype.throttle = function (value) { - var duration = this.tryDurationSelector(value); - if (!!duration) { - this.add(this._throttled = Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["innerSubscribe"])(duration, new _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleInnerSubscriber"](this))); - } - }; - ThrottleSubscriber.prototype.tryDurationSelector = function (value) { - try { - return this.durationSelector(value); - } - catch (err) { - this.destination.error(err); - return null; - } - }; - ThrottleSubscriber.prototype.throttlingDone = function () { - var _a = this, _throttled = _a._throttled, _trailing = _a._trailing; - if (_throttled) { - _throttled.unsubscribe(); - } - this._throttled = undefined; - if (_trailing) { - this.send(); - } - }; - ThrottleSubscriber.prototype.notifyNext = function () { - this.throttlingDone(); - }; - ThrottleSubscriber.prototype.notifyComplete = function () { - this.throttlingDone(); - }; - return ThrottleSubscriber; -}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleOuterSubscriber"])); -//# sourceMappingURL=throttle.js.map + +Object.defineProperties(Chalk.prototype, styles); + +const chalk = Chalk(); // eslint-disable-line new-cap +chalk.supportsColor = stdoutColor; +chalk.stderr = Chalk({level: stderrColor ? stderrColor.level : 0}); // eslint-disable-line new-cap +chalk.stderr.supportsColor = stderrColor; + +// For TypeScript +chalk.Level = { + None: 0, + Basic: 1, + Ansi256: 2, + TrueColor: 3, + 0: 'None', + 1: 'Basic', + 2: 'Ansi256', + 3: 'TrueColor' +}; + +module.exports = chalk; /***/ }), -/* 502 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* 530 */ +/***/ (function(module, exports, __webpack_require__) { "use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "throttleTime", function() { return throttleTime; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); -/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(56); -/* harmony import */ var _throttle__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(501); -/** PURE_IMPORTS_START tslib,_Subscriber,_scheduler_async,_throttle PURE_IMPORTS_END */ +const stringReplaceAll = (string, substring, replacer) => { + let index = string.indexOf(substring); + if (index === -1) { + return string; + } + const substringLength = substring.length; + let endIndex = 0; + let returnValue = ''; + do { + returnValue += string.substr(endIndex, index - endIndex) + substring + replacer; + endIndex = index + substringLength; + index = string.indexOf(substring, endIndex); + } while (index !== -1); -function throttleTime(duration, scheduler, config) { - if (scheduler === void 0) { - scheduler = _scheduler_async__WEBPACK_IMPORTED_MODULE_2__["async"]; - } - if (config === void 0) { - config = _throttle__WEBPACK_IMPORTED_MODULE_3__["defaultThrottleConfig"]; - } - return function (source) { return source.lift(new ThrottleTimeOperator(duration, scheduler, config.leading, config.trailing)); }; -} -var ThrottleTimeOperator = /*@__PURE__*/ (function () { - function ThrottleTimeOperator(duration, scheduler, leading, trailing) { - this.duration = duration; - this.scheduler = scheduler; - this.leading = leading; - this.trailing = trailing; - } - ThrottleTimeOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new ThrottleTimeSubscriber(subscriber, this.duration, this.scheduler, this.leading, this.trailing)); - }; - return ThrottleTimeOperator; -}()); -var ThrottleTimeSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](ThrottleTimeSubscriber, _super); - function ThrottleTimeSubscriber(destination, duration, scheduler, leading, trailing) { - var _this = _super.call(this, destination) || this; - _this.duration = duration; - _this.scheduler = scheduler; - _this.leading = leading; - _this.trailing = trailing; - _this._hasTrailingValue = false; - _this._trailingValue = null; - return _this; - } - ThrottleTimeSubscriber.prototype._next = function (value) { - if (this.throttled) { - if (this.trailing) { - this._trailingValue = value; - this._hasTrailingValue = true; - } - } - else { - this.add(this.throttled = this.scheduler.schedule(dispatchNext, this.duration, { subscriber: this })); - if (this.leading) { - this.destination.next(value); - } - else if (this.trailing) { - this._trailingValue = value; - this._hasTrailingValue = true; - } - } - }; - ThrottleTimeSubscriber.prototype._complete = function () { - if (this._hasTrailingValue) { - this.destination.next(this._trailingValue); - this.destination.complete(); - } - else { - this.destination.complete(); - } - }; - ThrottleTimeSubscriber.prototype.clearThrottle = function () { - var throttled = this.throttled; - if (throttled) { - if (this.trailing && this._hasTrailingValue) { - this.destination.next(this._trailingValue); - this._trailingValue = null; - this._hasTrailingValue = false; - } - throttled.unsubscribe(); - this.remove(throttled); - this.throttled = null; - } - }; - return ThrottleTimeSubscriber; -}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -function dispatchNext(arg) { - var subscriber = arg.subscriber; - subscriber.clearThrottle(); -} -//# sourceMappingURL=throttleTime.js.map + returnValue += string.substr(endIndex); + return returnValue; +}; + +const stringEncaseCRLFWithFirstIndex = (string, prefix, postfix, index) => { + let endIndex = 0; + let returnValue = ''; + do { + const gotCR = string[index - 1] === '\r'; + returnValue += string.substr(endIndex, (gotCR ? index - 1 : index) - endIndex) + prefix + (gotCR ? '\r\n' : '\n') + postfix; + endIndex = index + 1; + index = string.indexOf('\n', endIndex); + } while (index !== -1); + + returnValue += string.substr(endIndex); + return returnValue; +}; + +module.exports = { + stringReplaceAll, + stringEncaseCRLFWithFirstIndex +}; /***/ }), -/* 503 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* 531 */ +/***/ (function(module, exports, __webpack_require__) { "use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "timeInterval", function() { return timeInterval; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TimeInterval", function() { return TimeInterval; }); -/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(56); -/* harmony import */ var _scan__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(463); -/* harmony import */ var _observable_defer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(92); -/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(67); -/** PURE_IMPORTS_START _scheduler_async,_scan,_observable_defer,_map PURE_IMPORTS_END */ +const TEMPLATE_REGEX = /(?:\\(u(?:[a-f\d]{4}|\{[a-f\d]{1,6}\})|x[a-f\d]{2}|.))|(?:\{(~)?(\w+(?:\([^)]*\))?(?:\.\w+(?:\([^)]*\))?)*)(?:[ \t]|(?=\r?\n)))|(\})|((?:.|[\r\n\f])+?)/gi; +const STYLE_REGEX = /(?:^|\.)(\w+)(?:\(([^)]*)\))?/g; +const STRING_REGEX = /^(['"])((?:\\.|(?!\1)[^\\])*)\1$/; +const ESCAPE_REGEX = /\\(u(?:[a-f\d]{4}|\{[a-f\d]{1,6}\})|x[a-f\d]{2}|.)|([^\\])/gi; +const ESCAPES = new Map([ + ['n', '\n'], + ['r', '\r'], + ['t', '\t'], + ['b', '\b'], + ['f', '\f'], + ['v', '\v'], + ['0', '\0'], + ['\\', '\\'], + ['e', '\u001B'], + ['a', '\u0007'] +]); +function unescape(c) { + const u = c[0] === 'u'; + const bracket = c[1] === '{'; -function timeInterval(scheduler) { - if (scheduler === void 0) { - scheduler = _scheduler_async__WEBPACK_IMPORTED_MODULE_0__["async"]; - } - return function (source) { - return Object(_observable_defer__WEBPACK_IMPORTED_MODULE_2__["defer"])(function () { - return source.pipe(Object(_scan__WEBPACK_IMPORTED_MODULE_1__["scan"])(function (_a, value) { - var current = _a.current; - return ({ value: value, current: scheduler.now(), last: current }); - }, { current: scheduler.now(), value: undefined, last: undefined }), Object(_map__WEBPACK_IMPORTED_MODULE_3__["map"])(function (_a) { - var current = _a.current, last = _a.last, value = _a.value; - return new TimeInterval(value, current - last); - })); - }); - }; + if ((u && !bracket && c.length === 5) || (c[0] === 'x' && c.length === 3)) { + return String.fromCharCode(parseInt(c.slice(1), 16)); + } + + if (u && bracket) { + return String.fromCodePoint(parseInt(c.slice(2, -1), 16)); + } + + return ESCAPES.get(c) || c; } -var TimeInterval = /*@__PURE__*/ (function () { - function TimeInterval(value, interval) { - this.value = value; - this.interval = interval; - } - return TimeInterval; -}()); -//# sourceMappingURL=timeInterval.js.map +function parseArguments(name, arguments_) { + const results = []; + const chunks = arguments_.trim().split(/\s*,\s*/g); + let matches; + for (const chunk of chunks) { + const number = Number(chunk); + if (!Number.isNaN(number)) { + results.push(number); + } else if ((matches = chunk.match(STRING_REGEX))) { + results.push(matches[2].replace(ESCAPE_REGEX, (m, escape, character) => escape ? unescape(escape) : character)); + } else { + throw new Error(`Invalid Chalk template style argument: ${chunk} (in style '${name}')`); + } + } -/***/ }), -/* 504 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { + return results; +} -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "timeout", function() { return timeout; }); -/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(56); -/* harmony import */ var _util_TimeoutError__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(65); -/* harmony import */ var _timeoutWith__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(505); -/* harmony import */ var _observable_throwError__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(50); -/** PURE_IMPORTS_START _scheduler_async,_util_TimeoutError,_timeoutWith,_observable_throwError PURE_IMPORTS_END */ +function parseStyle(style) { + STYLE_REGEX.lastIndex = 0; + const results = []; + let matches; + while ((matches = STYLE_REGEX.exec(style)) !== null) { + const name = matches[1]; + if (matches[2]) { + const args = parseArguments(name, matches[2]); + results.push([name].concat(args)); + } else { + results.push([name]); + } + } -function timeout(due, scheduler) { - if (scheduler === void 0) { - scheduler = _scheduler_async__WEBPACK_IMPORTED_MODULE_0__["async"]; - } - return Object(_timeoutWith__WEBPACK_IMPORTED_MODULE_2__["timeoutWith"])(due, Object(_observable_throwError__WEBPACK_IMPORTED_MODULE_3__["throwError"])(new _util_TimeoutError__WEBPACK_IMPORTED_MODULE_1__["TimeoutError"]()), scheduler); + return results; } -//# sourceMappingURL=timeout.js.map - -/***/ }), -/* 505 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +function buildStyle(chalk, styles) { + const enabled = {}; -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "timeoutWith", function() { return timeoutWith; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(56); -/* harmony import */ var _util_isDate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(437); -/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(91); -/** PURE_IMPORTS_START tslib,_scheduler_async,_util_isDate,_innerSubscribe PURE_IMPORTS_END */ + for (const layer of styles) { + for (const style of layer.styles) { + enabled[style[0]] = layer.inverse ? null : style.slice(1); + } + } + let current = chalk; + for (const [styleName, styles] of Object.entries(enabled)) { + if (!Array.isArray(styles)) { + continue; + } + if (!(styleName in current)) { + throw new Error(`Unknown Chalk style: ${styleName}`); + } + current = styles.length > 0 ? current[styleName](...styles) : current[styleName]; + } -function timeoutWith(due, withObservable, scheduler) { - if (scheduler === void 0) { - scheduler = _scheduler_async__WEBPACK_IMPORTED_MODULE_1__["async"]; - } - return function (source) { - var absoluteTimeout = Object(_util_isDate__WEBPACK_IMPORTED_MODULE_2__["isDate"])(due); - var waitFor = absoluteTimeout ? (+due - scheduler.now()) : Math.abs(due); - return source.lift(new TimeoutWithOperator(waitFor, absoluteTimeout, withObservable, scheduler)); - }; + return current; } -var TimeoutWithOperator = /*@__PURE__*/ (function () { - function TimeoutWithOperator(waitFor, absoluteTimeout, withObservable, scheduler) { - this.waitFor = waitFor; - this.absoluteTimeout = absoluteTimeout; - this.withObservable = withObservable; - this.scheduler = scheduler; - } - TimeoutWithOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new TimeoutWithSubscriber(subscriber, this.absoluteTimeout, this.waitFor, this.withObservable, this.scheduler)); - }; - return TimeoutWithOperator; -}()); -var TimeoutWithSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](TimeoutWithSubscriber, _super); - function TimeoutWithSubscriber(destination, absoluteTimeout, waitFor, withObservable, scheduler) { - var _this = _super.call(this, destination) || this; - _this.absoluteTimeout = absoluteTimeout; - _this.waitFor = waitFor; - _this.withObservable = withObservable; - _this.scheduler = scheduler; - _this.scheduleTimeout(); - return _this; - } - TimeoutWithSubscriber.dispatchTimeout = function (subscriber) { - var withObservable = subscriber.withObservable; - subscriber._unsubscribeAndRecycle(); - subscriber.add(Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_3__["innerSubscribe"])(withObservable, new _innerSubscribe__WEBPACK_IMPORTED_MODULE_3__["SimpleInnerSubscriber"](subscriber))); - }; - TimeoutWithSubscriber.prototype.scheduleTimeout = function () { - var action = this.action; - if (action) { - this.action = action.schedule(this, this.waitFor); - } - else { - this.add(this.action = this.scheduler.schedule(TimeoutWithSubscriber.dispatchTimeout, this.waitFor, this)); - } - }; - TimeoutWithSubscriber.prototype._next = function (value) { - if (!this.absoluteTimeout) { - this.scheduleTimeout(); - } - _super.prototype._next.call(this, value); - }; - TimeoutWithSubscriber.prototype._unsubscribe = function () { - this.action = undefined; - this.scheduler = null; - this.withObservable = null; - }; - return TimeoutWithSubscriber; -}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_3__["SimpleOuterSubscriber"])); -//# sourceMappingURL=timeoutWith.js.map +module.exports = (chalk, temporary) => { + const styles = []; + const chunks = []; + let chunk = []; -/***/ }), -/* 506 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { + // eslint-disable-next-line max-params + temporary.replace(TEMPLATE_REGEX, (m, escapeCharacter, inverse, style, close, character) => { + if (escapeCharacter) { + chunk.push(unescape(escapeCharacter)); + } else if (style) { + const string = chunk.join(''); + chunk = []; + chunks.push(styles.length === 0 ? string : buildStyle(chalk, styles)(string)); + styles.push({inverse, styles: parseStyle(style)}); + } else if (close) { + if (styles.length === 0) { + throw new Error('Found extraneous } in Chalk template literal'); + } -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "timestamp", function() { return timestamp; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Timestamp", function() { return Timestamp; }); -/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(56); -/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(67); -/** PURE_IMPORTS_START _scheduler_async,_map PURE_IMPORTS_END */ + chunks.push(buildStyle(chalk, styles)(chunk.join(''))); + chunk = []; + styles.pop(); + } else { + chunk.push(character); + } + }); + chunks.push(chunk.join('')); -function timestamp(scheduler) { - if (scheduler === void 0) { - scheduler = _scheduler_async__WEBPACK_IMPORTED_MODULE_0__["async"]; - } - return Object(_map__WEBPACK_IMPORTED_MODULE_1__["map"])(function (value) { return new Timestamp(value, scheduler.now()); }); -} -var Timestamp = /*@__PURE__*/ (function () { - function Timestamp(value, timestamp) { - this.value = value; - this.timestamp = timestamp; - } - return Timestamp; -}()); + if (styles.length > 0) { + const errMsg = `Chalk template literal is missing ${styles.length} closing bracket${styles.length === 1 ? '' : 's'} (\`}\`)`; + throw new Error(errMsg); + } -//# sourceMappingURL=timestamp.js.map + return chunks.join(''); +}; /***/ }), -/* 507 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* 532 */ +/***/ (function(module, exports, __webpack_require__) { "use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "toArray", function() { return toArray; }); -/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(462); -/** PURE_IMPORTS_START _reduce PURE_IMPORTS_END */ -function toArrayReducer(arr, item, index) { - if (index === 0) { - return [item]; - } - arr.push(item); - return arr; -} -function toArray() { - return Object(_reduce__WEBPACK_IMPORTED_MODULE_0__["reduce"])(toArrayReducer, []); -} -//# sourceMappingURL=toArray.js.map +const restoreCursor = __webpack_require__(533); +let isHidden = false; -/***/ }), -/* 508 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +exports.show = (writableStream = process.stderr) => { + if (!writableStream.isTTY) { + return; + } -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "window", function() { return window; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(28); -/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(91); -/** PURE_IMPORTS_START tslib,_Subject,_innerSubscribe PURE_IMPORTS_END */ + isHidden = false; + writableStream.write('\u001B[?25h'); +}; +exports.hide = (writableStream = process.stderr) => { + if (!writableStream.isTTY) { + return; + } + restoreCursor(); + isHidden = true; + writableStream.write('\u001B[?25l'); +}; -function window(windowBoundaries) { - return function windowOperatorFunction(source) { - return source.lift(new WindowOperator(windowBoundaries)); - }; -} -var WindowOperator = /*@__PURE__*/ (function () { - function WindowOperator(windowBoundaries) { - this.windowBoundaries = windowBoundaries; - } - WindowOperator.prototype.call = function (subscriber, source) { - var windowSubscriber = new WindowSubscriber(subscriber); - var sourceSubscription = source.subscribe(windowSubscriber); - if (!sourceSubscription.closed) { - windowSubscriber.add(Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_2__["innerSubscribe"])(this.windowBoundaries, new _innerSubscribe__WEBPACK_IMPORTED_MODULE_2__["SimpleInnerSubscriber"](windowSubscriber))); - } - return sourceSubscription; - }; - return WindowOperator; -}()); -var WindowSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](WindowSubscriber, _super); - function WindowSubscriber(destination) { - var _this = _super.call(this, destination) || this; - _this.window = new _Subject__WEBPACK_IMPORTED_MODULE_1__["Subject"](); - destination.next(_this.window); - return _this; - } - WindowSubscriber.prototype.notifyNext = function () { - this.openWindow(); - }; - WindowSubscriber.prototype.notifyError = function (error) { - this._error(error); - }; - WindowSubscriber.prototype.notifyComplete = function () { - this._complete(); - }; - WindowSubscriber.prototype._next = function (value) { - this.window.next(value); - }; - WindowSubscriber.prototype._error = function (err) { - this.window.error(err); - this.destination.error(err); - }; - WindowSubscriber.prototype._complete = function () { - this.window.complete(); - this.destination.complete(); - }; - WindowSubscriber.prototype._unsubscribe = function () { - this.window = null; - }; - WindowSubscriber.prototype.openWindow = function () { - var prevWindow = this.window; - if (prevWindow) { - prevWindow.complete(); - } - var destination = this.destination; - var newWindow = this.window = new _Subject__WEBPACK_IMPORTED_MODULE_1__["Subject"](); - destination.next(newWindow); - }; - return WindowSubscriber; -}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_2__["SimpleOuterSubscriber"])); -//# sourceMappingURL=window.js.map +exports.toggle = (force, writableStream) => { + if (force !== undefined) { + isHidden = force; + } + + if (isHidden) { + exports.show(writableStream); + } else { + exports.hide(writableStream); + } +}; /***/ }), -/* 509 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* 533 */ +/***/ (function(module, exports, __webpack_require__) { "use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "windowCount", function() { return windowCount; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); -/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(28); -/** PURE_IMPORTS_START tslib,_Subscriber,_Subject PURE_IMPORTS_END */ - +const onetime = __webpack_require__(152); +const signalExit = __webpack_require__(161); -function windowCount(windowSize, startWindowEvery) { - if (startWindowEvery === void 0) { - startWindowEvery = 0; - } - return function windowCountOperatorFunction(source) { - return source.lift(new WindowCountOperator(windowSize, startWindowEvery)); - }; -} -var WindowCountOperator = /*@__PURE__*/ (function () { - function WindowCountOperator(windowSize, startWindowEvery) { - this.windowSize = windowSize; - this.startWindowEvery = startWindowEvery; - } - WindowCountOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new WindowCountSubscriber(subscriber, this.windowSize, this.startWindowEvery)); - }; - return WindowCountOperator; -}()); -var WindowCountSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](WindowCountSubscriber, _super); - function WindowCountSubscriber(destination, windowSize, startWindowEvery) { - var _this = _super.call(this, destination) || this; - _this.destination = destination; - _this.windowSize = windowSize; - _this.startWindowEvery = startWindowEvery; - _this.windows = [new _Subject__WEBPACK_IMPORTED_MODULE_2__["Subject"]()]; - _this.count = 0; - destination.next(_this.windows[0]); - return _this; - } - WindowCountSubscriber.prototype._next = function (value) { - var startWindowEvery = (this.startWindowEvery > 0) ? this.startWindowEvery : this.windowSize; - var destination = this.destination; - var windowSize = this.windowSize; - var windows = this.windows; - var len = windows.length; - for (var i = 0; i < len && !this.closed; i++) { - windows[i].next(value); - } - var c = this.count - windowSize + 1; - if (c >= 0 && c % startWindowEvery === 0 && !this.closed) { - windows.shift().complete(); - } - if (++this.count % startWindowEvery === 0 && !this.closed) { - var window_1 = new _Subject__WEBPACK_IMPORTED_MODULE_2__["Subject"](); - windows.push(window_1); - destination.next(window_1); - } - }; - WindowCountSubscriber.prototype._error = function (err) { - var windows = this.windows; - if (windows) { - while (windows.length > 0 && !this.closed) { - windows.shift().error(err); - } - } - this.destination.error(err); - }; - WindowCountSubscriber.prototype._complete = function () { - var windows = this.windows; - if (windows) { - while (windows.length > 0 && !this.closed) { - windows.shift().complete(); - } - } - this.destination.complete(); - }; - WindowCountSubscriber.prototype._unsubscribe = function () { - this.count = 0; - this.windows = null; - }; - return WindowCountSubscriber; -}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -//# sourceMappingURL=windowCount.js.map +module.exports = onetime(() => { + signalExit(() => { + process.stderr.write('\u001B[?25h'); + }, {alwaysLast: true}); +}); /***/ }), -/* 510 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* 534 */ +/***/ (function(module, exports, __webpack_require__) { "use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "windowTime", function() { return windowTime; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(28); -/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(56); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(12); -/* harmony import */ var _util_isNumeric__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(99); -/* harmony import */ var _util_isScheduler__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(46); -/** PURE_IMPORTS_START tslib,_Subject,_scheduler_async,_Subscriber,_util_isNumeric,_util_isScheduler PURE_IMPORTS_END */ +const spinners = Object.assign({}, __webpack_require__(535)); +const spinnersList = Object.keys(spinners); +Object.defineProperty(spinners, 'random', { + get() { + const randomIndex = Math.floor(Math.random() * spinnersList.length); + const spinnerName = spinnersList[randomIndex]; + return spinners[spinnerName]; + } +}); +module.exports = spinners; +// TODO: Remove this for the next major release +module.exports.default = spinners; -function windowTime(windowTimeSpan) { - var scheduler = _scheduler_async__WEBPACK_IMPORTED_MODULE_2__["async"]; - var windowCreationInterval = null; - var maxWindowSize = Number.POSITIVE_INFINITY; - if (Object(_util_isScheduler__WEBPACK_IMPORTED_MODULE_5__["isScheduler"])(arguments[3])) { - scheduler = arguments[3]; - } - if (Object(_util_isScheduler__WEBPACK_IMPORTED_MODULE_5__["isScheduler"])(arguments[2])) { - scheduler = arguments[2]; - } - else if (Object(_util_isNumeric__WEBPACK_IMPORTED_MODULE_4__["isNumeric"])(arguments[2])) { - maxWindowSize = Number(arguments[2]); - } - if (Object(_util_isScheduler__WEBPACK_IMPORTED_MODULE_5__["isScheduler"])(arguments[1])) { - scheduler = arguments[1]; - } - else if (Object(_util_isNumeric__WEBPACK_IMPORTED_MODULE_4__["isNumeric"])(arguments[1])) { - windowCreationInterval = Number(arguments[1]); - } - return function windowTimeOperatorFunction(source) { - return source.lift(new WindowTimeOperator(windowTimeSpan, windowCreationInterval, maxWindowSize, scheduler)); - }; -} -var WindowTimeOperator = /*@__PURE__*/ (function () { - function WindowTimeOperator(windowTimeSpan, windowCreationInterval, maxWindowSize, scheduler) { - this.windowTimeSpan = windowTimeSpan; - this.windowCreationInterval = windowCreationInterval; - this.maxWindowSize = maxWindowSize; - this.scheduler = scheduler; - } - WindowTimeOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new WindowTimeSubscriber(subscriber, this.windowTimeSpan, this.windowCreationInterval, this.maxWindowSize, this.scheduler)); - }; - return WindowTimeOperator; -}()); -var CountedSubject = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](CountedSubject, _super); - function CountedSubject() { - var _this = _super !== null && _super.apply(this, arguments) || this; - _this._numberOfNextedValues = 0; - return _this; - } - CountedSubject.prototype.next = function (value) { - this._numberOfNextedValues++; - _super.prototype.next.call(this, value); - }; - Object.defineProperty(CountedSubject.prototype, "numberOfNextedValues", { - get: function () { - return this._numberOfNextedValues; - }, - enumerable: true, - configurable: true - }); - return CountedSubject; -}(_Subject__WEBPACK_IMPORTED_MODULE_1__["Subject"])); -var WindowTimeSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](WindowTimeSubscriber, _super); - function WindowTimeSubscriber(destination, windowTimeSpan, windowCreationInterval, maxWindowSize, scheduler) { - var _this = _super.call(this, destination) || this; - _this.destination = destination; - _this.windowTimeSpan = windowTimeSpan; - _this.windowCreationInterval = windowCreationInterval; - _this.maxWindowSize = maxWindowSize; - _this.scheduler = scheduler; - _this.windows = []; - var window = _this.openWindow(); - if (windowCreationInterval !== null && windowCreationInterval >= 0) { - var closeState = { subscriber: _this, window: window, context: null }; - var creationState = { windowTimeSpan: windowTimeSpan, windowCreationInterval: windowCreationInterval, subscriber: _this, scheduler: scheduler }; - _this.add(scheduler.schedule(dispatchWindowClose, windowTimeSpan, closeState)); - _this.add(scheduler.schedule(dispatchWindowCreation, windowCreationInterval, creationState)); - } - else { - var timeSpanOnlyState = { subscriber: _this, window: window, windowTimeSpan: windowTimeSpan }; - _this.add(scheduler.schedule(dispatchWindowTimeSpanOnly, windowTimeSpan, timeSpanOnlyState)); - } - return _this; - } - WindowTimeSubscriber.prototype._next = function (value) { - var windows = this.windows; - var len = windows.length; - for (var i = 0; i < len; i++) { - var window_1 = windows[i]; - if (!window_1.closed) { - window_1.next(value); - if (window_1.numberOfNextedValues >= this.maxWindowSize) { - this.closeWindow(window_1); - } - } - } - }; - WindowTimeSubscriber.prototype._error = function (err) { - var windows = this.windows; - while (windows.length > 0) { - windows.shift().error(err); - } - this.destination.error(err); - }; - WindowTimeSubscriber.prototype._complete = function () { - var windows = this.windows; - while (windows.length > 0) { - var window_2 = windows.shift(); - if (!window_2.closed) { - window_2.complete(); - } - } - this.destination.complete(); - }; - WindowTimeSubscriber.prototype.openWindow = function () { - var window = new CountedSubject(); - this.windows.push(window); - var destination = this.destination; - destination.next(window); - return window; - }; - WindowTimeSubscriber.prototype.closeWindow = function (window) { - window.complete(); - var windows = this.windows; - windows.splice(windows.indexOf(window), 1); - }; - return WindowTimeSubscriber; -}(_Subscriber__WEBPACK_IMPORTED_MODULE_3__["Subscriber"])); -function dispatchWindowTimeSpanOnly(state) { - var subscriber = state.subscriber, windowTimeSpan = state.windowTimeSpan, window = state.window; - if (window) { - subscriber.closeWindow(window); - } - state.window = subscriber.openWindow(); - this.schedule(state, windowTimeSpan); -} -function dispatchWindowCreation(state) { - var windowTimeSpan = state.windowTimeSpan, subscriber = state.subscriber, scheduler = state.scheduler, windowCreationInterval = state.windowCreationInterval; - var window = subscriber.openWindow(); - var action = this; - var context = { action: action, subscription: null }; - var timeSpanState = { subscriber: subscriber, window: window, context: context }; - context.subscription = scheduler.schedule(dispatchWindowClose, windowTimeSpan, timeSpanState); - action.add(context.subscription); - action.schedule(state, windowCreationInterval); -} -function dispatchWindowClose(state) { - var subscriber = state.subscriber, window = state.window, context = state.context; - if (context && context.action && context.subscription) { - context.action.remove(context.subscription); - } - subscriber.closeWindow(window); -} -//# sourceMappingURL=windowTime.js.map + +/***/ }), +/* 535 */ +/***/ (function(module) { + +module.exports = JSON.parse("{\"dots\":{\"interval\":80,\"frames\":[\"⠋\",\"⠙\",\"⠹\",\"⠸\",\"⠼\",\"⠴\",\"⠦\",\"⠧\",\"⠇\",\"⠏\"]},\"dots2\":{\"interval\":80,\"frames\":[\"⣾\",\"⣽\",\"⣻\",\"⢿\",\"⡿\",\"⣟\",\"⣯\",\"⣷\"]},\"dots3\":{\"interval\":80,\"frames\":[\"⠋\",\"⠙\",\"⠚\",\"⠞\",\"⠖\",\"⠦\",\"⠴\",\"⠲\",\"⠳\",\"⠓\"]},\"dots4\":{\"interval\":80,\"frames\":[\"⠄\",\"⠆\",\"⠇\",\"⠋\",\"⠙\",\"⠸\",\"⠰\",\"⠠\",\"⠰\",\"⠸\",\"⠙\",\"⠋\",\"⠇\",\"⠆\"]},\"dots5\":{\"interval\":80,\"frames\":[\"⠋\",\"⠙\",\"⠚\",\"⠒\",\"⠂\",\"⠂\",\"⠒\",\"⠲\",\"⠴\",\"⠦\",\"⠖\",\"⠒\",\"⠐\",\"⠐\",\"⠒\",\"⠓\",\"⠋\"]},\"dots6\":{\"interval\":80,\"frames\":[\"⠁\",\"⠉\",\"⠙\",\"⠚\",\"⠒\",\"⠂\",\"⠂\",\"⠒\",\"⠲\",\"⠴\",\"⠤\",\"⠄\",\"⠄\",\"⠤\",\"⠴\",\"⠲\",\"⠒\",\"⠂\",\"⠂\",\"⠒\",\"⠚\",\"⠙\",\"⠉\",\"⠁\"]},\"dots7\":{\"interval\":80,\"frames\":[\"⠈\",\"⠉\",\"⠋\",\"⠓\",\"⠒\",\"⠐\",\"⠐\",\"⠒\",\"⠖\",\"⠦\",\"⠤\",\"⠠\",\"⠠\",\"⠤\",\"⠦\",\"⠖\",\"⠒\",\"⠐\",\"⠐\",\"⠒\",\"⠓\",\"⠋\",\"⠉\",\"⠈\"]},\"dots8\":{\"interval\":80,\"frames\":[\"⠁\",\"⠁\",\"⠉\",\"⠙\",\"⠚\",\"⠒\",\"⠂\",\"⠂\",\"⠒\",\"⠲\",\"⠴\",\"⠤\",\"⠄\",\"⠄\",\"⠤\",\"⠠\",\"⠠\",\"⠤\",\"⠦\",\"⠖\",\"⠒\",\"⠐\",\"⠐\",\"⠒\",\"⠓\",\"⠋\",\"⠉\",\"⠈\",\"⠈\"]},\"dots9\":{\"interval\":80,\"frames\":[\"⢹\",\"⢺\",\"⢼\",\"⣸\",\"⣇\",\"⡧\",\"⡗\",\"⡏\"]},\"dots10\":{\"interval\":80,\"frames\":[\"⢄\",\"⢂\",\"⢁\",\"⡁\",\"⡈\",\"⡐\",\"⡠\"]},\"dots11\":{\"interval\":100,\"frames\":[\"⠁\",\"⠂\",\"⠄\",\"⡀\",\"⢀\",\"⠠\",\"⠐\",\"⠈\"]},\"dots12\":{\"interval\":80,\"frames\":[\"⢀⠀\",\"⡀⠀\",\"⠄⠀\",\"⢂⠀\",\"⡂⠀\",\"⠅⠀\",\"⢃⠀\",\"⡃⠀\",\"⠍⠀\",\"⢋⠀\",\"⡋⠀\",\"⠍⠁\",\"⢋⠁\",\"⡋⠁\",\"⠍⠉\",\"⠋⠉\",\"⠋⠉\",\"⠉⠙\",\"⠉⠙\",\"⠉⠩\",\"⠈⢙\",\"⠈⡙\",\"⢈⠩\",\"⡀⢙\",\"⠄⡙\",\"⢂⠩\",\"⡂⢘\",\"⠅⡘\",\"⢃⠨\",\"⡃⢐\",\"⠍⡐\",\"⢋⠠\",\"⡋⢀\",\"⠍⡁\",\"⢋⠁\",\"⡋⠁\",\"⠍⠉\",\"⠋⠉\",\"⠋⠉\",\"⠉⠙\",\"⠉⠙\",\"⠉⠩\",\"⠈⢙\",\"⠈⡙\",\"⠈⠩\",\"⠀⢙\",\"⠀⡙\",\"⠀⠩\",\"⠀⢘\",\"⠀⡘\",\"⠀⠨\",\"⠀⢐\",\"⠀⡐\",\"⠀⠠\",\"⠀⢀\",\"⠀⡀\"]},\"dots8Bit\":{\"interval\":80,\"frames\":[\"⠀\",\"⠁\",\"⠂\",\"⠃\",\"⠄\",\"⠅\",\"⠆\",\"⠇\",\"⡀\",\"⡁\",\"⡂\",\"⡃\",\"⡄\",\"⡅\",\"⡆\",\"⡇\",\"⠈\",\"⠉\",\"⠊\",\"⠋\",\"⠌\",\"⠍\",\"⠎\",\"⠏\",\"⡈\",\"⡉\",\"⡊\",\"⡋\",\"⡌\",\"⡍\",\"⡎\",\"⡏\",\"⠐\",\"⠑\",\"⠒\",\"⠓\",\"⠔\",\"⠕\",\"⠖\",\"⠗\",\"⡐\",\"⡑\",\"⡒\",\"⡓\",\"⡔\",\"⡕\",\"⡖\",\"⡗\",\"⠘\",\"⠙\",\"⠚\",\"⠛\",\"⠜\",\"⠝\",\"⠞\",\"⠟\",\"⡘\",\"⡙\",\"⡚\",\"⡛\",\"⡜\",\"⡝\",\"⡞\",\"⡟\",\"⠠\",\"⠡\",\"⠢\",\"⠣\",\"⠤\",\"⠥\",\"⠦\",\"⠧\",\"⡠\",\"⡡\",\"⡢\",\"⡣\",\"⡤\",\"⡥\",\"⡦\",\"⡧\",\"⠨\",\"⠩\",\"⠪\",\"⠫\",\"⠬\",\"⠭\",\"⠮\",\"⠯\",\"⡨\",\"⡩\",\"⡪\",\"⡫\",\"⡬\",\"⡭\",\"⡮\",\"⡯\",\"⠰\",\"⠱\",\"⠲\",\"⠳\",\"⠴\",\"⠵\",\"⠶\",\"⠷\",\"⡰\",\"⡱\",\"⡲\",\"⡳\",\"⡴\",\"⡵\",\"⡶\",\"⡷\",\"⠸\",\"⠹\",\"⠺\",\"⠻\",\"⠼\",\"⠽\",\"⠾\",\"⠿\",\"⡸\",\"⡹\",\"⡺\",\"⡻\",\"⡼\",\"⡽\",\"⡾\",\"⡿\",\"⢀\",\"⢁\",\"⢂\",\"⢃\",\"⢄\",\"⢅\",\"⢆\",\"⢇\",\"⣀\",\"⣁\",\"⣂\",\"⣃\",\"⣄\",\"⣅\",\"⣆\",\"⣇\",\"⢈\",\"⢉\",\"⢊\",\"⢋\",\"⢌\",\"⢍\",\"⢎\",\"⢏\",\"⣈\",\"⣉\",\"⣊\",\"⣋\",\"⣌\",\"⣍\",\"⣎\",\"⣏\",\"⢐\",\"⢑\",\"⢒\",\"⢓\",\"⢔\",\"⢕\",\"⢖\",\"⢗\",\"⣐\",\"⣑\",\"⣒\",\"⣓\",\"⣔\",\"⣕\",\"⣖\",\"⣗\",\"⢘\",\"⢙\",\"⢚\",\"⢛\",\"⢜\",\"⢝\",\"⢞\",\"⢟\",\"⣘\",\"⣙\",\"⣚\",\"⣛\",\"⣜\",\"⣝\",\"⣞\",\"⣟\",\"⢠\",\"⢡\",\"⢢\",\"⢣\",\"⢤\",\"⢥\",\"⢦\",\"⢧\",\"⣠\",\"⣡\",\"⣢\",\"⣣\",\"⣤\",\"⣥\",\"⣦\",\"⣧\",\"⢨\",\"⢩\",\"⢪\",\"⢫\",\"⢬\",\"⢭\",\"⢮\",\"⢯\",\"⣨\",\"⣩\",\"⣪\",\"⣫\",\"⣬\",\"⣭\",\"⣮\",\"⣯\",\"⢰\",\"⢱\",\"⢲\",\"⢳\",\"⢴\",\"⢵\",\"⢶\",\"⢷\",\"⣰\",\"⣱\",\"⣲\",\"⣳\",\"⣴\",\"⣵\",\"⣶\",\"⣷\",\"⢸\",\"⢹\",\"⢺\",\"⢻\",\"⢼\",\"⢽\",\"⢾\",\"⢿\",\"⣸\",\"⣹\",\"⣺\",\"⣻\",\"⣼\",\"⣽\",\"⣾\",\"⣿\"]},\"line\":{\"interval\":130,\"frames\":[\"-\",\"\\\\\",\"|\",\"/\"]},\"line2\":{\"interval\":100,\"frames\":[\"⠂\",\"-\",\"–\",\"—\",\"–\",\"-\"]},\"pipe\":{\"interval\":100,\"frames\":[\"┤\",\"┘\",\"┴\",\"└\",\"├\",\"┌\",\"┬\",\"┐\"]},\"simpleDots\":{\"interval\":400,\"frames\":[\". \",\".. \",\"...\",\" \"]},\"simpleDotsScrolling\":{\"interval\":200,\"frames\":[\". \",\".. \",\"...\",\" ..\",\" .\",\" \"]},\"star\":{\"interval\":70,\"frames\":[\"✶\",\"✸\",\"✹\",\"✺\",\"✹\",\"✷\"]},\"star2\":{\"interval\":80,\"frames\":[\"+\",\"x\",\"*\"]},\"flip\":{\"interval\":70,\"frames\":[\"_\",\"_\",\"_\",\"-\",\"`\",\"`\",\"'\",\"´\",\"-\",\"_\",\"_\",\"_\"]},\"hamburger\":{\"interval\":100,\"frames\":[\"☱\",\"☲\",\"☴\"]},\"growVertical\":{\"interval\":120,\"frames\":[\"▁\",\"▃\",\"▄\",\"▅\",\"▆\",\"▇\",\"▆\",\"▅\",\"▄\",\"▃\"]},\"growHorizontal\":{\"interval\":120,\"frames\":[\"▏\",\"▎\",\"▍\",\"▌\",\"▋\",\"▊\",\"▉\",\"▊\",\"▋\",\"▌\",\"▍\",\"▎\"]},\"balloon\":{\"interval\":140,\"frames\":[\" \",\".\",\"o\",\"O\",\"@\",\"*\",\" \"]},\"balloon2\":{\"interval\":120,\"frames\":[\".\",\"o\",\"O\",\"°\",\"O\",\"o\",\".\"]},\"noise\":{\"interval\":100,\"frames\":[\"▓\",\"▒\",\"░\"]},\"bounce\":{\"interval\":120,\"frames\":[\"⠁\",\"⠂\",\"⠄\",\"⠂\"]},\"boxBounce\":{\"interval\":120,\"frames\":[\"▖\",\"▘\",\"▝\",\"▗\"]},\"boxBounce2\":{\"interval\":100,\"frames\":[\"▌\",\"▀\",\"▐\",\"▄\"]},\"triangle\":{\"interval\":50,\"frames\":[\"◢\",\"◣\",\"◤\",\"◥\"]},\"arc\":{\"interval\":100,\"frames\":[\"◜\",\"◠\",\"◝\",\"◞\",\"◡\",\"◟\"]},\"circle\":{\"interval\":120,\"frames\":[\"◡\",\"⊙\",\"◠\"]},\"squareCorners\":{\"interval\":180,\"frames\":[\"◰\",\"◳\",\"◲\",\"◱\"]},\"circleQuarters\":{\"interval\":120,\"frames\":[\"◴\",\"◷\",\"◶\",\"◵\"]},\"circleHalves\":{\"interval\":50,\"frames\":[\"◐\",\"◓\",\"◑\",\"◒\"]},\"squish\":{\"interval\":100,\"frames\":[\"╫\",\"╪\"]},\"toggle\":{\"interval\":250,\"frames\":[\"⊶\",\"⊷\"]},\"toggle2\":{\"interval\":80,\"frames\":[\"▫\",\"▪\"]},\"toggle3\":{\"interval\":120,\"frames\":[\"□\",\"■\"]},\"toggle4\":{\"interval\":100,\"frames\":[\"■\",\"□\",\"▪\",\"▫\"]},\"toggle5\":{\"interval\":100,\"frames\":[\"▮\",\"▯\"]},\"toggle6\":{\"interval\":300,\"frames\":[\"ဝ\",\"၀\"]},\"toggle7\":{\"interval\":80,\"frames\":[\"⦾\",\"⦿\"]},\"toggle8\":{\"interval\":100,\"frames\":[\"◍\",\"◌\"]},\"toggle9\":{\"interval\":100,\"frames\":[\"◉\",\"◎\"]},\"toggle10\":{\"interval\":100,\"frames\":[\"㊂\",\"㊀\",\"㊁\"]},\"toggle11\":{\"interval\":50,\"frames\":[\"⧇\",\"⧆\"]},\"toggle12\":{\"interval\":120,\"frames\":[\"☗\",\"☖\"]},\"toggle13\":{\"interval\":80,\"frames\":[\"=\",\"*\",\"-\"]},\"arrow\":{\"interval\":100,\"frames\":[\"←\",\"↖\",\"↑\",\"↗\",\"→\",\"↘\",\"↓\",\"↙\"]},\"arrow2\":{\"interval\":80,\"frames\":[\"⬆️ \",\"↗️ \",\"➡️ \",\"↘️ \",\"⬇️ \",\"↙️ \",\"⬅️ \",\"↖️ \"]},\"arrow3\":{\"interval\":120,\"frames\":[\"▹▹▹▹▹\",\"▸▹▹▹▹\",\"▹▸▹▹▹\",\"▹▹▸▹▹\",\"▹▹▹▸▹\",\"▹▹▹▹▸\"]},\"bouncingBar\":{\"interval\":80,\"frames\":[\"[ ]\",\"[= ]\",\"[== ]\",\"[=== ]\",\"[ ===]\",\"[ ==]\",\"[ =]\",\"[ ]\",\"[ =]\",\"[ ==]\",\"[ ===]\",\"[====]\",\"[=== ]\",\"[== ]\",\"[= ]\"]},\"bouncingBall\":{\"interval\":80,\"frames\":[\"( ● )\",\"( ● )\",\"( ● )\",\"( ● )\",\"( ●)\",\"( ● )\",\"( ● )\",\"( ● )\",\"( ● )\",\"(● )\"]},\"smiley\":{\"interval\":200,\"frames\":[\"😄 \",\"😝 \"]},\"monkey\":{\"interval\":300,\"frames\":[\"🙈 \",\"🙈 \",\"🙉 \",\"🙊 \"]},\"hearts\":{\"interval\":100,\"frames\":[\"💛 \",\"💙 \",\"💜 \",\"💚 \",\"❤️ \"]},\"clock\":{\"interval\":100,\"frames\":[\"🕛 \",\"🕐 \",\"🕑 \",\"🕒 \",\"🕓 \",\"🕔 \",\"🕕 \",\"🕖 \",\"🕗 \",\"🕘 \",\"🕙 \",\"🕚 \"]},\"earth\":{\"interval\":180,\"frames\":[\"🌍 \",\"🌎 \",\"🌏 \"]},\"material\":{\"interval\":17,\"frames\":[\"█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"███▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"████▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"██████▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"██████▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"███████▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"████████▁▁▁▁▁▁▁▁▁▁▁▁\",\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\"██████████▁▁▁▁▁▁▁▁▁▁\",\"███████████▁▁▁▁▁▁▁▁▁\",\"█████████████▁▁▁▁▁▁▁\",\"██████████████▁▁▁▁▁▁\",\"██████████████▁▁▁▁▁▁\",\"▁██████████████▁▁▁▁▁\",\"▁██████████████▁▁▁▁▁\",\"▁██████████████▁▁▁▁▁\",\"▁▁██████████████▁▁▁▁\",\"▁▁▁██████████████▁▁▁\",\"▁▁▁▁█████████████▁▁▁\",\"▁▁▁▁██████████████▁▁\",\"▁▁▁▁██████████████▁▁\",\"▁▁▁▁▁██████████████▁\",\"▁▁▁▁▁██████████████▁\",\"▁▁▁▁▁██████████████▁\",\"▁▁▁▁▁▁██████████████\",\"▁▁▁▁▁▁██████████████\",\"▁▁▁▁▁▁▁█████████████\",\"▁▁▁▁▁▁▁█████████████\",\"▁▁▁▁▁▁▁▁████████████\",\"▁▁▁▁▁▁▁▁████████████\",\"▁▁▁▁▁▁▁▁▁███████████\",\"▁▁▁▁▁▁▁▁▁███████████\",\"▁▁▁▁▁▁▁▁▁▁██████████\",\"▁▁▁▁▁▁▁▁▁▁██████████\",\"▁▁▁▁▁▁▁▁▁▁▁▁████████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁███████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁██████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█████\",\"█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████\",\"██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███\",\"██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███\",\"███▁▁▁▁▁▁▁▁▁▁▁▁▁▁███\",\"████▁▁▁▁▁▁▁▁▁▁▁▁▁▁██\",\"█████▁▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\"█████▁▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\"██████▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\"████████▁▁▁▁▁▁▁▁▁▁▁▁\",\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\"███████████▁▁▁▁▁▁▁▁▁\",\"████████████▁▁▁▁▁▁▁▁\",\"████████████▁▁▁▁▁▁▁▁\",\"██████████████▁▁▁▁▁▁\",\"██████████████▁▁▁▁▁▁\",\"▁██████████████▁▁▁▁▁\",\"▁██████████████▁▁▁▁▁\",\"▁▁▁█████████████▁▁▁▁\",\"▁▁▁▁▁████████████▁▁▁\",\"▁▁▁▁▁████████████▁▁▁\",\"▁▁▁▁▁▁███████████▁▁▁\",\"▁▁▁▁▁▁▁▁█████████▁▁▁\",\"▁▁▁▁▁▁▁▁█████████▁▁▁\",\"▁▁▁▁▁▁▁▁▁█████████▁▁\",\"▁▁▁▁▁▁▁▁▁█████████▁▁\",\"▁▁▁▁▁▁▁▁▁▁█████████▁\",\"▁▁▁▁▁▁▁▁▁▁▁████████▁\",\"▁▁▁▁▁▁▁▁▁▁▁████████▁\",\"▁▁▁▁▁▁▁▁▁▁▁▁███████▁\",\"▁▁▁▁▁▁▁▁▁▁▁▁███████▁\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁███████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁███████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁██\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁██\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁██\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\"]},\"moon\":{\"interval\":80,\"frames\":[\"🌑 \",\"🌒 \",\"🌓 \",\"🌔 \",\"🌕 \",\"🌖 \",\"🌗 \",\"🌘 \"]},\"runner\":{\"interval\":140,\"frames\":[\"🚶 \",\"🏃 \"]},\"pong\":{\"interval\":80,\"frames\":[\"▐⠂ ▌\",\"▐⠈ ▌\",\"▐ ⠂ ▌\",\"▐ ⠠ ▌\",\"▐ ⡀ ▌\",\"▐ ⠠ ▌\",\"▐ ⠂ ▌\",\"▐ ⠈ ▌\",\"▐ ⠂ ▌\",\"▐ ⠠ ▌\",\"▐ ⡀ ▌\",\"▐ ⠠ ▌\",\"▐ ⠂ ▌\",\"▐ ⠈ ▌\",\"▐ ⠂▌\",\"▐ ⠠▌\",\"▐ ⡀▌\",\"▐ ⠠ ▌\",\"▐ ⠂ ▌\",\"▐ ⠈ ▌\",\"▐ ⠂ ▌\",\"▐ ⠠ ▌\",\"▐ ⡀ ▌\",\"▐ ⠠ ▌\",\"▐ ⠂ ▌\",\"▐ ⠈ ▌\",\"▐ ⠂ ▌\",\"▐ ⠠ ▌\",\"▐ ⡀ ▌\",\"▐⠠ ▌\"]},\"shark\":{\"interval\":120,\"frames\":[\"▐|\\\\____________▌\",\"▐_|\\\\___________▌\",\"▐__|\\\\__________▌\",\"▐___|\\\\_________▌\",\"▐____|\\\\________▌\",\"▐_____|\\\\_______▌\",\"▐______|\\\\______▌\",\"▐_______|\\\\_____▌\",\"▐________|\\\\____▌\",\"▐_________|\\\\___▌\",\"▐__________|\\\\__▌\",\"▐___________|\\\\_▌\",\"▐____________|\\\\▌\",\"▐____________/|▌\",\"▐___________/|_▌\",\"▐__________/|__▌\",\"▐_________/|___▌\",\"▐________/|____▌\",\"▐_______/|_____▌\",\"▐______/|______▌\",\"▐_____/|_______▌\",\"▐____/|________▌\",\"▐___/|_________▌\",\"▐__/|__________▌\",\"▐_/|___________▌\",\"▐/|____________▌\"]},\"dqpb\":{\"interval\":100,\"frames\":[\"d\",\"q\",\"p\",\"b\"]},\"weather\":{\"interval\":100,\"frames\":[\"☀️ \",\"☀️ \",\"☀️ \",\"🌤 \",\"⛅️ \",\"🌥 \",\"☁️ \",\"🌧 \",\"🌨 \",\"🌧 \",\"🌨 \",\"🌧 \",\"🌨 \",\"⛈ \",\"🌨 \",\"🌧 \",\"🌨 \",\"☁️ \",\"🌥 \",\"⛅️ \",\"🌤 \",\"☀️ \",\"☀️ \"]},\"christmas\":{\"interval\":400,\"frames\":[\"🌲\",\"🎄\"]},\"grenade\":{\"interval\":80,\"frames\":[\"، \",\"′ \",\" ´ \",\" ‾ \",\" ⸌\",\" ⸊\",\" |\",\" ⁎\",\" ⁕\",\" ෴ \",\" ⁓\",\" \",\" \",\" \"]},\"point\":{\"interval\":125,\"frames\":[\"∙∙∙\",\"●∙∙\",\"∙●∙\",\"∙∙●\",\"∙∙∙\"]},\"layer\":{\"interval\":150,\"frames\":[\"-\",\"=\",\"≡\"]},\"betaWave\":{\"interval\":80,\"frames\":[\"ρββββββ\",\"βρβββββ\",\"ββρββββ\",\"βββρβββ\",\"ββββρββ\",\"βββββρβ\",\"ββββββρ\"]},\"aesthetic\":{\"interval\":80,\"frames\":[\"▰▱▱▱▱▱▱\",\"▰▰▱▱▱▱▱\",\"▰▰▰▱▱▱▱\",\"▰▰▰▰▱▱▱\",\"▰▰▰▰▰▱▱\",\"▰▰▰▰▰▰▱\",\"▰▰▰▰▰▰▰\",\"▰▱▱▱▱▱▱\"]}}"); + +/***/ }), +/* 536 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +const chalk = __webpack_require__(537); + +const isSupported = process.platform !== 'win32' || process.env.CI || process.env.TERM === 'xterm-256color'; + +const main = { + info: chalk.blue('ℹ'), + success: chalk.green('✔'), + warning: chalk.yellow('⚠'), + error: chalk.red('✖') +}; + +const fallbacks = { + info: chalk.blue('i'), + success: chalk.green('√'), + warning: chalk.yellow('‼'), + error: chalk.red('×') +}; + +module.exports = isSupported ? main : fallbacks; /***/ }), -/* 511 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* 537 */ +/***/ (function(module, exports, __webpack_require__) { "use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "windowToggle", function() { return windowToggle; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(28); -/* harmony import */ var _Subscription__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(18); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(70); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(71); -/** PURE_IMPORTS_START tslib,_Subject,_Subscription,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ +const escapeStringRegexp = __webpack_require__(363); +const ansiStyles = __webpack_require__(538); +const stdoutColor = __webpack_require__(539).stdout; +const template = __webpack_require__(541); +const isSimpleWindowsTerm = process.platform === 'win32' && !(process.env.TERM || '').toLowerCase().startsWith('xterm'); +// `supportsColor.level` → `ansiStyles.color[name]` mapping +const levelMapping = ['ansi', 'ansi', 'ansi256', 'ansi16m']; -function windowToggle(openings, closingSelector) { - return function (source) { return source.lift(new WindowToggleOperator(openings, closingSelector)); }; +// `color-convert` models to exclude from the Chalk API due to conflicts and such +const skipModels = new Set(['gray']); + +const styles = Object.create(null); + +function applyOptions(obj, options) { + options = options || {}; + + // Detect level if not set manually + const scLevel = stdoutColor ? stdoutColor.level : 0; + obj.level = options.level === undefined ? scLevel : options.level; + obj.enabled = 'enabled' in options ? options.enabled : obj.level > 0; } -var WindowToggleOperator = /*@__PURE__*/ (function () { - function WindowToggleOperator(openings, closingSelector) { - this.openings = openings; - this.closingSelector = closingSelector; - } - WindowToggleOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new WindowToggleSubscriber(subscriber, this.openings, this.closingSelector)); - }; - return WindowToggleOperator; -}()); -var WindowToggleSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](WindowToggleSubscriber, _super); - function WindowToggleSubscriber(destination, openings, closingSelector) { - var _this = _super.call(this, destination) || this; - _this.openings = openings; - _this.closingSelector = closingSelector; - _this.contexts = []; - _this.add(_this.openSubscription = Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__["subscribeToResult"])(_this, openings, openings)); - return _this; - } - WindowToggleSubscriber.prototype._next = function (value) { - var contexts = this.contexts; - if (contexts) { - var len = contexts.length; - for (var i = 0; i < len; i++) { - contexts[i].window.next(value); - } - } - }; - WindowToggleSubscriber.prototype._error = function (err) { - var contexts = this.contexts; - this.contexts = null; - if (contexts) { - var len = contexts.length; - var index = -1; - while (++index < len) { - var context_1 = contexts[index]; - context_1.window.error(err); - context_1.subscription.unsubscribe(); - } - } - _super.prototype._error.call(this, err); - }; - WindowToggleSubscriber.prototype._complete = function () { - var contexts = this.contexts; - this.contexts = null; - if (contexts) { - var len = contexts.length; - var index = -1; - while (++index < len) { - var context_2 = contexts[index]; - context_2.window.complete(); - context_2.subscription.unsubscribe(); - } - } - _super.prototype._complete.call(this); - }; - WindowToggleSubscriber.prototype._unsubscribe = function () { - var contexts = this.contexts; - this.contexts = null; - if (contexts) { - var len = contexts.length; - var index = -1; - while (++index < len) { - var context_3 = contexts[index]; - context_3.window.unsubscribe(); - context_3.subscription.unsubscribe(); - } - } - }; - WindowToggleSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) { - if (outerValue === this.openings) { - var closingNotifier = void 0; - try { - var closingSelector = this.closingSelector; - closingNotifier = closingSelector(innerValue); - } - catch (e) { - return this.error(e); - } - var window_1 = new _Subject__WEBPACK_IMPORTED_MODULE_1__["Subject"](); - var subscription = new _Subscription__WEBPACK_IMPORTED_MODULE_2__["Subscription"](); - var context_4 = { window: window_1, subscription: subscription }; - this.contexts.push(context_4); - var innerSubscription = Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__["subscribeToResult"])(this, closingNotifier, context_4); - if (innerSubscription.closed) { - this.closeWindow(this.contexts.length - 1); - } - else { - innerSubscription.context = context_4; - subscription.add(innerSubscription); - } - this.destination.next(window_1); - } - else { - this.closeWindow(this.contexts.indexOf(outerValue)); - } - }; - WindowToggleSubscriber.prototype.notifyError = function (err) { - this.error(err); - }; - WindowToggleSubscriber.prototype.notifyComplete = function (inner) { - if (inner !== this.openSubscription) { - this.closeWindow(this.contexts.indexOf(inner.context)); - } - }; - WindowToggleSubscriber.prototype.closeWindow = function (index) { - if (index === -1) { - return; - } - var contexts = this.contexts; - var context = contexts[index]; - var window = context.window, subscription = context.subscription; - contexts.splice(index, 1); - window.complete(); - subscription.unsubscribe(); - }; - return WindowToggleSubscriber; -}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__["OuterSubscriber"])); -//# sourceMappingURL=windowToggle.js.map +function Chalk(options) { + // We check for this.template here since calling `chalk.constructor()` + // by itself will have a `this` of a previously constructed chalk object + if (!this || !(this instanceof Chalk) || this.template) { + const chalk = {}; + applyOptions(chalk, options); -/***/ }), -/* 512 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { + chalk.template = function () { + const args = [].slice.call(arguments); + return chalkTag.apply(null, [chalk.template].concat(args)); + }; -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "windowWhen", function() { return windowWhen; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(28); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(70); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(71); -/** PURE_IMPORTS_START tslib,_Subject,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ + Object.setPrototypeOf(chalk, Chalk.prototype); + Object.setPrototypeOf(chalk.template, chalk); + chalk.template.constructor = Chalk; + return chalk.template; + } + applyOptions(this, options); +} -function windowWhen(closingSelector) { - return function windowWhenOperatorFunction(source) { - return source.lift(new WindowOperator(closingSelector)); - }; +// Use bright blue on Windows as the normal blue color is illegible +if (isSimpleWindowsTerm) { + ansiStyles.blue.open = '\u001B[94m'; } -var WindowOperator = /*@__PURE__*/ (function () { - function WindowOperator(closingSelector) { - this.closingSelector = closingSelector; - } - WindowOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new WindowSubscriber(subscriber, this.closingSelector)); - }; - return WindowOperator; -}()); -var WindowSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](WindowSubscriber, _super); - function WindowSubscriber(destination, closingSelector) { - var _this = _super.call(this, destination) || this; - _this.destination = destination; - _this.closingSelector = closingSelector; - _this.openWindow(); - return _this; - } - WindowSubscriber.prototype.notifyNext = function (_outerValue, _innerValue, _outerIndex, _innerIndex, innerSub) { - this.openWindow(innerSub); - }; - WindowSubscriber.prototype.notifyError = function (error) { - this._error(error); - }; - WindowSubscriber.prototype.notifyComplete = function (innerSub) { - this.openWindow(innerSub); - }; - WindowSubscriber.prototype._next = function (value) { - this.window.next(value); - }; - WindowSubscriber.prototype._error = function (err) { - this.window.error(err); - this.destination.error(err); - this.unsubscribeClosingNotification(); - }; - WindowSubscriber.prototype._complete = function () { - this.window.complete(); - this.destination.complete(); - this.unsubscribeClosingNotification(); - }; - WindowSubscriber.prototype.unsubscribeClosingNotification = function () { - if (this.closingNotification) { - this.closingNotification.unsubscribe(); - } - }; - WindowSubscriber.prototype.openWindow = function (innerSub) { - if (innerSub === void 0) { - innerSub = null; - } - if (innerSub) { - this.remove(innerSub); - innerSub.unsubscribe(); - } - var prevWindow = this.window; - if (prevWindow) { - prevWindow.complete(); - } - var window = this.window = new _Subject__WEBPACK_IMPORTED_MODULE_1__["Subject"](); - this.destination.next(window); - var closingNotifier; - try { - var closingSelector = this.closingSelector; - closingNotifier = closingSelector(); - } - catch (e) { - this.destination.error(e); - this.window.error(e); - return; - } - this.add(this.closingNotification = Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__["subscribeToResult"])(this, closingNotifier)); - }; - return WindowSubscriber; -}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_2__["OuterSubscriber"])); -//# sourceMappingURL=windowWhen.js.map +for (const key of Object.keys(ansiStyles)) { + ansiStyles[key].closeRe = new RegExp(escapeStringRegexp(ansiStyles[key].close), 'g'); -/***/ }), -/* 513 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { + styles[key] = { + get() { + const codes = ansiStyles[key]; + return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, key); + } + }; +} -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "withLatestFrom", function() { return withLatestFrom; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(13); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(70); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(71); -/** PURE_IMPORTS_START tslib,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ +styles.visible = { + get() { + return build.call(this, this._styles || [], true, 'visible'); + } +}; + +ansiStyles.color.closeRe = new RegExp(escapeStringRegexp(ansiStyles.color.close), 'g'); +for (const model of Object.keys(ansiStyles.color.ansi)) { + if (skipModels.has(model)) { + continue; + } + styles[model] = { + get() { + const level = this.level; + return function () { + const open = ansiStyles.color[levelMapping[level]][model].apply(null, arguments); + const codes = { + open, + close: ansiStyles.color.close, + closeRe: ansiStyles.color.closeRe + }; + return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, model); + }; + } + }; +} +ansiStyles.bgColor.closeRe = new RegExp(escapeStringRegexp(ansiStyles.bgColor.close), 'g'); +for (const model of Object.keys(ansiStyles.bgColor.ansi)) { + if (skipModels.has(model)) { + continue; + } -function withLatestFrom() { - var args = []; - for (var _i = 0; _i < arguments.length; _i++) { - args[_i] = arguments[_i]; - } - return function (source) { - var project; - if (typeof args[args.length - 1] === 'function') { - project = args.pop(); - } - var observables = args; - return source.lift(new WithLatestFromOperator(observables, project)); - }; + const bgModel = 'bg' + model[0].toUpperCase() + model.slice(1); + styles[bgModel] = { + get() { + const level = this.level; + return function () { + const open = ansiStyles.bgColor[levelMapping[level]][model].apply(null, arguments); + const codes = { + open, + close: ansiStyles.bgColor.close, + closeRe: ansiStyles.bgColor.closeRe + }; + return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, model); + }; + } + }; } -var WithLatestFromOperator = /*@__PURE__*/ (function () { - function WithLatestFromOperator(observables, project) { - this.observables = observables; - this.project = project; - } - WithLatestFromOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new WithLatestFromSubscriber(subscriber, this.observables, this.project)); - }; - return WithLatestFromOperator; -}()); -var WithLatestFromSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](WithLatestFromSubscriber, _super); - function WithLatestFromSubscriber(destination, observables, project) { - var _this = _super.call(this, destination) || this; - _this.observables = observables; - _this.project = project; - _this.toRespond = []; - var len = observables.length; - _this.values = new Array(len); - for (var i = 0; i < len; i++) { - _this.toRespond.push(i); - } - for (var i = 0; i < len; i++) { - var observable = observables[i]; - _this.add(Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__["subscribeToResult"])(_this, observable, undefined, i)); - } - return _this; - } - WithLatestFromSubscriber.prototype.notifyNext = function (_outerValue, innerValue, outerIndex) { - this.values[outerIndex] = innerValue; - var toRespond = this.toRespond; - if (toRespond.length > 0) { - var found = toRespond.indexOf(outerIndex); - if (found !== -1) { - toRespond.splice(found, 1); - } - } - }; - WithLatestFromSubscriber.prototype.notifyComplete = function () { - }; - WithLatestFromSubscriber.prototype._next = function (value) { - if (this.toRespond.length === 0) { - var args = [value].concat(this.values); - if (this.project) { - this._tryProject(args); - } - else { - this.destination.next(args); - } - } - }; - WithLatestFromSubscriber.prototype._tryProject = function (args) { - var result; - try { - result = this.project.apply(this, args); - } - catch (err) { - this.destination.error(err); - return; - } - this.destination.next(result); - }; - return WithLatestFromSubscriber; -}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__["OuterSubscriber"])); -//# sourceMappingURL=withLatestFrom.js.map +const proto = Object.defineProperties(() => {}, styles); -/***/ }), -/* 514 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +function build(_styles, _empty, key) { + const builder = function () { + return applyStyle.apply(builder, arguments); + }; -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "zip", function() { return zip; }); -/* harmony import */ var _observable_zip__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(111); -/** PURE_IMPORTS_START _observable_zip PURE_IMPORTS_END */ + builder._styles = _styles; + builder._empty = _empty; -function zip() { - var observables = []; - for (var _i = 0; _i < arguments.length; _i++) { - observables[_i] = arguments[_i]; - } - return function zipOperatorFunction(source) { - return source.lift.call(_observable_zip__WEBPACK_IMPORTED_MODULE_0__["zip"].apply(void 0, [source].concat(observables))); - }; + const self = this; + + Object.defineProperty(builder, 'level', { + enumerable: true, + get() { + return self.level; + }, + set(level) { + self.level = level; + } + }); + + Object.defineProperty(builder, 'enabled', { + enumerable: true, + get() { + return self.enabled; + }, + set(enabled) { + self.enabled = enabled; + } + }); + + // See below for fix regarding invisible grey/dim combination on Windows + builder.hasGrey = this.hasGrey || key === 'gray' || key === 'grey'; + + // `__proto__` is used because we must return a function, but there is + // no way to create a function with a different prototype + builder.__proto__ = proto; // eslint-disable-line no-proto + + return builder; } -//# sourceMappingURL=zip.js.map +function applyStyle() { + // Support varags, but simply cast to string in case there's only one arg + const args = arguments; + const argsLen = args.length; + let str = String(arguments[0]); + + if (argsLen === 0) { + return ''; + } -/***/ }), -/* 515 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { + if (argsLen > 1) { + // Don't slice `arguments`, it prevents V8 optimizations + for (let a = 1; a < argsLen; a++) { + str += ' ' + args[a]; + } + } -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "zipAll", function() { return zipAll; }); -/* harmony import */ var _observable_zip__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(111); -/** PURE_IMPORTS_START _observable_zip PURE_IMPORTS_END */ + if (!this.enabled || this.level <= 0 || !str) { + return this._empty ? '' : str; + } + + // Turns out that on Windows dimmed gray text becomes invisible in cmd.exe, + // see https://github.com/chalk/chalk/issues/58 + // If we're on Windows and we're dealing with a gray color, temporarily make 'dim' a noop. + const originalDim = ansiStyles.dim.open; + if (isSimpleWindowsTerm && this.hasGrey) { + ansiStyles.dim.open = ''; + } + + for (const code of this._styles.slice().reverse()) { + // Replace any instances already present with a re-opening code + // otherwise only the part of the string until said closing code + // will be colored, and the rest will simply be 'plain'. + str = code.open + str.replace(code.closeRe, code.open) + code.close; -function zipAll(project) { - return function (source) { return source.lift(new _observable_zip__WEBPACK_IMPORTED_MODULE_0__["ZipOperator"](project)); }; -} -//# sourceMappingURL=zipAll.js.map + // Close the styling before a linebreak and reopen + // after next line to fix a bleed issue on macOS + // https://github.com/chalk/chalk/pull/92 + str = str.replace(/\r?\n/g, `${code.close}$&${code.open}`); + } + // Reset the original `dim` if we changed it to work around the Windows dimmed gray issue + ansiStyles.dim.open = originalDim; -/***/ }), -/* 516 */ -/***/ (function(module, exports, __webpack_require__) { + return str; +} -"use strict"; +function chalkTag(chalk, strings) { + if (!Array.isArray(strings)) { + // If chalk() was called by itself or with a string, + // return the string itself as a string. + return [].slice.call(arguments, 1).join(' '); + } + const args = [].slice.call(arguments, 2); + const parts = [strings.raw[0]]; -Object.defineProperty(exports, "__esModule", { - value: true -}); + for (let i = 1; i < strings.length; i++) { + parts.push(String(args[i - 1]).replace(/[{}\\]/g, '\\$&')); + parts.push(String(strings.raw[i])); + } -var _observe_lines = __webpack_require__(517); + return template(chalk, parts.join('')); +} -Object.keys(_observe_lines).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _observe_lines[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function () { - return _observe_lines[key]; - } - }); -}); +Object.defineProperties(Chalk.prototype, styles); -var _observe_readable = __webpack_require__(518); +module.exports = Chalk(); // eslint-disable-line new-cap +module.exports.supportsColor = stdoutColor; +module.exports.default = module.exports; // For TypeScript -Object.keys(_observe_readable).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (key in exports && exports[key] === _observe_readable[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function () { - return _observe_readable[key]; - } - }); -}); /***/ }), -/* 517 */ +/* 538 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; +/* WEBPACK VAR INJECTION */(function(module) { +const colorConvert = __webpack_require__(365); +const wrapAnsi16 = (fn, offset) => function () { + const code = fn.apply(colorConvert, arguments); + return `\u001B[${code + offset}m`; +}; -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.observeLines = observeLines; +const wrapAnsi256 = (fn, offset) => function () { + const code = fn.apply(colorConvert, arguments); + return `\u001B[${38 + offset};5;${code}m`; +}; -var Rx = _interopRequireWildcard(__webpack_require__(9)); +const wrapAnsi16m = (fn, offset) => function () { + const rgb = fn.apply(colorConvert, arguments); + return `\u001B[${38 + offset};2;${rgb[0]};${rgb[1]};${rgb[2]}m`; +}; -var _operators = __webpack_require__(418); +function assembleStyles() { + const codes = new Map(); + const styles = { + modifier: { + reset: [0, 0], + // 21 isn't widely supported and 22 does the same thing + bold: [1, 22], + dim: [2, 22], + italic: [3, 23], + underline: [4, 24], + inverse: [7, 27], + hidden: [8, 28], + strikethrough: [9, 29] + }, + color: { + black: [30, 39], + red: [31, 39], + green: [32, 39], + yellow: [33, 39], + blue: [34, 39], + magenta: [35, 39], + cyan: [36, 39], + white: [37, 39], + gray: [90, 39], -var _observe_readable = __webpack_require__(518); + // Bright color + redBright: [91, 39], + greenBright: [92, 39], + yellowBright: [93, 39], + blueBright: [94, 39], + magentaBright: [95, 39], + cyanBright: [96, 39], + whiteBright: [97, 39] + }, + bgColor: { + bgBlack: [40, 49], + bgRed: [41, 49], + bgGreen: [42, 49], + bgYellow: [43, 49], + bgBlue: [44, 49], + bgMagenta: [45, 49], + bgCyan: [46, 49], + bgWhite: [47, 49], -function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } + // Bright color + bgBlackBright: [100, 49], + bgRedBright: [101, 49], + bgGreenBright: [102, 49], + bgYellowBright: [103, 49], + bgBlueBright: [104, 49], + bgMagentaBright: [105, 49], + bgCyanBright: [106, 49], + bgWhiteBright: [107, 49] + } + }; -function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } + // Fix humans + styles.color.grey = styles.color.gray; -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ -const SEP = /\r?\n/; + for (const groupName of Object.keys(styles)) { + const group = styles[groupName]; -/** - * Creates an Observable from a Readable Stream that: - * - splits data from `readable` into lines - * - completes when `readable` emits "end" - * - fails if `readable` emits "errors" - * - * @param {ReadableStream} readable - * @return {Rx.Observable} - */ -function observeLines(readable) { - const done$ = (0, _observe_readable.observeReadable)(readable).pipe((0, _operators.share)()); - const scan$ = Rx.fromEvent(readable, 'data').pipe((0, _operators.scan)(({ - buffer - }, chunk) => { - buffer += chunk; - const lines = []; + for (const styleName of Object.keys(group)) { + const style = group[styleName]; - while (true) { - const match = buffer.match(SEP); + styles[styleName] = { + open: `\u001B[${style[0]}m`, + close: `\u001B[${style[1]}m` + }; - if (!match || match.index === undefined) { - break; - } + group[styleName] = styles[styleName]; - lines.push(buffer.slice(0, match.index)); - buffer = buffer.slice(match.index + match[0].length); - } + codes.set(style[0], style[1]); + } - return { - buffer, - lines - }; - }, { - buffer: '' - }), // stop if done completes or errors - (0, _operators.takeUntil)(done$.pipe((0, _operators.materialize)())), (0, _operators.share)()); - return Rx.merge( // use done$ to provide completion/errors - done$, // merge in the "lines" from each step - scan$.pipe((0, _operators.mergeMap)(({ - lines - }) => lines || [])), // inject the "unsplit" data at the end - scan$.pipe((0, _operators.last)(), (0, _operators.mergeMap)(({ - buffer - }) => buffer ? [buffer] : []), // if there were no lines, last() will error, so catch and complete - (0, _operators.catchError)(() => Rx.empty()))); -} + Object.defineProperty(styles, groupName, { + value: group, + enumerable: false + }); -/***/ }), -/* 518 */ -/***/ (function(module, exports, __webpack_require__) { + Object.defineProperty(styles, 'codes', { + value: codes, + enumerable: false + }); + } -"use strict"; + const ansi2ansi = n => n; + const rgb2rgb = (r, g, b) => [r, g, b]; + styles.color.close = '\u001B[39m'; + styles.bgColor.close = '\u001B[49m'; -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.observeReadable = observeReadable; + styles.color.ansi = { + ansi: wrapAnsi16(ansi2ansi, 0) + }; + styles.color.ansi256 = { + ansi256: wrapAnsi256(ansi2ansi, 0) + }; + styles.color.ansi16m = { + rgb: wrapAnsi16m(rgb2rgb, 0) + }; -var Rx = _interopRequireWildcard(__webpack_require__(9)); + styles.bgColor.ansi = { + ansi: wrapAnsi16(ansi2ansi, 10) + }; + styles.bgColor.ansi256 = { + ansi256: wrapAnsi256(ansi2ansi, 10) + }; + styles.bgColor.ansi16m = { + rgb: wrapAnsi16m(rgb2rgb, 10) + }; -var _operators = __webpack_require__(418); + for (let key of Object.keys(colorConvert)) { + if (typeof colorConvert[key] !== 'object') { + continue; + } -function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } + const suite = colorConvert[key]; -function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } + if (key === 'ansi16') { + key = 'ansi'; + } -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ + if ('ansi16' in suite) { + styles.color.ansi[key] = wrapAnsi16(suite.ansi16, 0); + styles.bgColor.ansi[key] = wrapAnsi16(suite.ansi16, 10); + } -/** - * Produces an Observable from a ReadableSteam that: - * - completes on the first "end" event - * - fails on the first "error" event - */ -function observeReadable(readable) { - return Rx.race(Rx.fromEvent(readable, 'end').pipe((0, _operators.first)(), (0, _operators.ignoreElements)()), Rx.fromEvent(readable, 'error').pipe((0, _operators.first)(), (0, _operators.mergeMap)(err => Rx.throwError(err)))); + if ('ansi256' in suite) { + styles.color.ansi256[key] = wrapAnsi256(suite.ansi256, 0); + styles.bgColor.ansi256[key] = wrapAnsi256(suite.ansi256, 10); + } + + if ('rgb' in suite) { + styles.color.ansi16m[key] = wrapAnsi16m(suite.rgb, 0); + styles.bgColor.ansi16m[key] = wrapAnsi16m(suite.rgb, 10); + } + } + + return styles; } +// Make the export immutable +Object.defineProperty(module, 'exports', { + enumerable: true, + get: assembleStyles +}); + +/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(116)(module))) + /***/ }), -/* 519 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* 539 */ +/***/ (function(module, exports, __webpack_require__) { "use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BuildCommand", function() { return BuildCommand; }); -/* harmony import */ var _utils_bazel__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(413); -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ -const BuildCommand = { - description: 'Runs a build in the Bazel built packages', - name: 'build', - reportTiming: { - group: 'scripts/kbn build', - id: 'total' - }, +const os = __webpack_require__(122); +const hasFlag = __webpack_require__(540); - async run(projects, projectGraph, { - options - }) { - const runOffline = (options === null || options === void 0 ? void 0 : options.offline) === true; // Call bazel with the target to build all available packages +const env = process.env; - await Object(_utils_bazel__WEBPACK_IMPORTED_MODULE_0__["runBazel"])(['build', '//packages:build', '--show_result=1'], runOffline); - } +let forceColor; +if (hasFlag('no-color') || + hasFlag('no-colors') || + hasFlag('color=false')) { + forceColor = false; +} else if (hasFlag('color') || + hasFlag('colors') || + hasFlag('color=true') || + hasFlag('color=always')) { + forceColor = true; +} +if ('FORCE_COLOR' in env) { + forceColor = env.FORCE_COLOR.length === 0 || parseInt(env.FORCE_COLOR, 10) !== 0; +} -}; +function translateLevel(level) { + if (level === 0) { + return false; + } -/***/ }), -/* 520 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { + return { + level, + hasBasic: true, + has256: level >= 2, + has16m: level >= 3 + }; +} -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "CleanCommand", function() { return CleanCommand; }); -/* harmony import */ var dedent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2); -/* harmony import */ var dedent__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(dedent__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(240); -/* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(del__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var ora__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(521); -/* harmony import */ var ora__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(ora__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_3__); -/* harmony import */ var _utils_bazel__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(413); -/* harmony import */ var _utils_fs__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(231); -/* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(220); -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ +function supportsColor(stream) { + if (forceColor === false) { + return 0; + } + if (hasFlag('color=16m') || + hasFlag('color=full') || + hasFlag('color=truecolor')) { + return 3; + } + if (hasFlag('color=256')) { + return 2; + } + if (stream && !stream.isTTY && forceColor !== true) { + return 0; + } + const min = forceColor ? 1 : 0; + if (process.platform === 'win32') { + // Node.js 7.5.0 is the first version of Node.js to include a patch to + // libuv that enables 256 color output on Windows. Anything earlier and it + // won't work. However, here we target Node.js 8 at minimum as it is an LTS + // release, and Node.js 7 is not. Windows 10 build 10586 is the first Windows + // release that supports 256 colors. Windows 10 build 14931 is the first release + // that supports 16m/TrueColor. + const osRelease = os.release().split('.'); + if ( + Number(process.versions.node.split('.')[0]) >= 8 && + Number(osRelease[0]) >= 10 && + Number(osRelease[2]) >= 10586 + ) { + return Number(osRelease[2]) >= 14931 ? 3 : 2; + } + return 1; + } -const CleanCommand = { - description: 'Deletes output directories, node_modules and resets internal caches.', - name: 'clean', - reportTiming: { - group: 'scripts/kbn clean', - id: 'total' - }, + if ('CI' in env) { + if (['TRAVIS', 'CIRCLECI', 'APPVEYOR', 'GITLAB_CI'].some(sign => sign in env) || env.CI_NAME === 'codeship') { + return 1; + } - async run(projects) { - _utils_log__WEBPACK_IMPORTED_MODULE_6__["log"].warning(dedent__WEBPACK_IMPORTED_MODULE_0___default.a` - This command is only necessary for the rare circumstance where you need to recover a consistent - state when problems arise. If you need to run this command often, please let us know by - filling out this form: https://ela.st/yarn-kbn-clean - `); - const toDelete = []; + return min; + } - for (const project of projects.values()) { - if (await Object(_utils_fs__WEBPACK_IMPORTED_MODULE_5__["isDirectory"])(project.nodeModulesLocation)) { - toDelete.push({ - cwd: project.path, - pattern: Object(path__WEBPACK_IMPORTED_MODULE_3__["relative"])(project.path, project.nodeModulesLocation) - }); - } + if ('TEAMCITY_VERSION' in env) { + return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0; + } - if (await Object(_utils_fs__WEBPACK_IMPORTED_MODULE_5__["isDirectory"])(project.targetLocation)) { - toDelete.push({ - cwd: project.path, - pattern: Object(path__WEBPACK_IMPORTED_MODULE_3__["relative"])(project.path, project.targetLocation) - }); - } + if (env.COLORTERM === 'truecolor') { + return 3; + } - const { - extraPatterns - } = project.getCleanConfig(); + if ('TERM_PROGRAM' in env) { + const version = parseInt((env.TERM_PROGRAM_VERSION || '').split('.')[0], 10); - if (extraPatterns) { - toDelete.push({ - cwd: project.path, - pattern: extraPatterns - }); - } - } // Runs Bazel soft clean + switch (env.TERM_PROGRAM) { + case 'iTerm.app': + return version >= 3 ? 3 : 2; + case 'Apple_Terminal': + return 2; + // No default + } + } + if (/-256(color)?$/i.test(env.TERM)) { + return 2; + } - if (await Object(_utils_bazel__WEBPACK_IMPORTED_MODULE_4__["isBazelBinAvailable"])()) { - await Object(_utils_bazel__WEBPACK_IMPORTED_MODULE_4__["runBazel"])(['clean']); - _utils_log__WEBPACK_IMPORTED_MODULE_6__["log"].success('Soft cleaned bazel'); - } + if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) { + return 1; + } - if (toDelete.length === 0) { - _utils_log__WEBPACK_IMPORTED_MODULE_6__["log"].success('Nothing to delete'); - } else { - /** - * In order to avoid patterns like `/build` in packages from accidentally - * impacting files outside the package we use `process.chdir()` to change - * the cwd to the package and execute `del()` without the `force` option - * so it will check that each file being deleted is within the package. - * - * `del()` does support a `cwd` option, but it's only for resolving the - * patterns and does not impact the cwd check. - */ - const originalCwd = process.cwd(); + if ('COLORTERM' in env) { + return 1; + } - try { - for (const { - pattern, - cwd - } of toDelete) { - process.chdir(cwd); - const promise = del__WEBPACK_IMPORTED_MODULE_1___default()(pattern); + if (env.TERM === 'dumb') { + return min; + } - if (_utils_log__WEBPACK_IMPORTED_MODULE_6__["log"].wouldLogLevel('info')) { - ora__WEBPACK_IMPORTED_MODULE_2___default.a.promise(promise, Object(path__WEBPACK_IMPORTED_MODULE_3__["relative"])(originalCwd, Object(path__WEBPACK_IMPORTED_MODULE_3__["join"])(cwd, String(pattern)))); - } + return min; +} - await promise; - } - } finally { - process.chdir(originalCwd); - } - } - } +function getSupportLevel(stream) { + const level = supportsColor(stream); + return translateLevel(level); +} +module.exports = { + supportsColor: getSupportLevel, + stdout: getSupportLevel(process.stdout), + stderr: getSupportLevel(process.stderr) }; + /***/ }), -/* 521 */ +/* 540 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const readline = __webpack_require__(522); -const chalk = __webpack_require__(523); -const cliCursor = __webpack_require__(526); -const cliSpinners = __webpack_require__(528); -const logSymbols = __webpack_require__(530); -const stripAnsi = __webpack_require__(536); -const wcwidth = __webpack_require__(538); -const isInteractive = __webpack_require__(542); -const MuteStream = __webpack_require__(543); - -const TEXT = Symbol('text'); -const PREFIX_TEXT = Symbol('prefixText'); +module.exports = (flag, argv) => { + argv = argv || process.argv; + const prefix = flag.startsWith('-') ? '' : (flag.length === 1 ? '-' : '--'); + const pos = argv.indexOf(prefix + flag); + const terminatorPos = argv.indexOf('--'); + return pos !== -1 && (terminatorPos === -1 ? true : pos < terminatorPos); +}; -const ASCII_ETX_CODE = 0x03; // Ctrl+C emits this code -class StdinDiscarder { - constructor() { - this.requests = 0; +/***/ }), +/* 541 */ +/***/ (function(module, exports, __webpack_require__) { - this.mutedStream = new MuteStream(); - this.mutedStream.pipe(process.stdout); - this.mutedStream.mute(); +"use strict"; - const self = this; - this.ourEmit = function (event, data, ...args) { - const {stdin} = process; - if (self.requests > 0 || stdin.emit === self.ourEmit) { - if (event === 'keypress') { // Fixes readline behavior - return; - } +const TEMPLATE_REGEX = /(?:\\(u[a-f\d]{4}|x[a-f\d]{2}|.))|(?:\{(~)?(\w+(?:\([^)]*\))?(?:\.\w+(?:\([^)]*\))?)*)(?:[ \t]|(?=\r?\n)))|(\})|((?:.|[\r\n\f])+?)/gi; +const STYLE_REGEX = /(?:^|\.)(\w+)(?:\(([^)]*)\))?/g; +const STRING_REGEX = /^(['"])((?:\\.|(?!\1)[^\\])*)\1$/; +const ESCAPE_REGEX = /\\(u[a-f\d]{4}|x[a-f\d]{2}|.)|([^\\])/gi; - if (event === 'data' && data.includes(ASCII_ETX_CODE)) { - process.emit('SIGINT'); - } +const ESCAPES = new Map([ + ['n', '\n'], + ['r', '\r'], + ['t', '\t'], + ['b', '\b'], + ['f', '\f'], + ['v', '\v'], + ['0', '\0'], + ['\\', '\\'], + ['e', '\u001B'], + ['a', '\u0007'] +]); - Reflect.apply(self.oldEmit, this, [event, data, ...args]); - } else { - Reflect.apply(process.stdin.emit, this, [event, data, ...args]); - } - }; +function unescape(c) { + if ((c[0] === 'u' && c.length === 5) || (c[0] === 'x' && c.length === 3)) { + return String.fromCharCode(parseInt(c.slice(1), 16)); } - start() { - this.requests++; + return ESCAPES.get(c) || c; +} - if (this.requests === 1) { - this.realStart(); +function parseArguments(name, args) { + const results = []; + const chunks = args.trim().split(/\s*,\s*/g); + let matches; + + for (const chunk of chunks) { + if (!isNaN(chunk)) { + results.push(Number(chunk)); + } else if ((matches = chunk.match(STRING_REGEX))) { + results.push(matches[2].replace(ESCAPE_REGEX, (m, escape, chr) => escape ? unescape(escape) : chr)); + } else { + throw new Error(`Invalid Chalk template style argument: ${chunk} (in style '${name}')`); } } - stop() { - if (this.requests <= 0) { - throw new Error('`stop` called more times than `start`'); - } + return results; +} - this.requests--; +function parseStyle(style) { + STYLE_REGEX.lastIndex = 0; - if (this.requests === 0) { - this.realStop(); + const results = []; + let matches; + + while ((matches = STYLE_REGEX.exec(style)) !== null) { + const name = matches[1]; + + if (matches[2]) { + const args = parseArguments(name, matches[2]); + results.push([name].concat(args)); + } else { + results.push([name]); } } - realStart() { - // No known way to make it work reliably on Windows - if (process.platform === 'win32') { - return; + return results; +} + +function buildStyle(chalk, styles) { + const enabled = {}; + + for (const layer of styles) { + for (const style of layer.styles) { + enabled[style[0]] = layer.inverse ? null : style.slice(1); } + } - this.rl = readline.createInterface({ - input: process.stdin, - output: this.mutedStream - }); + let current = chalk; + for (const styleName of Object.keys(enabled)) { + if (Array.isArray(enabled[styleName])) { + if (!(styleName in current)) { + throw new Error(`Unknown Chalk style: ${styleName}`); + } - this.rl.on('SIGINT', () => { - if (process.listenerCount('SIGINT') === 0) { - process.emit('SIGINT'); + if (enabled[styleName].length > 0) { + current = current[styleName].apply(current, enabled[styleName]); } else { - this.rl.close(); - process.kill(process.pid, 'SIGINT'); + current = current[styleName]; } - }); + } } - realStop() { - if (process.platform === 'win32') { - return; + return current; +} + +module.exports = (chalk, tmp) => { + const styles = []; + const chunks = []; + let chunk = []; + + // eslint-disable-next-line max-params + tmp.replace(TEMPLATE_REGEX, (m, escapeChar, inverse, style, close, chr) => { + if (escapeChar) { + chunk.push(unescape(escapeChar)); + } else if (style) { + const str = chunk.join(''); + chunk = []; + chunks.push(styles.length === 0 ? str : buildStyle(chalk, styles)(str)); + styles.push({inverse, styles: parseStyle(style)}); + } else if (close) { + if (styles.length === 0) { + throw new Error('Found extraneous } in Chalk template literal'); + } + + chunks.push(buildStyle(chalk, styles)(chunk.join(''))); + chunk = []; + styles.pop(); + } else { + chunk.push(chr); } + }); - this.rl.close(); - this.rl = undefined; + chunks.push(chunk.join('')); + + if (styles.length > 0) { + const errMsg = `Chalk template literal is missing ${styles.length} closing bracket${styles.length === 1 ? '' : 's'} (\`}\`)`; + throw new Error(errMsg); } -} -let stdinDiscarder; + return chunks.join(''); +}; -class Ora { - constructor(options) { - if (!stdinDiscarder) { - stdinDiscarder = new StdinDiscarder(); - } - if (typeof options === 'string') { - options = { - text: options - }; - } +/***/ }), +/* 542 */ +/***/ (function(module, exports, __webpack_require__) { - this.options = { - text: '', - color: 'cyan', - stream: process.stderr, - discardStdin: true, - ...options - }; +"use strict"; - this.spinner = this.options.spinner; +const ansiRegex = __webpack_require__(543); - this.color = this.options.color; - this.hideCursor = this.options.hideCursor !== false; - this.interval = this.options.interval || this.spinner.interval || 100; - this.stream = this.options.stream; - this.id = undefined; - this.isEnabled = typeof this.options.isEnabled === 'boolean' ? this.options.isEnabled : isInteractive({stream: this.stream}); +module.exports = string => typeof string === 'string' ? string.replace(ansiRegex(), '') : string; - // Set *after* `this.stream` - this.text = this.options.text; - this.prefixText = this.options.prefixText; - this.linesToClear = 0; - this.indent = this.options.indent; - this.discardStdin = this.options.discardStdin; - this.isDiscardingStdin = false; - } - get indent() { - return this._indent; - } +/***/ }), +/* 543 */ +/***/ (function(module, exports, __webpack_require__) { - set indent(indent = 0) { - if (!(indent >= 0 && Number.isInteger(indent))) { - throw new Error('The `indent` option must be an integer from 0 and up'); - } +"use strict"; - this._indent = indent; - } - _updateInterval(interval) { - if (interval !== undefined) { - this.interval = interval; - } - } +module.exports = ({onlyFirst = false} = {}) => { + const pattern = [ + '[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)', + '(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))' + ].join('|'); - get spinner() { - return this._spinner; - } + return new RegExp(pattern, onlyFirst ? undefined : 'g'); +}; - set spinner(spinner) { - this.frameIndex = 0; - if (typeof spinner === 'object') { - if (spinner.frames === undefined) { - throw new Error('The given spinner must have a `frames` property'); - } +/***/ }), +/* 544 */ +/***/ (function(module, exports, __webpack_require__) { - this._spinner = spinner; - } else if (process.platform === 'win32') { - this._spinner = cliSpinners.line; - } else if (spinner === undefined) { - // Set default spinner - this._spinner = cliSpinners.dots; - } else if (cliSpinners[spinner]) { - this._spinner = cliSpinners[spinner]; - } else { - throw new Error(`There is no built-in spinner named '${spinner}'. See https://github.com/sindresorhus/cli-spinners/blob/master/spinners.json for a full list.`); - } +"use strict"; - this._updateInterval(this._spinner.interval); - } - get text() { - return this[TEXT]; - } +var defaults = __webpack_require__(545) +var combining = __webpack_require__(547) - get prefixText() { - return this[PREFIX_TEXT]; - } +var DEFAULTS = { + nul: 0, + control: 0 +} - get isSpinning() { - return this.id !== undefined; - } +module.exports = function wcwidth(str) { + return wcswidth(str, DEFAULTS) +} - updateLineCount() { - const columns = this.stream.columns || 80; - const fullPrefixText = (typeof this[PREFIX_TEXT] === 'string') ? this[PREFIX_TEXT] + '-' : ''; - this.lineCount = stripAnsi(fullPrefixText + '--' + this[TEXT]).split('\n').reduce((count, line) => { - return count + Math.max(1, Math.ceil(wcwidth(line) / columns)); - }, 0); - } +module.exports.config = function(opts) { + opts = defaults(opts || {}, DEFAULTS) + return function wcwidth(str) { + return wcswidth(str, opts) + } +} - set text(value) { - this[TEXT] = value; - this.updateLineCount(); - } +/* + * The following functions define the column width of an ISO 10646 + * character as follows: + * - The null character (U+0000) has a column width of 0. + * - Other C0/C1 control characters and DEL will lead to a return value + * of -1. + * - Non-spacing and enclosing combining characters (general category + * code Mn or Me in the + * Unicode database) have a column width of 0. + * - SOFT HYPHEN (U+00AD) has a column width of 1. + * - Other format characters (general category code Cf in the Unicode + * database) and ZERO WIDTH + * SPACE (U+200B) have a column width of 0. + * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF) + * have a column width of 0. + * - Spacing characters in the East Asian Wide (W) or East Asian + * Full-width (F) category as + * defined in Unicode Technical Report #11 have a column width of 2. + * - All remaining characters (including all printable ISO 8859-1 and + * WGL4 characters, Unicode control characters, etc.) have a column + * width of 1. + * This implementation assumes that characters are encoded in ISO 10646. +*/ - set prefixText(value) { - this[PREFIX_TEXT] = value; - this.updateLineCount(); - } +function wcswidth(str, opts) { + if (typeof str !== 'string') return wcwidth(str, opts) - frame() { - const {frames} = this.spinner; - let frame = frames[this.frameIndex]; + var s = 0 + for (var i = 0; i < str.length; i++) { + var n = wcwidth(str.charCodeAt(i), opts) + if (n < 0) return -1 + s += n + } - if (this.color) { - frame = chalk[this.color](frame); - } + return s +} - this.frameIndex = ++this.frameIndex % frames.length; - const fullPrefixText = (typeof this.prefixText === 'string' && this.prefixText !== '') ? this.prefixText + ' ' : ''; - const fullText = typeof this.text === 'string' ? ' ' + this.text : ''; +function wcwidth(ucs, opts) { + // test for 8-bit control characters + if (ucs === 0) return opts.nul + if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) return opts.control - return fullPrefixText + frame + fullText; - } + // binary search in table of non-spacing characters + if (bisearch(ucs)) return 0 - clear() { - if (!this.isEnabled || !this.stream.isTTY) { - return this; - } + // if we arrive here, ucs is not a combining or C0/C1 control character + return 1 + + (ucs >= 0x1100 && + (ucs <= 0x115f || // Hangul Jamo init. consonants + ucs == 0x2329 || ucs == 0x232a || + (ucs >= 0x2e80 && ucs <= 0xa4cf && + ucs != 0x303f) || // CJK ... Yi + (ucs >= 0xac00 && ucs <= 0xd7a3) || // Hangul Syllables + (ucs >= 0xf900 && ucs <= 0xfaff) || // CJK Compatibility Ideographs + (ucs >= 0xfe10 && ucs <= 0xfe19) || // Vertical forms + (ucs >= 0xfe30 && ucs <= 0xfe6f) || // CJK Compatibility Forms + (ucs >= 0xff00 && ucs <= 0xff60) || // Fullwidth Forms + (ucs >= 0xffe0 && ucs <= 0xffe6) || + (ucs >= 0x20000 && ucs <= 0x2fffd) || + (ucs >= 0x30000 && ucs <= 0x3fffd))); +} - for (let i = 0; i < this.linesToClear; i++) { - if (i > 0) { - this.stream.moveCursor(0, -1); - } +function bisearch(ucs) { + var min = 0 + var max = combining.length - 1 + var mid - this.stream.clearLine(); - this.stream.cursorTo(this.indent); - } + if (ucs < combining[0][0] || ucs > combining[max][1]) return false - this.linesToClear = 0; + while (max >= min) { + mid = Math.floor((min + max) / 2) + if (ucs > combining[mid][1]) min = mid + 1 + else if (ucs < combining[mid][0]) max = mid - 1 + else return true + } - return this; - } + return false +} - render() { - this.clear(); - this.stream.write(this.frame()); - this.linesToClear = this.lineCount; - return this; - } +/***/ }), +/* 545 */ +/***/ (function(module, exports, __webpack_require__) { - start(text) { - if (text) { - this.text = text; - } +var clone = __webpack_require__(546); - if (!this.isEnabled) { - if (this.text) { - this.stream.write(`- ${this.text}\n`); - } +module.exports = function(options, defaults) { + options = options || {}; - return this; - } + Object.keys(defaults).forEach(function(key) { + if (typeof options[key] === 'undefined') { + options[key] = clone(defaults[key]); + } + }); - if (this.isSpinning) { - return this; - } + return options; +}; - if (this.hideCursor) { - cliCursor.hide(this.stream); - } +/***/ }), +/* 546 */ +/***/ (function(module, exports, __webpack_require__) { - if (this.discardStdin && process.stdin.isTTY) { - this.isDiscardingStdin = true; - stdinDiscarder.start(); - } +var clone = (function() { +'use strict'; - this.render(); - this.id = setInterval(this.render.bind(this), this.interval); +/** + * Clones (copies) an Object using deep copying. + * + * This function supports circular references by default, but if you are certain + * there are no circular references in your object, you can save some CPU time + * by calling clone(obj, false). + * + * Caution: if `circular` is false and `parent` contains circular references, + * your program may enter an infinite loop and crash. + * + * @param `parent` - the object to be cloned + * @param `circular` - set to true if the object to be cloned may contain + * circular references. (optional - true by default) + * @param `depth` - set to a number if the object is only to be cloned to + * a particular depth. (optional - defaults to Infinity) + * @param `prototype` - sets the prototype to be used when cloning an object. + * (optional - defaults to parent prototype). +*/ +function clone(parent, circular, depth, prototype) { + var filter; + if (typeof circular === 'object') { + depth = circular.depth; + prototype = circular.prototype; + filter = circular.filter; + circular = circular.circular + } + // maintain two arrays for circular references, where corresponding parents + // and children have the same index + var allParents = []; + var allChildren = []; - return this; - } + var useBuffer = typeof Buffer != 'undefined'; - stop() { - if (!this.isEnabled) { - return this; - } + if (typeof circular == 'undefined') + circular = true; + + if (typeof depth == 'undefined') + depth = Infinity; + + // recurse this function so we don't reset allParents and allChildren + function _clone(parent, depth) { + // cloning null always returns null + if (parent === null) + return null; + + if (depth == 0) + return parent; + + var child; + var proto; + if (typeof parent != 'object') { + return parent; + } + + if (clone.__isArray(parent)) { + child = []; + } else if (clone.__isRegExp(parent)) { + child = new RegExp(parent.source, __getRegExpFlags(parent)); + if (parent.lastIndex) child.lastIndex = parent.lastIndex; + } else if (clone.__isDate(parent)) { + child = new Date(parent.getTime()); + } else if (useBuffer && Buffer.isBuffer(parent)) { + if (Buffer.allocUnsafe) { + // Node.js >= 4.5.0 + child = Buffer.allocUnsafe(parent.length); + } else { + // Older Node.js versions + child = new Buffer(parent.length); + } + parent.copy(child); + return child; + } else { + if (typeof prototype == 'undefined') { + proto = Object.getPrototypeOf(parent); + child = Object.create(proto); + } + else { + child = Object.create(prototype); + proto = prototype; + } + } - clearInterval(this.id); - this.id = undefined; - this.frameIndex = 0; - this.clear(); - if (this.hideCursor) { - cliCursor.show(this.stream); - } + if (circular) { + var index = allParents.indexOf(parent); - if (this.discardStdin && process.stdin.isTTY && this.isDiscardingStdin) { - stdinDiscarder.stop(); - this.isDiscardingStdin = false; - } + if (index != -1) { + return allChildren[index]; + } + allParents.push(parent); + allChildren.push(child); + } - return this; - } + for (var i in parent) { + var attrs; + if (proto) { + attrs = Object.getOwnPropertyDescriptor(proto, i); + } - succeed(text) { - return this.stopAndPersist({symbol: logSymbols.success, text}); - } + if (attrs && attrs.set == null) { + continue; + } + child[i] = _clone(parent[i], depth - 1); + } - fail(text) { - return this.stopAndPersist({symbol: logSymbols.error, text}); - } + return child; + } - warn(text) { - return this.stopAndPersist({symbol: logSymbols.warning, text}); - } + return _clone(parent, depth); +} - info(text) { - return this.stopAndPersist({symbol: logSymbols.info, text}); - } +/** + * Simple flat clone using prototype, accepts only objects, usefull for property + * override on FLAT configuration object (no nested props). + * + * USE WITH CAUTION! This may not behave as you wish if you do not know how this + * works. + */ +clone.clonePrototype = function clonePrototype(parent) { + if (parent === null) + return null; - stopAndPersist(options = {}) { - const prefixText = options.prefixText || this.prefixText; - const fullPrefixText = (typeof prefixText === 'string' && prefixText !== '') ? prefixText + ' ' : ''; - const text = options.text || this.text; - const fullText = (typeof text === 'string') ? ' ' + text : ''; + var c = function () {}; + c.prototype = parent; + return new c(); +}; - this.stop(); - this.stream.write(`${fullPrefixText}${options.symbol || ' '}${fullText}\n`); +// private utility functions - return this; - } -} +function __objToStr(o) { + return Object.prototype.toString.call(o); +}; +clone.__objToStr = __objToStr; -const oraFactory = function (options) { - return new Ora(options); +function __isDate(o) { + return typeof o === 'object' && __objToStr(o) === '[object Date]'; }; +clone.__isDate = __isDate; -module.exports = oraFactory; +function __isArray(o) { + return typeof o === 'object' && __objToStr(o) === '[object Array]'; +}; +clone.__isArray = __isArray; -module.exports.promise = (action, options) => { - // eslint-disable-next-line promise/prefer-await-to-then - if (typeof action.then !== 'function') { - throw new TypeError('Parameter `action` must be a Promise'); - } +function __isRegExp(o) { + return typeof o === 'object' && __objToStr(o) === '[object RegExp]'; +}; +clone.__isRegExp = __isRegExp; - const spinner = new Ora(options); - spinner.start(); +function __getRegExpFlags(re) { + var flags = ''; + if (re.global) flags += 'g'; + if (re.ignoreCase) flags += 'i'; + if (re.multiline) flags += 'm'; + return flags; +}; +clone.__getRegExpFlags = __getRegExpFlags; - (async () => { - try { - await action; - spinner.succeed(); - } catch (_) { - spinner.fail(); - } - })(); +return clone; +})(); - return spinner; -}; +if ( true && module.exports) { + module.exports = clone; +} /***/ }), -/* 522 */ +/* 547 */ /***/ (function(module, exports) { -module.exports = require("readline"); +module.exports = [ + [ 0x0300, 0x036F ], [ 0x0483, 0x0486 ], [ 0x0488, 0x0489 ], + [ 0x0591, 0x05BD ], [ 0x05BF, 0x05BF ], [ 0x05C1, 0x05C2 ], + [ 0x05C4, 0x05C5 ], [ 0x05C7, 0x05C7 ], [ 0x0600, 0x0603 ], + [ 0x0610, 0x0615 ], [ 0x064B, 0x065E ], [ 0x0670, 0x0670 ], + [ 0x06D6, 0x06E4 ], [ 0x06E7, 0x06E8 ], [ 0x06EA, 0x06ED ], + [ 0x070F, 0x070F ], [ 0x0711, 0x0711 ], [ 0x0730, 0x074A ], + [ 0x07A6, 0x07B0 ], [ 0x07EB, 0x07F3 ], [ 0x0901, 0x0902 ], + [ 0x093C, 0x093C ], [ 0x0941, 0x0948 ], [ 0x094D, 0x094D ], + [ 0x0951, 0x0954 ], [ 0x0962, 0x0963 ], [ 0x0981, 0x0981 ], + [ 0x09BC, 0x09BC ], [ 0x09C1, 0x09C4 ], [ 0x09CD, 0x09CD ], + [ 0x09E2, 0x09E3 ], [ 0x0A01, 0x0A02 ], [ 0x0A3C, 0x0A3C ], + [ 0x0A41, 0x0A42 ], [ 0x0A47, 0x0A48 ], [ 0x0A4B, 0x0A4D ], + [ 0x0A70, 0x0A71 ], [ 0x0A81, 0x0A82 ], [ 0x0ABC, 0x0ABC ], + [ 0x0AC1, 0x0AC5 ], [ 0x0AC7, 0x0AC8 ], [ 0x0ACD, 0x0ACD ], + [ 0x0AE2, 0x0AE3 ], [ 0x0B01, 0x0B01 ], [ 0x0B3C, 0x0B3C ], + [ 0x0B3F, 0x0B3F ], [ 0x0B41, 0x0B43 ], [ 0x0B4D, 0x0B4D ], + [ 0x0B56, 0x0B56 ], [ 0x0B82, 0x0B82 ], [ 0x0BC0, 0x0BC0 ], + [ 0x0BCD, 0x0BCD ], [ 0x0C3E, 0x0C40 ], [ 0x0C46, 0x0C48 ], + [ 0x0C4A, 0x0C4D ], [ 0x0C55, 0x0C56 ], [ 0x0CBC, 0x0CBC ], + [ 0x0CBF, 0x0CBF ], [ 0x0CC6, 0x0CC6 ], [ 0x0CCC, 0x0CCD ], + [ 0x0CE2, 0x0CE3 ], [ 0x0D41, 0x0D43 ], [ 0x0D4D, 0x0D4D ], + [ 0x0DCA, 0x0DCA ], [ 0x0DD2, 0x0DD4 ], [ 0x0DD6, 0x0DD6 ], + [ 0x0E31, 0x0E31 ], [ 0x0E34, 0x0E3A ], [ 0x0E47, 0x0E4E ], + [ 0x0EB1, 0x0EB1 ], [ 0x0EB4, 0x0EB9 ], [ 0x0EBB, 0x0EBC ], + [ 0x0EC8, 0x0ECD ], [ 0x0F18, 0x0F19 ], [ 0x0F35, 0x0F35 ], + [ 0x0F37, 0x0F37 ], [ 0x0F39, 0x0F39 ], [ 0x0F71, 0x0F7E ], + [ 0x0F80, 0x0F84 ], [ 0x0F86, 0x0F87 ], [ 0x0F90, 0x0F97 ], + [ 0x0F99, 0x0FBC ], [ 0x0FC6, 0x0FC6 ], [ 0x102D, 0x1030 ], + [ 0x1032, 0x1032 ], [ 0x1036, 0x1037 ], [ 0x1039, 0x1039 ], + [ 0x1058, 0x1059 ], [ 0x1160, 0x11FF ], [ 0x135F, 0x135F ], + [ 0x1712, 0x1714 ], [ 0x1732, 0x1734 ], [ 0x1752, 0x1753 ], + [ 0x1772, 0x1773 ], [ 0x17B4, 0x17B5 ], [ 0x17B7, 0x17BD ], + [ 0x17C6, 0x17C6 ], [ 0x17C9, 0x17D3 ], [ 0x17DD, 0x17DD ], + [ 0x180B, 0x180D ], [ 0x18A9, 0x18A9 ], [ 0x1920, 0x1922 ], + [ 0x1927, 0x1928 ], [ 0x1932, 0x1932 ], [ 0x1939, 0x193B ], + [ 0x1A17, 0x1A18 ], [ 0x1B00, 0x1B03 ], [ 0x1B34, 0x1B34 ], + [ 0x1B36, 0x1B3A ], [ 0x1B3C, 0x1B3C ], [ 0x1B42, 0x1B42 ], + [ 0x1B6B, 0x1B73 ], [ 0x1DC0, 0x1DCA ], [ 0x1DFE, 0x1DFF ], + [ 0x200B, 0x200F ], [ 0x202A, 0x202E ], [ 0x2060, 0x2063 ], + [ 0x206A, 0x206F ], [ 0x20D0, 0x20EF ], [ 0x302A, 0x302F ], + [ 0x3099, 0x309A ], [ 0xA806, 0xA806 ], [ 0xA80B, 0xA80B ], + [ 0xA825, 0xA826 ], [ 0xFB1E, 0xFB1E ], [ 0xFE00, 0xFE0F ], + [ 0xFE20, 0xFE23 ], [ 0xFEFF, 0xFEFF ], [ 0xFFF9, 0xFFFB ], + [ 0x10A01, 0x10A03 ], [ 0x10A05, 0x10A06 ], [ 0x10A0C, 0x10A0F ], + [ 0x10A38, 0x10A3A ], [ 0x10A3F, 0x10A3F ], [ 0x1D167, 0x1D169 ], + [ 0x1D173, 0x1D182 ], [ 0x1D185, 0x1D18B ], [ 0x1D1AA, 0x1D1AD ], + [ 0x1D242, 0x1D244 ], [ 0xE0001, 0xE0001 ], [ 0xE0020, 0xE007F ], + [ 0xE0100, 0xE01EF ] +] + /***/ }), -/* 523 */ +/* 548 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const ansiStyles = __webpack_require__(115); -const {stdout: stdoutColor, stderr: stderrColor} = __webpack_require__(121); -const { - stringReplaceAll, - stringEncaseCRLFWithFirstIndex -} = __webpack_require__(524); - -// `supportsColor.level` → `ansiStyles.color[name]` mapping -const levelMapping = [ - 'ansi', - 'ansi', - 'ansi256', - 'ansi16m' -]; - -const styles = Object.create(null); - -const applyOptions = (object, options = {}) => { - if (options.level > 3 || options.level < 0) { - throw new Error('The `level` option should be an integer from 0 to 3'); - } - // Detect level if not set manually - const colorLevel = stdoutColor ? stdoutColor.level : 0; - object.level = options.level === undefined ? colorLevel : options.level; +module.exports = ({stream = process.stdout} = {}) => { + return Boolean( + stream && stream.isTTY && + process.env.TERM !== 'dumb' && + !('CI' in process.env) + ); }; -class ChalkClass { - constructor(options) { - return chalkFactory(options); - } -} - -const chalkFactory = options => { - const chalk = {}; - applyOptions(chalk, options); - - chalk.template = (...arguments_) => chalkTag(chalk.template, ...arguments_); - - Object.setPrototypeOf(chalk, Chalk.prototype); - Object.setPrototypeOf(chalk.template, chalk); - chalk.template.constructor = () => { - throw new Error('`chalk.constructor()` is deprecated. Use `new chalk.Instance()` instead.'); - }; +/***/ }), +/* 549 */ +/***/ (function(module, exports, __webpack_require__) { - chalk.template.Instance = ChalkClass; +var Stream = __webpack_require__(173) - return chalk.template; -}; +module.exports = MuteStream -function Chalk(options) { - return chalkFactory(options); -} +// var out = new MuteStream(process.stdout) +// argument auto-pipes +function MuteStream (opts) { + Stream.apply(this) + opts = opts || {} + this.writable = this.readable = true + this.muted = false + this.on('pipe', this._onpipe) + this.replace = opts.replace -for (const [styleName, style] of Object.entries(ansiStyles)) { - styles[styleName] = { - get() { - const builder = createBuilder(this, createStyler(style.open, style.close, this._styler), this._isEmpty); - Object.defineProperty(this, styleName, {value: builder}); - return builder; - } - }; + // For readline-type situations + // This much at the start of a line being redrawn after a ctrl char + // is seen (such as backspace) won't be redrawn as the replacement + this._prompt = opts.prompt || null + this._hadControl = false } -styles.visible = { - get() { - const builder = createBuilder(this, this._styler, true); - Object.defineProperty(this, 'visible', {value: builder}); - return builder; - } -}; +MuteStream.prototype = Object.create(Stream.prototype) -const usedModels = ['rgb', 'hex', 'keyword', 'hsl', 'hsv', 'hwb', 'ansi', 'ansi256']; +Object.defineProperty(MuteStream.prototype, 'constructor', { + value: MuteStream, + enumerable: false +}) -for (const model of usedModels) { - styles[model] = { - get() { - const {level} = this; - return function (...arguments_) { - const styler = createStyler(ansiStyles.color[levelMapping[level]][model](...arguments_), ansiStyles.color.close, this._styler); - return createBuilder(this, styler, this._isEmpty); - }; - } - }; +MuteStream.prototype.mute = function () { + this.muted = true } -for (const model of usedModels) { - const bgModel = 'bg' + model[0].toUpperCase() + model.slice(1); - styles[bgModel] = { - get() { - const {level} = this; - return function (...arguments_) { - const styler = createStyler(ansiStyles.bgColor[levelMapping[level]][model](...arguments_), ansiStyles.bgColor.close, this._styler); - return createBuilder(this, styler, this._isEmpty); - }; - } - }; +MuteStream.prototype.unmute = function () { + this.muted = false } -const proto = Object.defineProperties(() => {}, { - ...styles, - level: { - enumerable: true, - get() { - return this._generator.level; - }, - set(level) { - this._generator.level = level; - } - } -}); - -const createStyler = (open, close, parent) => { - let openAll; - let closeAll; - if (parent === undefined) { - openAll = open; - closeAll = close; - } else { - openAll = parent.openAll + open; - closeAll = close + parent.closeAll; - } - - return { - open, - close, - openAll, - closeAll, - parent - }; -}; - -const createBuilder = (self, _styler, _isEmpty) => { - const builder = (...arguments_) => { - // Single argument is hot path, implicit coercion is faster than anything - // eslint-disable-next-line no-implicit-coercion - return applyStyle(builder, (arguments_.length === 1) ? ('' + arguments_[0]) : arguments_.join(' ')); - }; - - // `__proto__` is used because we must return a function, but there is - // no way to create a function with a different prototype - builder.__proto__ = proto; // eslint-disable-line no-proto - - builder._generator = self; - builder._styler = _styler; - builder._isEmpty = _isEmpty; - - return builder; -}; - -const applyStyle = (self, string) => { - if (self.level <= 0 || !string) { - return self._isEmpty ? '' : string; - } - - let styler = self._styler; - - if (styler === undefined) { - return string; - } +Object.defineProperty(MuteStream.prototype, '_onpipe', { + value: onPipe, + enumerable: false, + writable: true, + configurable: true +}) - const {openAll, closeAll} = styler; - if (string.indexOf('\u001B') !== -1) { - while (styler !== undefined) { - // Replace any instances already present with a re-opening code - // otherwise only the part of the string until said closing code - // will be colored, and the rest will simply be 'plain'. - string = stringReplaceAll(string, styler.close, styler.open); +function onPipe (src) { + this._src = src +} - styler = styler.parent; - } - } +Object.defineProperty(MuteStream.prototype, 'isTTY', { + get: getIsTTY, + set: setIsTTY, + enumerable: true, + configurable: true +}) - // We can move both next actions out of loop, because remaining actions in loop won't have - // any/visible effect on parts we add here. Close the styling before a linebreak and reopen - // after next line to fix a bleed issue on macOS: https://github.com/chalk/chalk/pull/92 - const lfIndex = string.indexOf('\n'); - if (lfIndex !== -1) { - string = stringEncaseCRLFWithFirstIndex(string, closeAll, openAll, lfIndex); - } +function getIsTTY () { + return( (this._dest) ? this._dest.isTTY + : (this._src) ? this._src.isTTY + : false + ) +} - return openAll + string + closeAll; -}; +// basically just get replace the getter/setter with a regular value +function setIsTTY (isTTY) { + Object.defineProperty(this, 'isTTY', { + value: isTTY, + enumerable: true, + writable: true, + configurable: true + }) +} -let template; -const chalkTag = (chalk, ...strings) => { - const [firstString] = strings; +Object.defineProperty(MuteStream.prototype, 'rows', { + get: function () { + return( this._dest ? this._dest.rows + : this._src ? this._src.rows + : undefined ) + }, enumerable: true, configurable: true }) - if (!Array.isArray(firstString)) { - // If chalk() was called by itself or with a string, - // return the string itself as a string. - return strings.join(' '); - } +Object.defineProperty(MuteStream.prototype, 'columns', { + get: function () { + return( this._dest ? this._dest.columns + : this._src ? this._src.columns + : undefined ) + }, enumerable: true, configurable: true }) - const arguments_ = strings.slice(1); - const parts = [firstString.raw[0]]; - for (let i = 1; i < firstString.length; i++) { - parts.push( - String(arguments_[i - 1]).replace(/[{}\\]/g, '\\$&'), - String(firstString.raw[i]) - ); - } +MuteStream.prototype.pipe = function (dest, options) { + this._dest = dest + return Stream.prototype.pipe.call(this, dest, options) +} - if (template === undefined) { - template = __webpack_require__(525); - } +MuteStream.prototype.pause = function () { + if (this._src) return this._src.pause() +} - return template(chalk, parts.join('')); -}; +MuteStream.prototype.resume = function () { + if (this._src) return this._src.resume() +} -Object.defineProperties(Chalk.prototype, styles); +MuteStream.prototype.write = function (c) { + if (this.muted) { + if (!this.replace) return true + if (c.match(/^\u001b/)) { + if(c.indexOf(this._prompt) === 0) { + c = c.substr(this._prompt.length); + c = c.replace(/./g, this.replace); + c = this._prompt + c; + } + this._hadControl = true + return this.emit('data', c) + } else { + if (this._prompt && this._hadControl && + c.indexOf(this._prompt) === 0) { + this._hadControl = false + this.emit('data', this._prompt) + c = c.substr(this._prompt.length) + } + c = c.toString().replace(/./g, this.replace) + } + } + this.emit('data', c) +} -const chalk = Chalk(); // eslint-disable-line new-cap -chalk.supportsColor = stdoutColor; -chalk.stderr = Chalk({level: stderrColor ? stderrColor.level : 0}); // eslint-disable-line new-cap -chalk.stderr.supportsColor = stderrColor; +MuteStream.prototype.end = function (c) { + if (this.muted) { + if (c && this.replace) { + c = c.toString().replace(/./g, this.replace) + } else { + c = null + } + } + if (c) this.emit('data', c) + this.emit('end') +} -// For TypeScript -chalk.Level = { - None: 0, - Basic: 1, - Ansi256: 2, - TrueColor: 3, - 0: 'None', - 1: 'Basic', - 2: 'Ansi256', - 3: 'TrueColor' -}; +function proxy (fn) { return function () { + var d = this._dest + var s = this._src + if (d && d[fn]) d[fn].apply(d, arguments) + if (s && s[fn]) s[fn].apply(s, arguments) +}} -module.exports = chalk; +MuteStream.prototype.destroy = proxy('destroy') +MuteStream.prototype.destroySoon = proxy('destroySoon') +MuteStream.prototype.close = proxy('close') /***/ }), -/* 524 */ -/***/ (function(module, exports, __webpack_require__) { +/* 550 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ResetCommand", function() { return ResetCommand; }); +/* harmony import */ var dedent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2); +/* harmony import */ var dedent__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(dedent__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(240); +/* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(del__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var ora__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(527); +/* harmony import */ var ora__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(ora__WEBPACK_IMPORTED_MODULE_2__); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_3__); +/* harmony import */ var _utils_bazel__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(419); +/* harmony import */ var _utils_fs__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(231); +/* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(220); +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ -const stringReplaceAll = (string, substring, replacer) => { - let index = string.indexOf(substring); - if (index === -1) { - return string; - } - - const substringLength = substring.length; - let endIndex = 0; - let returnValue = ''; - do { - returnValue += string.substr(endIndex, index - endIndex) + substring + replacer; - endIndex = index + substringLength; - index = string.indexOf(substring, endIndex); - } while (index !== -1); - returnValue += string.substr(endIndex); - return returnValue; -}; -const stringEncaseCRLFWithFirstIndex = (string, prefix, postfix, index) => { - let endIndex = 0; - let returnValue = ''; - do { - const gotCR = string[index - 1] === '\r'; - returnValue += string.substr(endIndex, (gotCR ? index - 1 : index) - endIndex) + prefix + (gotCR ? '\r\n' : '\n') + postfix; - endIndex = index + 1; - index = string.indexOf('\n', endIndex); - } while (index !== -1); - returnValue += string.substr(endIndex); - return returnValue; -}; -module.exports = { - stringReplaceAll, - stringEncaseCRLFWithFirstIndex -}; +const ResetCommand = { + description: 'Deletes node_modules and output directories, resets internal and disk caches, and stops Bazel server', + name: 'reset', + reportTiming: { + group: 'scripts/kbn reset', + id: 'total' + }, -/***/ }), -/* 525 */ -/***/ (function(module, exports, __webpack_require__) { + async run(projects) { + _utils_log__WEBPACK_IMPORTED_MODULE_6__["log"].warning(dedent__WEBPACK_IMPORTED_MODULE_0___default.a` + In most cases, 'yarn kbn clean' is all that should be needed to recover a consistent state when + problems arise. If you need to use this command, please let us know, as it should not be necessary. + `); + const toDelete = []; -"use strict"; + for (const project of projects.values()) { + if (await Object(_utils_fs__WEBPACK_IMPORTED_MODULE_5__["isDirectory"])(project.nodeModulesLocation)) { + toDelete.push({ + cwd: project.path, + pattern: Object(path__WEBPACK_IMPORTED_MODULE_3__["relative"])(project.path, project.nodeModulesLocation) + }); + } -const TEMPLATE_REGEX = /(?:\\(u(?:[a-f\d]{4}|\{[a-f\d]{1,6}\})|x[a-f\d]{2}|.))|(?:\{(~)?(\w+(?:\([^)]*\))?(?:\.\w+(?:\([^)]*\))?)*)(?:[ \t]|(?=\r?\n)))|(\})|((?:.|[\r\n\f])+?)/gi; -const STYLE_REGEX = /(?:^|\.)(\w+)(?:\(([^)]*)\))?/g; -const STRING_REGEX = /^(['"])((?:\\.|(?!\1)[^\\])*)\1$/; -const ESCAPE_REGEX = /\\(u(?:[a-f\d]{4}|\{[a-f\d]{1,6}\})|x[a-f\d]{2}|.)|([^\\])/gi; + if (await Object(_utils_fs__WEBPACK_IMPORTED_MODULE_5__["isDirectory"])(project.targetLocation)) { + toDelete.push({ + cwd: project.path, + pattern: Object(path__WEBPACK_IMPORTED_MODULE_3__["relative"])(project.path, project.targetLocation) + }); + } -const ESCAPES = new Map([ - ['n', '\n'], - ['r', '\r'], - ['t', '\t'], - ['b', '\b'], - ['f', '\f'], - ['v', '\v'], - ['0', '\0'], - ['\\', '\\'], - ['e', '\u001B'], - ['a', '\u0007'] -]); + const { + extraPatterns + } = project.getCleanConfig(); -function unescape(c) { - const u = c[0] === 'u'; - const bracket = c[1] === '{'; + if (extraPatterns) { + toDelete.push({ + cwd: project.path, + pattern: extraPatterns + }); + } + } // Runs Bazel hard clean and deletes Bazel Cache Folders - if ((u && !bracket && c.length === 5) || (c[0] === 'x' && c.length === 3)) { - return String.fromCharCode(parseInt(c.slice(1), 16)); - } - if (u && bracket) { - return String.fromCodePoint(parseInt(c.slice(2, -1), 16)); - } + if (await Object(_utils_bazel__WEBPACK_IMPORTED_MODULE_4__["isBazelBinAvailable"])()) { + // Hard cleaning bazel + await Object(_utils_bazel__WEBPACK_IMPORTED_MODULE_4__["runBazel"])(['clean', '--expunge']); + _utils_log__WEBPACK_IMPORTED_MODULE_6__["log"].success('Hard cleaned bazel'); // Deletes Bazel Cache Folders - return ESCAPES.get(c) || c; -} + await del__WEBPACK_IMPORTED_MODULE_1___default()([await Object(_utils_bazel__WEBPACK_IMPORTED_MODULE_4__["getBazelDiskCacheFolder"])(), await Object(_utils_bazel__WEBPACK_IMPORTED_MODULE_4__["getBazelRepositoryCacheFolder"])()], { + force: true + }); + _utils_log__WEBPACK_IMPORTED_MODULE_6__["log"].success('Removed disk caches'); + } -function parseArguments(name, arguments_) { - const results = []; - const chunks = arguments_.trim().split(/\s*,\s*/g); - let matches; + if (toDelete.length === 0) { + return; + } + /** + * In order to avoid patterns like `/build` in packages from accidentally + * impacting files outside the package we use `process.chdir()` to change + * the cwd to the package and execute `del()` without the `force` option + * so it will check that each file being deleted is within the package. + * + * `del()` does support a `cwd` option, but it's only for resolving the + * patterns and does not impact the cwd check. + */ - for (const chunk of chunks) { - const number = Number(chunk); - if (!Number.isNaN(number)) { - results.push(number); - } else if ((matches = chunk.match(STRING_REGEX))) { - results.push(matches[2].replace(ESCAPE_REGEX, (m, escape, character) => escape ? unescape(escape) : character)); - } else { - throw new Error(`Invalid Chalk template style argument: ${chunk} (in style '${name}')`); - } - } - return results; -} + const originalCwd = process.cwd(); -function parseStyle(style) { - STYLE_REGEX.lastIndex = 0; + try { + for (const { + pattern, + cwd + } of toDelete) { + process.chdir(cwd); + const promise = del__WEBPACK_IMPORTED_MODULE_1___default()(pattern); - const results = []; - let matches; + if (_utils_log__WEBPACK_IMPORTED_MODULE_6__["log"].wouldLogLevel('info')) { + ora__WEBPACK_IMPORTED_MODULE_2___default.a.promise(promise, Object(path__WEBPACK_IMPORTED_MODULE_3__["relative"])(originalCwd, Object(path__WEBPACK_IMPORTED_MODULE_3__["join"])(cwd, String(pattern)))); + } - while ((matches = STYLE_REGEX.exec(style)) !== null) { - const name = matches[1]; + await promise; + } + } finally { + process.chdir(originalCwd); + } + } - if (matches[2]) { - const args = parseArguments(name, matches[2]); - results.push([name].concat(args)); - } else { - results.push([name]); - } - } +}; - return results; -} +/***/ }), +/* 551 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { -function buildStyle(chalk, styles) { - const enabled = {}; +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RunCommand", function() { return RunCommand; }); +/* harmony import */ var dedent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2); +/* harmony import */ var dedent__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(dedent__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(347); +/* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(220); +/* harmony import */ var _utils_parallelize__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(552); +/* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(346); +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ - for (const layer of styles) { - for (const style of layer.styles) { - enabled[style[0]] = layer.inverse ? null : style.slice(1); - } - } - let current = chalk; - for (const [styleName, styles] of Object.entries(enabled)) { - if (!Array.isArray(styles)) { - continue; - } - if (!(styleName in current)) { - throw new Error(`Unknown Chalk style: ${styleName}`); - } - current = styles.length > 0 ? current[styleName](...styles) : current[styleName]; - } - return current; -} +const RunCommand = { + description: 'Run script defined in package.json in each package that contains that script (only works on packages not using Bazel yet)', + name: 'run', + reportTiming: { + group: 'scripts/kbn run', + id: 'total' + }, -module.exports = (chalk, temporary) => { - const styles = []; - const chunks = []; - let chunk = []; + async run(projects, projectGraph, { + extraArgs, + options + }) { + _utils_log__WEBPACK_IMPORTED_MODULE_2__["log"].warning(dedent__WEBPACK_IMPORTED_MODULE_0___default.a` + We are migrating packages into the Bazel build system and we will no longer support running npm scripts on + packages using 'yarn kbn run' on Bazel built packages. If the package you are trying to act on contains a + BUILD.bazel file please just use 'yarn kbn build' to build it or 'yarn kbn watch' to watch it + `); + const batchedProjects = Object(_utils_projects__WEBPACK_IMPORTED_MODULE_4__["topologicallyBatchProjects"])(projects, projectGraph); - // eslint-disable-next-line max-params - temporary.replace(TEMPLATE_REGEX, (m, escapeCharacter, inverse, style, close, character) => { - if (escapeCharacter) { - chunk.push(unescape(escapeCharacter)); - } else if (style) { - const string = chunk.join(''); - chunk = []; - chunks.push(styles.length === 0 ? string : buildStyle(chalk, styles)(string)); - styles.push({inverse, styles: parseStyle(style)}); - } else if (close) { - if (styles.length === 0) { - throw new Error('Found extraneous } in Chalk template literal'); - } + if (extraArgs.length === 0) { + throw new _utils_errors__WEBPACK_IMPORTED_MODULE_1__["CliError"]('No script specified'); + } - chunks.push(buildStyle(chalk, styles)(chunk.join(''))); - chunk = []; - styles.pop(); - } else { - chunk.push(character); - } - }); + const scriptName = extraArgs[0]; + const scriptArgs = extraArgs.slice(1); + await Object(_utils_parallelize__WEBPACK_IMPORTED_MODULE_3__["parallelizeBatches"])(batchedProjects, async project => { + if (!project.hasScript(scriptName)) { + if (!!options['skip-missing']) { + return; + } - chunks.push(chunk.join('')); + throw new _utils_errors__WEBPACK_IMPORTED_MODULE_1__["CliError"](`[${project.name}] no "${scriptName}" script defined. To skip packages without the "${scriptName}" script pass --skip-missing`); + } - if (styles.length > 0) { - const errMsg = `Chalk template literal is missing ${styles.length} closing bracket${styles.length === 1 ? '' : 's'} (\`}\`)`; - throw new Error(errMsg); - } + _utils_log__WEBPACK_IMPORTED_MODULE_2__["log"].info(`[${project.name}] running "${scriptName}" script`); + await project.runScriptStreaming(scriptName, { + args: scriptArgs + }); + _utils_log__WEBPACK_IMPORTED_MODULE_2__["log"].success(`[${project.name}] complete`); + }); + } - return chunks.join(''); }; - /***/ }), -/* 526 */ -/***/ (function(module, exports, __webpack_require__) { +/* 552 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "parallelizeBatches", function() { return parallelizeBatches; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "parallelize", function() { return parallelize; }); +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +async function parallelizeBatches(batches, fn) { + for (const batch of batches) { + // We need to make sure the entire batch has completed before we can move on + // to the next batch + await parallelize(batch, fn); + } +} +async function parallelize(items, fn, concurrency = 4) { + if (items.length === 0) { + return; + } -const restoreCursor = __webpack_require__(527); - -let isHidden = false; - -exports.show = (writableStream = process.stderr) => { - if (!writableStream.isTTY) { - return; - } - - isHidden = false; - writableStream.write('\u001B[?25h'); -}; - -exports.hide = (writableStream = process.stderr) => { - if (!writableStream.isTTY) { - return; - } + return new Promise((resolve, reject) => { + let activePromises = 0; + const values = items.slice(0); - restoreCursor(); - isHidden = true; - writableStream.write('\u001B[?25l'); -}; + async function scheduleItem(item) { + activePromises++; -exports.toggle = (force, writableStream) => { - if (force !== undefined) { - isHidden = force; - } + try { + await fn(item); + activePromises--; - if (isHidden) { - exports.show(writableStream); - } else { - exports.hide(writableStream); - } -}; + if (values.length > 0) { + // We have more work to do, so we schedule the next promise + scheduleItem(values.shift()); + } else if (activePromises === 0) { + // We have no more values left, and all items have completed, so we've + // completed all the work. + resolve(); + } + } catch (error) { + reject(error); + } + } + values.splice(0, concurrency).map(scheduleItem); + }); +} /***/ }), -/* 527 */ -/***/ (function(module, exports, __webpack_require__) { +/* 553 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "WatchCommand", function() { return WatchCommand; }); +/* harmony import */ var _utils_bazel__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(419); +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ -const onetime = __webpack_require__(152); -const signalExit = __webpack_require__(161); +const WatchCommand = { + description: 'Runs a build in the Bazel built packages and keeps watching them for changes', + name: 'watch', + reportTiming: { + group: 'scripts/kbn watch', + id: 'total' + }, -module.exports = onetime(() => { - signalExit(() => { - process.stderr.write('\u001B[?25h'); - }, {alwaysLast: true}); -}); + async run(projects, projectGraph, { + options + }) { + const runOffline = (options === null || options === void 0 ? void 0 : options.offline) === true; // Call bazel with the target to build all available packages and run it through iBazel to watch it for changes + // + // Note: --run_output=false arg will disable the iBazel notifications about gazelle and buildozer when running it + // Can also be solved by adding a root `.bazel_fix_commands.json` but its not needed at the moment + + await Object(_utils_bazel__WEBPACK_IMPORTED_MODULE_0__["runIBazel"])(['--run_output=false', 'build', '//packages:build', '--show_result=1'], runOffline); + } +}; /***/ }), -/* 528 */ -/***/ (function(module, exports, __webpack_require__) { +/* 554 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "runCommand", function() { return runCommand; }); +/* harmony import */ var _kbn_dev_utils_ci_stats_reporter__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(131); +/* harmony import */ var _kbn_dev_utils_ci_stats_reporter__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_kbn_dev_utils_ci_stats_reporter__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(347); +/* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(220); +/* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(346); +/* harmony import */ var _utils_projects_tree__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(418); +/* harmony import */ var _utils_kibana__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(555); +function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } +function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } -const spinners = Object.assign({}, __webpack_require__(529)); +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } -const spinnersList = Object.keys(spinners); +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ -Object.defineProperty(spinners, 'random', { - get() { - const randomIndex = Math.floor(Math.random() * spinnersList.length); - const spinnerName = spinnersList[randomIndex]; - return spinners[spinnerName]; - } -}); -module.exports = spinners; -// TODO: Remove this for the next major release -module.exports.default = spinners; -/***/ }), -/* 529 */ -/***/ (function(module) { -module.exports = JSON.parse("{\"dots\":{\"interval\":80,\"frames\":[\"⠋\",\"⠙\",\"⠹\",\"⠸\",\"⠼\",\"⠴\",\"⠦\",\"⠧\",\"⠇\",\"⠏\"]},\"dots2\":{\"interval\":80,\"frames\":[\"⣾\",\"⣽\",\"⣻\",\"⢿\",\"⡿\",\"⣟\",\"⣯\",\"⣷\"]},\"dots3\":{\"interval\":80,\"frames\":[\"⠋\",\"⠙\",\"⠚\",\"⠞\",\"⠖\",\"⠦\",\"⠴\",\"⠲\",\"⠳\",\"⠓\"]},\"dots4\":{\"interval\":80,\"frames\":[\"⠄\",\"⠆\",\"⠇\",\"⠋\",\"⠙\",\"⠸\",\"⠰\",\"⠠\",\"⠰\",\"⠸\",\"⠙\",\"⠋\",\"⠇\",\"⠆\"]},\"dots5\":{\"interval\":80,\"frames\":[\"⠋\",\"⠙\",\"⠚\",\"⠒\",\"⠂\",\"⠂\",\"⠒\",\"⠲\",\"⠴\",\"⠦\",\"⠖\",\"⠒\",\"⠐\",\"⠐\",\"⠒\",\"⠓\",\"⠋\"]},\"dots6\":{\"interval\":80,\"frames\":[\"⠁\",\"⠉\",\"⠙\",\"⠚\",\"⠒\",\"⠂\",\"⠂\",\"⠒\",\"⠲\",\"⠴\",\"⠤\",\"⠄\",\"⠄\",\"⠤\",\"⠴\",\"⠲\",\"⠒\",\"⠂\",\"⠂\",\"⠒\",\"⠚\",\"⠙\",\"⠉\",\"⠁\"]},\"dots7\":{\"interval\":80,\"frames\":[\"⠈\",\"⠉\",\"⠋\",\"⠓\",\"⠒\",\"⠐\",\"⠐\",\"⠒\",\"⠖\",\"⠦\",\"⠤\",\"⠠\",\"⠠\",\"⠤\",\"⠦\",\"⠖\",\"⠒\",\"⠐\",\"⠐\",\"⠒\",\"⠓\",\"⠋\",\"⠉\",\"⠈\"]},\"dots8\":{\"interval\":80,\"frames\":[\"⠁\",\"⠁\",\"⠉\",\"⠙\",\"⠚\",\"⠒\",\"⠂\",\"⠂\",\"⠒\",\"⠲\",\"⠴\",\"⠤\",\"⠄\",\"⠄\",\"⠤\",\"⠠\",\"⠠\",\"⠤\",\"⠦\",\"⠖\",\"⠒\",\"⠐\",\"⠐\",\"⠒\",\"⠓\",\"⠋\",\"⠉\",\"⠈\",\"⠈\"]},\"dots9\":{\"interval\":80,\"frames\":[\"⢹\",\"⢺\",\"⢼\",\"⣸\",\"⣇\",\"⡧\",\"⡗\",\"⡏\"]},\"dots10\":{\"interval\":80,\"frames\":[\"⢄\",\"⢂\",\"⢁\",\"⡁\",\"⡈\",\"⡐\",\"⡠\"]},\"dots11\":{\"interval\":100,\"frames\":[\"⠁\",\"⠂\",\"⠄\",\"⡀\",\"⢀\",\"⠠\",\"⠐\",\"⠈\"]},\"dots12\":{\"interval\":80,\"frames\":[\"⢀⠀\",\"⡀⠀\",\"⠄⠀\",\"⢂⠀\",\"⡂⠀\",\"⠅⠀\",\"⢃⠀\",\"⡃⠀\",\"⠍⠀\",\"⢋⠀\",\"⡋⠀\",\"⠍⠁\",\"⢋⠁\",\"⡋⠁\",\"⠍⠉\",\"⠋⠉\",\"⠋⠉\",\"⠉⠙\",\"⠉⠙\",\"⠉⠩\",\"⠈⢙\",\"⠈⡙\",\"⢈⠩\",\"⡀⢙\",\"⠄⡙\",\"⢂⠩\",\"⡂⢘\",\"⠅⡘\",\"⢃⠨\",\"⡃⢐\",\"⠍⡐\",\"⢋⠠\",\"⡋⢀\",\"⠍⡁\",\"⢋⠁\",\"⡋⠁\",\"⠍⠉\",\"⠋⠉\",\"⠋⠉\",\"⠉⠙\",\"⠉⠙\",\"⠉⠩\",\"⠈⢙\",\"⠈⡙\",\"⠈⠩\",\"⠀⢙\",\"⠀⡙\",\"⠀⠩\",\"⠀⢘\",\"⠀⡘\",\"⠀⠨\",\"⠀⢐\",\"⠀⡐\",\"⠀⠠\",\"⠀⢀\",\"⠀⡀\"]},\"dots8Bit\":{\"interval\":80,\"frames\":[\"⠀\",\"⠁\",\"⠂\",\"⠃\",\"⠄\",\"⠅\",\"⠆\",\"⠇\",\"⡀\",\"⡁\",\"⡂\",\"⡃\",\"⡄\",\"⡅\",\"⡆\",\"⡇\",\"⠈\",\"⠉\",\"⠊\",\"⠋\",\"⠌\",\"⠍\",\"⠎\",\"⠏\",\"⡈\",\"⡉\",\"⡊\",\"⡋\",\"⡌\",\"⡍\",\"⡎\",\"⡏\",\"⠐\",\"⠑\",\"⠒\",\"⠓\",\"⠔\",\"⠕\",\"⠖\",\"⠗\",\"⡐\",\"⡑\",\"⡒\",\"⡓\",\"⡔\",\"⡕\",\"⡖\",\"⡗\",\"⠘\",\"⠙\",\"⠚\",\"⠛\",\"⠜\",\"⠝\",\"⠞\",\"⠟\",\"⡘\",\"⡙\",\"⡚\",\"⡛\",\"⡜\",\"⡝\",\"⡞\",\"⡟\",\"⠠\",\"⠡\",\"⠢\",\"⠣\",\"⠤\",\"⠥\",\"⠦\",\"⠧\",\"⡠\",\"⡡\",\"⡢\",\"⡣\",\"⡤\",\"⡥\",\"⡦\",\"⡧\",\"⠨\",\"⠩\",\"⠪\",\"⠫\",\"⠬\",\"⠭\",\"⠮\",\"⠯\",\"⡨\",\"⡩\",\"⡪\",\"⡫\",\"⡬\",\"⡭\",\"⡮\",\"⡯\",\"⠰\",\"⠱\",\"⠲\",\"⠳\",\"⠴\",\"⠵\",\"⠶\",\"⠷\",\"⡰\",\"⡱\",\"⡲\",\"⡳\",\"⡴\",\"⡵\",\"⡶\",\"⡷\",\"⠸\",\"⠹\",\"⠺\",\"⠻\",\"⠼\",\"⠽\",\"⠾\",\"⠿\",\"⡸\",\"⡹\",\"⡺\",\"⡻\",\"⡼\",\"⡽\",\"⡾\",\"⡿\",\"⢀\",\"⢁\",\"⢂\",\"⢃\",\"⢄\",\"⢅\",\"⢆\",\"⢇\",\"⣀\",\"⣁\",\"⣂\",\"⣃\",\"⣄\",\"⣅\",\"⣆\",\"⣇\",\"⢈\",\"⢉\",\"⢊\",\"⢋\",\"⢌\",\"⢍\",\"⢎\",\"⢏\",\"⣈\",\"⣉\",\"⣊\",\"⣋\",\"⣌\",\"⣍\",\"⣎\",\"⣏\",\"⢐\",\"⢑\",\"⢒\",\"⢓\",\"⢔\",\"⢕\",\"⢖\",\"⢗\",\"⣐\",\"⣑\",\"⣒\",\"⣓\",\"⣔\",\"⣕\",\"⣖\",\"⣗\",\"⢘\",\"⢙\",\"⢚\",\"⢛\",\"⢜\",\"⢝\",\"⢞\",\"⢟\",\"⣘\",\"⣙\",\"⣚\",\"⣛\",\"⣜\",\"⣝\",\"⣞\",\"⣟\",\"⢠\",\"⢡\",\"⢢\",\"⢣\",\"⢤\",\"⢥\",\"⢦\",\"⢧\",\"⣠\",\"⣡\",\"⣢\",\"⣣\",\"⣤\",\"⣥\",\"⣦\",\"⣧\",\"⢨\",\"⢩\",\"⢪\",\"⢫\",\"⢬\",\"⢭\",\"⢮\",\"⢯\",\"⣨\",\"⣩\",\"⣪\",\"⣫\",\"⣬\",\"⣭\",\"⣮\",\"⣯\",\"⢰\",\"⢱\",\"⢲\",\"⢳\",\"⢴\",\"⢵\",\"⢶\",\"⢷\",\"⣰\",\"⣱\",\"⣲\",\"⣳\",\"⣴\",\"⣵\",\"⣶\",\"⣷\",\"⢸\",\"⢹\",\"⢺\",\"⢻\",\"⢼\",\"⢽\",\"⢾\",\"⢿\",\"⣸\",\"⣹\",\"⣺\",\"⣻\",\"⣼\",\"⣽\",\"⣾\",\"⣿\"]},\"line\":{\"interval\":130,\"frames\":[\"-\",\"\\\\\",\"|\",\"/\"]},\"line2\":{\"interval\":100,\"frames\":[\"⠂\",\"-\",\"–\",\"—\",\"–\",\"-\"]},\"pipe\":{\"interval\":100,\"frames\":[\"┤\",\"┘\",\"┴\",\"└\",\"├\",\"┌\",\"┬\",\"┐\"]},\"simpleDots\":{\"interval\":400,\"frames\":[\". \",\".. \",\"...\",\" \"]},\"simpleDotsScrolling\":{\"interval\":200,\"frames\":[\". \",\".. \",\"...\",\" ..\",\" .\",\" \"]},\"star\":{\"interval\":70,\"frames\":[\"✶\",\"✸\",\"✹\",\"✺\",\"✹\",\"✷\"]},\"star2\":{\"interval\":80,\"frames\":[\"+\",\"x\",\"*\"]},\"flip\":{\"interval\":70,\"frames\":[\"_\",\"_\",\"_\",\"-\",\"`\",\"`\",\"'\",\"´\",\"-\",\"_\",\"_\",\"_\"]},\"hamburger\":{\"interval\":100,\"frames\":[\"☱\",\"☲\",\"☴\"]},\"growVertical\":{\"interval\":120,\"frames\":[\"▁\",\"▃\",\"▄\",\"▅\",\"▆\",\"▇\",\"▆\",\"▅\",\"▄\",\"▃\"]},\"growHorizontal\":{\"interval\":120,\"frames\":[\"▏\",\"▎\",\"▍\",\"▌\",\"▋\",\"▊\",\"▉\",\"▊\",\"▋\",\"▌\",\"▍\",\"▎\"]},\"balloon\":{\"interval\":140,\"frames\":[\" \",\".\",\"o\",\"O\",\"@\",\"*\",\" \"]},\"balloon2\":{\"interval\":120,\"frames\":[\".\",\"o\",\"O\",\"°\",\"O\",\"o\",\".\"]},\"noise\":{\"interval\":100,\"frames\":[\"▓\",\"▒\",\"░\"]},\"bounce\":{\"interval\":120,\"frames\":[\"⠁\",\"⠂\",\"⠄\",\"⠂\"]},\"boxBounce\":{\"interval\":120,\"frames\":[\"▖\",\"▘\",\"▝\",\"▗\"]},\"boxBounce2\":{\"interval\":100,\"frames\":[\"▌\",\"▀\",\"▐\",\"▄\"]},\"triangle\":{\"interval\":50,\"frames\":[\"◢\",\"◣\",\"◤\",\"◥\"]},\"arc\":{\"interval\":100,\"frames\":[\"◜\",\"◠\",\"◝\",\"◞\",\"◡\",\"◟\"]},\"circle\":{\"interval\":120,\"frames\":[\"◡\",\"⊙\",\"◠\"]},\"squareCorners\":{\"interval\":180,\"frames\":[\"◰\",\"◳\",\"◲\",\"◱\"]},\"circleQuarters\":{\"interval\":120,\"frames\":[\"◴\",\"◷\",\"◶\",\"◵\"]},\"circleHalves\":{\"interval\":50,\"frames\":[\"◐\",\"◓\",\"◑\",\"◒\"]},\"squish\":{\"interval\":100,\"frames\":[\"╫\",\"╪\"]},\"toggle\":{\"interval\":250,\"frames\":[\"⊶\",\"⊷\"]},\"toggle2\":{\"interval\":80,\"frames\":[\"▫\",\"▪\"]},\"toggle3\":{\"interval\":120,\"frames\":[\"□\",\"■\"]},\"toggle4\":{\"interval\":100,\"frames\":[\"■\",\"□\",\"▪\",\"▫\"]},\"toggle5\":{\"interval\":100,\"frames\":[\"▮\",\"▯\"]},\"toggle6\":{\"interval\":300,\"frames\":[\"ဝ\",\"၀\"]},\"toggle7\":{\"interval\":80,\"frames\":[\"⦾\",\"⦿\"]},\"toggle8\":{\"interval\":100,\"frames\":[\"◍\",\"◌\"]},\"toggle9\":{\"interval\":100,\"frames\":[\"◉\",\"◎\"]},\"toggle10\":{\"interval\":100,\"frames\":[\"㊂\",\"㊀\",\"㊁\"]},\"toggle11\":{\"interval\":50,\"frames\":[\"⧇\",\"⧆\"]},\"toggle12\":{\"interval\":120,\"frames\":[\"☗\",\"☖\"]},\"toggle13\":{\"interval\":80,\"frames\":[\"=\",\"*\",\"-\"]},\"arrow\":{\"interval\":100,\"frames\":[\"←\",\"↖\",\"↑\",\"↗\",\"→\",\"↘\",\"↓\",\"↙\"]},\"arrow2\":{\"interval\":80,\"frames\":[\"⬆️ \",\"↗️ \",\"➡️ \",\"↘️ \",\"⬇️ \",\"↙️ \",\"⬅️ \",\"↖️ \"]},\"arrow3\":{\"interval\":120,\"frames\":[\"▹▹▹▹▹\",\"▸▹▹▹▹\",\"▹▸▹▹▹\",\"▹▹▸▹▹\",\"▹▹▹▸▹\",\"▹▹▹▹▸\"]},\"bouncingBar\":{\"interval\":80,\"frames\":[\"[ ]\",\"[= ]\",\"[== ]\",\"[=== ]\",\"[ ===]\",\"[ ==]\",\"[ =]\",\"[ ]\",\"[ =]\",\"[ ==]\",\"[ ===]\",\"[====]\",\"[=== ]\",\"[== ]\",\"[= ]\"]},\"bouncingBall\":{\"interval\":80,\"frames\":[\"( ● )\",\"( ● )\",\"( ● )\",\"( ● )\",\"( ●)\",\"( ● )\",\"( ● )\",\"( ● )\",\"( ● )\",\"(● )\"]},\"smiley\":{\"interval\":200,\"frames\":[\"😄 \",\"😝 \"]},\"monkey\":{\"interval\":300,\"frames\":[\"🙈 \",\"🙈 \",\"🙉 \",\"🙊 \"]},\"hearts\":{\"interval\":100,\"frames\":[\"💛 \",\"💙 \",\"💜 \",\"💚 \",\"❤️ \"]},\"clock\":{\"interval\":100,\"frames\":[\"🕛 \",\"🕐 \",\"🕑 \",\"🕒 \",\"🕓 \",\"🕔 \",\"🕕 \",\"🕖 \",\"🕗 \",\"🕘 \",\"🕙 \",\"🕚 \"]},\"earth\":{\"interval\":180,\"frames\":[\"🌍 \",\"🌎 \",\"🌏 \"]},\"material\":{\"interval\":17,\"frames\":[\"█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"███▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"████▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"██████▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"██████▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"███████▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"████████▁▁▁▁▁▁▁▁▁▁▁▁\",\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\"██████████▁▁▁▁▁▁▁▁▁▁\",\"███████████▁▁▁▁▁▁▁▁▁\",\"█████████████▁▁▁▁▁▁▁\",\"██████████████▁▁▁▁▁▁\",\"██████████████▁▁▁▁▁▁\",\"▁██████████████▁▁▁▁▁\",\"▁██████████████▁▁▁▁▁\",\"▁██████████████▁▁▁▁▁\",\"▁▁██████████████▁▁▁▁\",\"▁▁▁██████████████▁▁▁\",\"▁▁▁▁█████████████▁▁▁\",\"▁▁▁▁██████████████▁▁\",\"▁▁▁▁██████████████▁▁\",\"▁▁▁▁▁██████████████▁\",\"▁▁▁▁▁██████████████▁\",\"▁▁▁▁▁██████████████▁\",\"▁▁▁▁▁▁██████████████\",\"▁▁▁▁▁▁██████████████\",\"▁▁▁▁▁▁▁█████████████\",\"▁▁▁▁▁▁▁█████████████\",\"▁▁▁▁▁▁▁▁████████████\",\"▁▁▁▁▁▁▁▁████████████\",\"▁▁▁▁▁▁▁▁▁███████████\",\"▁▁▁▁▁▁▁▁▁███████████\",\"▁▁▁▁▁▁▁▁▁▁██████████\",\"▁▁▁▁▁▁▁▁▁▁██████████\",\"▁▁▁▁▁▁▁▁▁▁▁▁████████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁███████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁██████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█████\",\"█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████\",\"██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███\",\"██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███\",\"███▁▁▁▁▁▁▁▁▁▁▁▁▁▁███\",\"████▁▁▁▁▁▁▁▁▁▁▁▁▁▁██\",\"█████▁▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\"█████▁▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\"██████▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\"████████▁▁▁▁▁▁▁▁▁▁▁▁\",\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\"███████████▁▁▁▁▁▁▁▁▁\",\"████████████▁▁▁▁▁▁▁▁\",\"████████████▁▁▁▁▁▁▁▁\",\"██████████████▁▁▁▁▁▁\",\"██████████████▁▁▁▁▁▁\",\"▁██████████████▁▁▁▁▁\",\"▁██████████████▁▁▁▁▁\",\"▁▁▁█████████████▁▁▁▁\",\"▁▁▁▁▁████████████▁▁▁\",\"▁▁▁▁▁████████████▁▁▁\",\"▁▁▁▁▁▁███████████▁▁▁\",\"▁▁▁▁▁▁▁▁█████████▁▁▁\",\"▁▁▁▁▁▁▁▁█████████▁▁▁\",\"▁▁▁▁▁▁▁▁▁█████████▁▁\",\"▁▁▁▁▁▁▁▁▁█████████▁▁\",\"▁▁▁▁▁▁▁▁▁▁█████████▁\",\"▁▁▁▁▁▁▁▁▁▁▁████████▁\",\"▁▁▁▁▁▁▁▁▁▁▁████████▁\",\"▁▁▁▁▁▁▁▁▁▁▁▁███████▁\",\"▁▁▁▁▁▁▁▁▁▁▁▁███████▁\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁███████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁███████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁██\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁██\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁██\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\"]},\"moon\":{\"interval\":80,\"frames\":[\"🌑 \",\"🌒 \",\"🌓 \",\"🌔 \",\"🌕 \",\"🌖 \",\"🌗 \",\"🌘 \"]},\"runner\":{\"interval\":140,\"frames\":[\"🚶 \",\"🏃 \"]},\"pong\":{\"interval\":80,\"frames\":[\"▐⠂ ▌\",\"▐⠈ ▌\",\"▐ ⠂ ▌\",\"▐ ⠠ ▌\",\"▐ ⡀ ▌\",\"▐ ⠠ ▌\",\"▐ ⠂ ▌\",\"▐ ⠈ ▌\",\"▐ ⠂ ▌\",\"▐ ⠠ ▌\",\"▐ ⡀ ▌\",\"▐ ⠠ ▌\",\"▐ ⠂ ▌\",\"▐ ⠈ ▌\",\"▐ ⠂▌\",\"▐ ⠠▌\",\"▐ ⡀▌\",\"▐ ⠠ ▌\",\"▐ ⠂ ▌\",\"▐ ⠈ ▌\",\"▐ ⠂ ▌\",\"▐ ⠠ ▌\",\"▐ ⡀ ▌\",\"▐ ⠠ ▌\",\"▐ ⠂ ▌\",\"▐ ⠈ ▌\",\"▐ ⠂ ▌\",\"▐ ⠠ ▌\",\"▐ ⡀ ▌\",\"▐⠠ ▌\"]},\"shark\":{\"interval\":120,\"frames\":[\"▐|\\\\____________▌\",\"▐_|\\\\___________▌\",\"▐__|\\\\__________▌\",\"▐___|\\\\_________▌\",\"▐____|\\\\________▌\",\"▐_____|\\\\_______▌\",\"▐______|\\\\______▌\",\"▐_______|\\\\_____▌\",\"▐________|\\\\____▌\",\"▐_________|\\\\___▌\",\"▐__________|\\\\__▌\",\"▐___________|\\\\_▌\",\"▐____________|\\\\▌\",\"▐____________/|▌\",\"▐___________/|_▌\",\"▐__________/|__▌\",\"▐_________/|___▌\",\"▐________/|____▌\",\"▐_______/|_____▌\",\"▐______/|______▌\",\"▐_____/|_______▌\",\"▐____/|________▌\",\"▐___/|_________▌\",\"▐__/|__________▌\",\"▐_/|___________▌\",\"▐/|____________▌\"]},\"dqpb\":{\"interval\":100,\"frames\":[\"d\",\"q\",\"p\",\"b\"]},\"weather\":{\"interval\":100,\"frames\":[\"☀️ \",\"☀️ \",\"☀️ \",\"🌤 \",\"⛅️ \",\"🌥 \",\"☁️ \",\"🌧 \",\"🌨 \",\"🌧 \",\"🌨 \",\"🌧 \",\"🌨 \",\"⛈ \",\"🌨 \",\"🌧 \",\"🌨 \",\"☁️ \",\"🌥 \",\"⛅️ \",\"🌤 \",\"☀️ \",\"☀️ \"]},\"christmas\":{\"interval\":400,\"frames\":[\"🌲\",\"🎄\"]},\"grenade\":{\"interval\":80,\"frames\":[\"، \",\"′ \",\" ´ \",\" ‾ \",\" ⸌\",\" ⸊\",\" |\",\" ⁎\",\" ⁕\",\" ෴ \",\" ⁓\",\" \",\" \",\" \"]},\"point\":{\"interval\":125,\"frames\":[\"∙∙∙\",\"●∙∙\",\"∙●∙\",\"∙∙●\",\"∙∙∙\"]},\"layer\":{\"interval\":150,\"frames\":[\"-\",\"=\",\"≡\"]},\"betaWave\":{\"interval\":80,\"frames\":[\"ρββββββ\",\"βρβββββ\",\"ββρββββ\",\"βββρβββ\",\"ββββρββ\",\"βββββρβ\",\"ββββββρ\"]},\"aesthetic\":{\"interval\":80,\"frames\":[\"▰▱▱▱▱▱▱\",\"▰▰▱▱▱▱▱\",\"▰▰▰▱▱▱▱\",\"▰▰▰▰▱▱▱\",\"▰▰▰▰▰▱▱\",\"▰▰▰▰▰▰▱\",\"▰▰▰▰▰▰▰\",\"▰▱▱▱▱▱▱\"]}}"); -/***/ }), -/* 530 */ -/***/ (function(module, exports, __webpack_require__) { +process.env.CI_STATS_NESTED_TIMING = 'true'; +async function runCommand(command, config) { + const runStartTime = Date.now(); + let kbn; -"use strict"; + try { + _utils_log__WEBPACK_IMPORTED_MODULE_2__["log"].debug(`Running [${command.name}] command from [${config.rootPath}]`); + kbn = await _utils_kibana__WEBPACK_IMPORTED_MODULE_5__["Kibana"].loadFrom(config.rootPath); + const projects = kbn.getFilteredProjects({ + skipKibanaPlugins: Boolean(config.options['skip-kibana-plugins']), + ossOnly: Boolean(config.options.oss), + exclude: toArray(config.options.exclude), + include: toArray(config.options.include) + }); + + if (projects.size === 0) { + _utils_log__WEBPACK_IMPORTED_MODULE_2__["log"].error(`There are no projects found. Double check project name(s) in '-i/--include' and '-e/--exclude' filters.`); + return process.exit(1); + } + + const projectGraph = Object(_utils_projects__WEBPACK_IMPORTED_MODULE_3__["buildProjectGraph"])(projects); + _utils_log__WEBPACK_IMPORTED_MODULE_2__["log"].debug(`Found ${projects.size.toString()} projects`); + _utils_log__WEBPACK_IMPORTED_MODULE_2__["log"].debug(Object(_utils_projects_tree__WEBPACK_IMPORTED_MODULE_4__["renderProjectsTree"])(config.rootPath, projects)); + await command.run(projects, projectGraph, _objectSpread(_objectSpread({}, config), {}, { + kbn + })); + + if (command.reportTiming) { + 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: true + } + }] + }); + } + } catch (error) { + if (command.reportTiming) { + // if we don't have a kbn object then things are too broken to report on + if (kbn) { + 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); + } + } + } -const chalk = __webpack_require__(531); + _utils_log__WEBPACK_IMPORTED_MODULE_2__["log"].error(`[${command.name}] failed:`); -const isSupported = process.platform !== 'win32' || process.env.CI || process.env.TERM === 'xterm-256color'; + if (error instanceof _utils_errors__WEBPACK_IMPORTED_MODULE_1__["CliError"]) { + _utils_log__WEBPACK_IMPORTED_MODULE_2__["log"].error(error.message); + const metaOutput = Object.entries(error.meta).map(([key, value]) => `${key}: ${value}`).join('\n'); -const main = { - info: chalk.blue('ℹ'), - success: chalk.green('✔'), - warning: chalk.yellow('⚠'), - error: chalk.red('✖') -}; + if (metaOutput) { + _utils_log__WEBPACK_IMPORTED_MODULE_2__["log"].info('Additional debugging info:\n'); + _utils_log__WEBPACK_IMPORTED_MODULE_2__["log"].indent(2); + _utils_log__WEBPACK_IMPORTED_MODULE_2__["log"].info(metaOutput); + _utils_log__WEBPACK_IMPORTED_MODULE_2__["log"].indent(-2); + } + } else { + _utils_log__WEBPACK_IMPORTED_MODULE_2__["log"].error(error); + } -const fallbacks = { - info: chalk.blue('i'), - success: chalk.green('√'), - warning: chalk.yellow('‼'), - error: chalk.red('×') -}; + process.exit(1); + } +} -module.exports = isSupported ? main : fallbacks; +function toArray(value) { + if (value == null) { + return []; + } + return Array.isArray(value) ? value : [value]; +} /***/ }), -/* 531 */ -/***/ (function(module, exports, __webpack_require__) { +/* 555 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Kibana", function() { return Kibana; }); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(132); +/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(fs__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var multimatch__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(556); +/* harmony import */ var multimatch__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(multimatch__WEBPACK_IMPORTED_MODULE_2__); +/* harmony import */ var is_path_inside__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(339); +/* harmony import */ var is_path_inside__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(is_path_inside__WEBPACK_IMPORTED_MODULE_3__); +/* harmony import */ var _yarn_lock__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(414); +/* harmony import */ var _projects__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(346); +/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(559); +function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } -const escapeStringRegexp = __webpack_require__(357); -const ansiStyles = __webpack_require__(532); -const stdoutColor = __webpack_require__(533).stdout; - -const template = __webpack_require__(535); - -const isSimpleWindowsTerm = process.platform === 'win32' && !(process.env.TERM || '').toLowerCase().startsWith('xterm'); - -// `supportsColor.level` → `ansiStyles.color[name]` mapping -const levelMapping = ['ansi', 'ansi', 'ansi256', 'ansi16m']; +function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } -// `color-convert` models to exclude from the Chalk API due to conflicts and such -const skipModels = new Set(['gray']); +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } -const styles = Object.create(null); +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ -function applyOptions(obj, options) { - options = options || {}; - // Detect level if not set manually - const scLevel = stdoutColor ? stdoutColor.level : 0; - obj.level = options.level === undefined ? scLevel : options.level; - obj.enabled = 'enabled' in options ? options.enabled : obj.level > 0; -} -function Chalk(options) { - // We check for this.template here since calling `chalk.constructor()` - // by itself will have a `this` of a previously constructed chalk object - if (!this || !(this instanceof Chalk) || this.template) { - const chalk = {}; - applyOptions(chalk, options); - chalk.template = function () { - const args = [].slice.call(arguments); - return chalkTag.apply(null, [chalk.template].concat(args)); - }; - Object.setPrototypeOf(chalk, Chalk.prototype); - Object.setPrototypeOf(chalk.template, chalk); - chalk.template.constructor = Chalk; - return chalk.template; - } +/** + * Helper class for dealing with a set of projects as children of + * the Kibana project. The kbn/pm is currently implemented to be + * more generic, where everything is an operation of generic projects, + * but that leads to exceptions where we need the kibana project and + * do things like `project.get('kibana')!`. + * + * Using this helper we can restructre the generic list of projects + * as a Kibana object which encapulates all the projects in the + * workspace and knows about the root Kibana project. + */ - applyOptions(this, options); -} +class Kibana { + static async loadFrom(rootPath) { + return new Kibana(await Object(_projects__WEBPACK_IMPORTED_MODULE_5__["getProjects"])(rootPath, Object(_config__WEBPACK_IMPORTED_MODULE_6__["getProjectPaths"])({ + rootPath + }))); + } -// Use bright blue on Windows as the normal blue color is illegible -if (isSimpleWindowsTerm) { - ansiStyles.blue.open = '\u001B[94m'; -} + constructor(allWorkspaceProjects) { + this.allWorkspaceProjects = allWorkspaceProjects; -for (const key of Object.keys(ansiStyles)) { - ansiStyles[key].closeRe = new RegExp(escapeStringRegexp(ansiStyles[key].close), 'g'); + _defineProperty(this, "kibanaProject", void 0); - styles[key] = { - get() { - const codes = ansiStyles[key]; - return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, key); - } - }; -} + const kibanaProject = allWorkspaceProjects.get('kibana'); -styles.visible = { - get() { - return build.call(this, this._styles || [], true, 'visible'); - } -}; + if (!kibanaProject) { + throw new TypeError('Unable to create Kibana object without all projects, including the Kibana project.'); + } -ansiStyles.color.closeRe = new RegExp(escapeStringRegexp(ansiStyles.color.close), 'g'); -for (const model of Object.keys(ansiStyles.color.ansi)) { - if (skipModels.has(model)) { - continue; - } + this.kibanaProject = kibanaProject; + } + /** make an absolute path by resolving subPath relative to the kibana repo */ - styles[model] = { - get() { - const level = this.level; - return function () { - const open = ansiStyles.color[levelMapping[level]][model].apply(null, arguments); - const codes = { - open, - close: ansiStyles.color.close, - closeRe: ansiStyles.color.closeRe - }; - return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, model); - }; - } - }; -} -ansiStyles.bgColor.closeRe = new RegExp(escapeStringRegexp(ansiStyles.bgColor.close), 'g'); -for (const model of Object.keys(ansiStyles.bgColor.ansi)) { - if (skipModels.has(model)) { - continue; - } + getAbsolute(...subPath) { + return path__WEBPACK_IMPORTED_MODULE_0___default.a.resolve(this.kibanaProject.path, ...subPath); + } + /** convert an absolute path to a relative path, relative to the kibana repo */ - const bgModel = 'bg' + model[0].toUpperCase() + model.slice(1); - styles[bgModel] = { - get() { - const level = this.level; - return function () { - const open = ansiStyles.bgColor[levelMapping[level]][model].apply(null, arguments); - const codes = { - open, - close: ansiStyles.bgColor.close, - closeRe: ansiStyles.bgColor.closeRe - }; - return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, model); - }; - } - }; -} -const proto = Object.defineProperties(() => {}, styles); + getRelative(absolute) { + return path__WEBPACK_IMPORTED_MODULE_0___default.a.relative(this.kibanaProject.path, absolute); + } + /** get a copy of the map of all projects in the kibana workspace */ -function build(_styles, _empty, key) { - const builder = function () { - return applyStyle.apply(builder, arguments); - }; - builder._styles = _styles; - builder._empty = _empty; + getAllProjects() { + return new Map(this.allWorkspaceProjects); + } + /** determine if a project with the given name exists */ - const self = this; - Object.defineProperty(builder, 'level', { - enumerable: true, - get() { - return self.level; - }, - set(level) { - self.level = level; - } - }); + hasProject(name) { + return this.allWorkspaceProjects.has(name); + } + /** get a specific project, throws if the name is not known (use hasProject() first) */ - Object.defineProperty(builder, 'enabled', { - enumerable: true, - get() { - return self.enabled; - }, - set(enabled) { - self.enabled = enabled; - } - }); - // See below for fix regarding invisible grey/dim combination on Windows - builder.hasGrey = this.hasGrey || key === 'gray' || key === 'grey'; + getProject(name) { + const project = this.allWorkspaceProjects.get(name); - // `__proto__` is used because we must return a function, but there is - // no way to create a function with a different prototype - builder.__proto__ = proto; // eslint-disable-line no-proto + if (!project) { + throw new Error(`No package with name "${name}" in the workspace`); + } - return builder; -} + return project; + } + /** get a project and all of the projects it depends on in a ProjectMap */ -function applyStyle() { - // Support varags, but simply cast to string in case there's only one arg - const args = arguments; - const argsLen = args.length; - let str = String(arguments[0]); - if (argsLen === 0) { - return ''; - } + getProjectAndDeps(name) { + const project = this.getProject(name); + return Object(_projects__WEBPACK_IMPORTED_MODULE_5__["includeTransitiveProjects"])([project], this.allWorkspaceProjects); + } + /** filter the projects to just those matching certain paths/include/exclude tags */ - if (argsLen > 1) { - // Don't slice `arguments`, it prevents V8 optimizations - for (let a = 1; a < argsLen; a++) { - str += ' ' + args[a]; - } - } - if (!this.enabled || this.level <= 0 || !str) { - return this._empty ? '' : str; - } + getFilteredProjects(options) { + const allProjects = this.getAllProjects(); + const filteredProjects = new Map(); + const pkgJsonPaths = Array.from(allProjects.values()).map(p => p.packageJsonLocation); + const filteredPkgJsonGlobs = Object(_config__WEBPACK_IMPORTED_MODULE_6__["getProjectPaths"])(_objectSpread(_objectSpread({}, options), {}, { + rootPath: this.kibanaProject.path + })).map(g => path__WEBPACK_IMPORTED_MODULE_0___default.a.resolve(g, 'package.json')); + const matchingPkgJsonPaths = multimatch__WEBPACK_IMPORTED_MODULE_2___default()(pkgJsonPaths, filteredPkgJsonGlobs); - // Turns out that on Windows dimmed gray text becomes invisible in cmd.exe, - // see https://github.com/chalk/chalk/issues/58 - // If we're on Windows and we're dealing with a gray color, temporarily make 'dim' a noop. - const originalDim = ansiStyles.dim.open; - if (isSimpleWindowsTerm && this.hasGrey) { - ansiStyles.dim.open = ''; - } + for (const project of allProjects.values()) { + const pathMatches = matchingPkgJsonPaths.includes(project.packageJsonLocation); + const notExcluded = !options.exclude.includes(project.name); + const isIncluded = !options.include.length || options.include.includes(project.name); - for (const code of this._styles.slice().reverse()) { - // Replace any instances already present with a re-opening code - // otherwise only the part of the string until said closing code - // will be colored, and the rest will simply be 'plain'. - str = code.open + str.replace(code.closeRe, code.open) + code.close; + if (pathMatches && notExcluded && isIncluded) { + filteredProjects.set(project.name, project); + } + } - // Close the styling before a linebreak and reopen - // after next line to fix a bleed issue on macOS - // https://github.com/chalk/chalk/pull/92 - str = str.replace(/\r?\n/g, `${code.close}$&${code.open}`); - } + return filteredProjects; + } - // Reset the original `dim` if we changed it to work around the Windows dimmed gray issue - ansiStyles.dim.open = originalDim; + isPartOfRepo(project) { + return project.path === this.kibanaProject.path || is_path_inside__WEBPACK_IMPORTED_MODULE_3___default()(project.path, this.kibanaProject.path); + } - return str; -} + isOutsideRepo(project) { + return !this.isPartOfRepo(project); + } -function chalkTag(chalk, strings) { - if (!Array.isArray(strings)) { - // If chalk() was called by itself or with a string, - // return the string itself as a string. - return [].slice.call(arguments, 1).join(' '); - } + resolveAllProductionDependencies(yarnLock, log) { + const kibanaDeps = Object(_yarn_lock__WEBPACK_IMPORTED_MODULE_4__["resolveDepsForProject"])({ + project: this.kibanaProject, + yarnLock, + kbn: this, + includeDependentProject: true, + productionDepsOnly: true, + log + }); + const xpackDeps = Object(_yarn_lock__WEBPACK_IMPORTED_MODULE_4__["resolveDepsForProject"])({ + project: this.getProject('x-pack'), + yarnLock, + kbn: this, + includeDependentProject: true, + productionDepsOnly: true, + log + }); + return new Map([...kibanaDeps.entries(), ...xpackDeps.entries()]); + } - const args = [].slice.call(arguments, 2); - const parts = [strings.raw[0]]; + getUuid() { + try { + return fs__WEBPACK_IMPORTED_MODULE_1___default.a.readFileSync(this.getAbsolute('data/uuid'), 'utf-8').trim(); + } catch (error) { + if (error.code === 'ENOENT') { + return undefined; + } - for (let i = 1; i < strings.length; i++) { - parts.push(String(args[i - 1]).replace(/[{}\\]/g, '\\$&')); - parts.push(String(strings.raw[i])); - } + throw error; + } + } - return template(chalk, parts.join('')); } -Object.defineProperties(Chalk.prototype, styles); - -module.exports = Chalk(); // eslint-disable-line new-cap -module.exports.supportsColor = stdoutColor; -module.exports.default = module.exports; // For TypeScript - - /***/ }), -/* 532 */ +/* 556 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -/* WEBPACK VAR INJECTION */(function(module) { -const colorConvert = __webpack_require__(359); -const wrapAnsi16 = (fn, offset) => function () { - const code = fn.apply(colorConvert, arguments); - return `\u001B[${code + offset}m`; -}; - -const wrapAnsi256 = (fn, offset) => function () { - const code = fn.apply(colorConvert, arguments); - return `\u001B[${38 + offset};5;${code}m`; -}; +const minimatch = __webpack_require__(247); +const arrayUnion = __webpack_require__(242); +const arrayDiffer = __webpack_require__(557); +const arrify = __webpack_require__(558); -const wrapAnsi16m = (fn, offset) => function () { - const rgb = fn.apply(colorConvert, arguments); - return `\u001B[${38 + offset};2;${rgb[0]};${rgb[1]};${rgb[2]}m`; -}; +module.exports = (list, patterns, options = {}) => { + list = arrify(list); + patterns = arrify(patterns); -function assembleStyles() { - const codes = new Map(); - const styles = { - modifier: { - reset: [0, 0], - // 21 isn't widely supported and 22 does the same thing - bold: [1, 22], - dim: [2, 22], - italic: [3, 23], - underline: [4, 24], - inverse: [7, 27], - hidden: [8, 28], - strikethrough: [9, 29] - }, - color: { - black: [30, 39], - red: [31, 39], - green: [32, 39], - yellow: [33, 39], - blue: [34, 39], - magenta: [35, 39], - cyan: [36, 39], - white: [37, 39], - gray: [90, 39], + if (list.length === 0 || patterns.length === 0) { + return []; + } - // Bright color - redBright: [91, 39], - greenBright: [92, 39], - yellowBright: [93, 39], - blueBright: [94, 39], - magentaBright: [95, 39], - cyanBright: [96, 39], - whiteBright: [97, 39] - }, - bgColor: { - bgBlack: [40, 49], - bgRed: [41, 49], - bgGreen: [42, 49], - bgYellow: [43, 49], - bgBlue: [44, 49], - bgMagenta: [45, 49], - bgCyan: [46, 49], - bgWhite: [47, 49], + return patterns.reduce((result, pattern) => { + let process = arrayUnion; - // Bright color - bgBlackBright: [100, 49], - bgRedBright: [101, 49], - bgGreenBright: [102, 49], - bgYellowBright: [103, 49], - bgBlueBright: [104, 49], - bgMagentaBright: [105, 49], - bgCyanBright: [106, 49], - bgWhiteBright: [107, 49] + if (pattern[0] === '!') { + pattern = pattern.slice(1); + process = arrayDiffer; } - }; - - // Fix humans - styles.color.grey = styles.color.gray; - - for (const groupName of Object.keys(styles)) { - const group = styles[groupName]; - for (const styleName of Object.keys(group)) { - const style = group[styleName]; - - styles[styleName] = { - open: `\u001B[${style[0]}m`, - close: `\u001B[${style[1]}m` - }; + return process(result, minimatch.match(list, pattern, options)); + }, []); +}; - group[styleName] = styles[styleName]; - codes.set(style[0], style[1]); - } +/***/ }), +/* 557 */ +/***/ (function(module, exports, __webpack_require__) { - Object.defineProperty(styles, groupName, { - value: group, - enumerable: false - }); +"use strict"; - Object.defineProperty(styles, 'codes', { - value: codes, - enumerable: false - }); - } - const ansi2ansi = n => n; - const rgb2rgb = (r, g, b) => [r, g, b]; +const arrayDiffer = (array, ...values) => { + const rest = new Set([].concat(...values)); + return array.filter(element => !rest.has(element)); +}; - styles.color.close = '\u001B[39m'; - styles.bgColor.close = '\u001B[49m'; +module.exports = arrayDiffer; - styles.color.ansi = { - ansi: wrapAnsi16(ansi2ansi, 0) - }; - styles.color.ansi256 = { - ansi256: wrapAnsi256(ansi2ansi, 0) - }; - styles.color.ansi16m = { - rgb: wrapAnsi16m(rgb2rgb, 0) - }; - styles.bgColor.ansi = { - ansi: wrapAnsi16(ansi2ansi, 10) - }; - styles.bgColor.ansi256 = { - ansi256: wrapAnsi256(ansi2ansi, 10) - }; - styles.bgColor.ansi16m = { - rgb: wrapAnsi16m(rgb2rgb, 10) - }; +/***/ }), +/* 558 */ +/***/ (function(module, exports, __webpack_require__) { - for (let key of Object.keys(colorConvert)) { - if (typeof colorConvert[key] !== 'object') { - continue; - } +"use strict"; - const suite = colorConvert[key]; - if (key === 'ansi16') { - key = 'ansi'; - } +const arrify = value => { + if (value === null || value === undefined) { + return []; + } - if ('ansi16' in suite) { - styles.color.ansi[key] = wrapAnsi16(suite.ansi16, 0); - styles.bgColor.ansi[key] = wrapAnsi16(suite.ansi16, 10); - } + if (Array.isArray(value)) { + return value; + } - if ('ansi256' in suite) { - styles.color.ansi256[key] = wrapAnsi256(suite.ansi256, 0); - styles.bgColor.ansi256[key] = wrapAnsi256(suite.ansi256, 10); - } + if (typeof value === 'string') { + return [value]; + } - if ('rgb' in suite) { - styles.color.ansi16m[key] = wrapAnsi16m(suite.rgb, 0); - styles.bgColor.ansi16m[key] = wrapAnsi16m(suite.rgb, 10); - } + if (typeof value[Symbol.iterator] === 'function') { + return [...value]; } - return styles; -} + return [value]; +}; -// Make the export immutable -Object.defineProperty(module, 'exports', { - enumerable: true, - get: assembleStyles -}); +module.exports = arrify; -/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(116)(module))) /***/ }), -/* 533 */ -/***/ (function(module, exports, __webpack_require__) { +/* 559 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getProjectPaths", function() { return getProjectPaths; }); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_0__); +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ -const os = __webpack_require__(122); -const hasFlag = __webpack_require__(534); -const env = process.env; +/** + * Returns all the paths where plugins are located + */ +function getProjectPaths({ + rootPath, + ossOnly, + skipKibanaPlugins +}) { + const projectPaths = [rootPath, Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'packages/*')]; // This is needed in order to install the dependencies for the declared + // plugin functional used in the selenium functional tests. + // As we are now using the webpack dll for the client vendors dependencies + // when we run the plugin functional tests against the distributable + // dependencies used by such plugins like @eui, react and react-dom can't + // be loaded from the dll as the context is different from the one declared + // into the webpack dll reference plugin. + // In anyway, have a plugin declaring their own dependencies is the + // correct and the expect behavior. -let forceColor; -if (hasFlag('no-color') || - hasFlag('no-colors') || - hasFlag('color=false')) { - forceColor = false; -} else if (hasFlag('color') || - hasFlag('colors') || - hasFlag('color=true') || - hasFlag('color=always')) { - forceColor = true; -} -if ('FORCE_COLOR' in env) { - forceColor = env.FORCE_COLOR.length === 0 || parseInt(env.FORCE_COLOR, 10) !== 0; -} + projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'test/plugin_functional/plugins/*')); + projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'test/interpreter_functional/plugins/*')); + projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'test/server_integration/__fixtures__/plugins/*')); + projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'examples/*')); -function translateLevel(level) { - if (level === 0) { - return false; - } + if (!ossOnly) { + projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'x-pack')); + projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'x-pack/plugins/*')); + projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'x-pack/legacy/plugins/*')); + projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'x-pack/test/functional_with_es_ssl/fixtures/plugins/*')); + } - return { - level, - hasBasic: true, - has256: level >= 2, - has16m: level >= 3 - }; + if (!skipKibanaPlugins) { + projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, '../kibana-extra/*')); + projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, '../kibana-extra/*/packages/*')); + projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, '../kibana-extra/*/plugins/*')); + projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'plugins/*')); + projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'plugins/*/packages/*')); + projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'plugins/*/plugins/*')); + } + + return projectPaths; } -function supportsColor(stream) { - if (forceColor === false) { - return 0; - } +/***/ }), +/* 560 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { - if (hasFlag('color=16m') || - hasFlag('color=full') || - hasFlag('color=truecolor')) { - return 3; - } +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony import */ var _build_bazel_production_projects__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(561); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildBazelProductionProjects", function() { return _build_bazel_production_projects__WEBPACK_IMPORTED_MODULE_0__["buildBazelProductionProjects"]; }); - if (hasFlag('color=256')) { - return 2; - } +/* harmony import */ var _build_non_bazel_production_projects__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(808); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildNonBazelProductionProjects", function() { return _build_non_bazel_production_projects__WEBPACK_IMPORTED_MODULE_1__["buildNonBazelProductionProjects"]; }); - if (stream && !stream.isTTY && forceColor !== true) { - return 0; - } +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ - const min = forceColor ? 1 : 0; - if (process.platform === 'win32') { - // Node.js 7.5.0 is the first version of Node.js to include a patch to - // libuv that enables 256 color output on Windows. Anything earlier and it - // won't work. However, here we target Node.js 8 at minimum as it is an LTS - // release, and Node.js 7 is not. Windows 10 build 10586 is the first Windows - // release that supports 256 colors. Windows 10 build 14931 is the first release - // that supports 16m/TrueColor. - const osRelease = os.release().split('.'); - if ( - Number(process.versions.node.split('.')[0]) >= 8 && - Number(osRelease[0]) >= 10 && - Number(osRelease[2]) >= 10586 - ) { - return Number(osRelease[2]) >= 14931 ? 3 : 2; - } - return 1; - } +/***/ }), +/* 561 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { - if ('CI' in env) { - if (['TRAVIS', 'CIRCLECI', 'APPVEYOR', 'GITLAB_CI'].some(sign => sign in env) || env.CI_NAME === 'codeship') { - return 1; - } +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "buildBazelProductionProjects", function() { return buildBazelProductionProjects; }); +/* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(562); +/* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(cpy__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var globby__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(774); +/* harmony import */ var globby__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(globby__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_2__); +/* harmony import */ var _build_non_bazel_production_projects__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(808); +/* harmony import */ var _utils_bazel__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(419); +/* harmony import */ var _utils_fs__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(231); +/* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(220); +/* harmony import */ var _utils_package_json__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(349); +/* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(346); +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ - return min; - } - if ('TEAMCITY_VERSION' in env) { - return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0; - } - if (env.COLORTERM === 'truecolor') { - return 3; - } - if ('TERM_PROGRAM' in env) { - const version = parseInt((env.TERM_PROGRAM_VERSION || '').split('.')[0], 10); - switch (env.TERM_PROGRAM) { - case 'iTerm.app': - return version >= 3 ? 3 : 2; - case 'Apple_Terminal': - return 2; - // No default - } - } - if (/-256(color)?$/i.test(env.TERM)) { - return 2; - } - if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) { - return 1; - } - if ('COLORTERM' in env) { - return 1; - } - if (env.TERM === 'dumb') { - return min; - } +async function buildBazelProductionProjects({ + kibanaRoot, + buildRoot, + onlyOSS +}) { + const projects = await Object(_utils_projects__WEBPACK_IMPORTED_MODULE_8__["getBazelProjectsOnly"])(await Object(_build_non_bazel_production_projects__WEBPACK_IMPORTED_MODULE_3__["getProductionProjects"])(kibanaRoot, onlyOSS)); + const projectNames = [...projects.values()].map(project => project.name); + _utils_log__WEBPACK_IMPORTED_MODULE_6__["log"].info(`Preparing Bazel projects production build for [${projectNames.join(', ')}]`); + await Object(_utils_bazel__WEBPACK_IMPORTED_MODULE_4__["runBazel"])(['build', '//packages:build']); + _utils_log__WEBPACK_IMPORTED_MODULE_6__["log"].info(`All Bazel projects production builds for [${projectNames.join(', ')}] are complete`); - return min; + for (const project of projects.values()) { + await copyToBuild(project, kibanaRoot, buildRoot); + await applyCorrectPermissions(project, kibanaRoot, buildRoot); + } } +/** + * Copy all the project's files from its Bazel dist directory into the + * project build folder. + * + * When copying all the files into the build, we exclude `node_modules` because + * we want the Kibana build to be responsible for actually installing all + * dependencies. The primary reason for allowing the Kibana build process to + * manage dependencies is that it will "dedupe" them, so we don't include + * unnecessary copies of dependencies. We also exclude every related Bazel build + * files in order to get the most cleaner package module we can in the final distributable. + */ -function getSupportLevel(stream) { - const level = supportsColor(stream); - return translateLevel(level); +async function copyToBuild(project, kibanaRoot, buildRoot) { + // We want the package to have the same relative location within the build + const relativeProjectPath = Object(path__WEBPACK_IMPORTED_MODULE_2__["relative"])(kibanaRoot, project.path); + const buildProjectPath = Object(path__WEBPACK_IMPORTED_MODULE_2__["resolve"])(buildRoot, relativeProjectPath); + await cpy__WEBPACK_IMPORTED_MODULE_0___default()(['**/*'], buildProjectPath, { + cwd: Object(path__WEBPACK_IMPORTED_MODULE_2__["join"])(kibanaRoot, 'bazel-bin', 'packages', Object(path__WEBPACK_IMPORTED_MODULE_2__["basename"])(buildProjectPath), 'npm_module'), + dot: true, + onlyFiles: true, + parents: true + }); // If a project is using an intermediate build directory, we special-case our + // handling of `package.json`, as the project build process might have copied + // (a potentially modified) `package.json` into the intermediate build + // directory already. If so, we want to use that `package.json` as the basis + // for creating the production-ready `package.json`. If it's not present in + // the intermediate build, we fall back to using the project's already defined + // `package.json`. + + const packageJson = (await Object(_utils_fs__WEBPACK_IMPORTED_MODULE_5__["isFile"])(Object(path__WEBPACK_IMPORTED_MODULE_2__["join"])(buildProjectPath, 'package.json'))) ? await Object(_utils_package_json__WEBPACK_IMPORTED_MODULE_7__["readPackageJson"])(buildProjectPath) : project.json; + const preparedPackageJson = Object(_utils_package_json__WEBPACK_IMPORTED_MODULE_7__["createProductionPackageJson"])(packageJson); + await Object(_utils_package_json__WEBPACK_IMPORTED_MODULE_7__["writePackageJson"])(buildProjectPath, preparedPackageJson); } -module.exports = { - supportsColor: getSupportLevel, - stdout: getSupportLevel(process.stdout), - stderr: getSupportLevel(process.stderr) -}; +async function applyCorrectPermissions(project, kibanaRoot, buildRoot) { + const relativeProjectPath = Object(path__WEBPACK_IMPORTED_MODULE_2__["relative"])(kibanaRoot, project.path); + const buildProjectPath = Object(path__WEBPACK_IMPORTED_MODULE_2__["resolve"])(buildRoot, relativeProjectPath); + const allPluginPaths = await globby__WEBPACK_IMPORTED_MODULE_1___default()([`**/*`], { + onlyFiles: false, + cwd: buildProjectPath, + dot: true + }); + for (const pluginPath of allPluginPaths) { + const resolvedPluginPath = Object(path__WEBPACK_IMPORTED_MODULE_2__["resolve"])(buildProjectPath, pluginPath); + + if (await Object(_utils_fs__WEBPACK_IMPORTED_MODULE_5__["isFile"])(resolvedPluginPath)) { + await Object(_utils_fs__WEBPACK_IMPORTED_MODULE_5__["chmod"])(resolvedPluginPath, 0o644); + } + + if (await Object(_utils_fs__WEBPACK_IMPORTED_MODULE_5__["isDirectory"])(resolvedPluginPath)) { + await Object(_utils_fs__WEBPACK_IMPORTED_MODULE_5__["chmod"])(resolvedPluginPath, 0o755); + } + } +} /***/ }), -/* 534 */ +/* 562 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -module.exports = (flag, argv) => { - argv = argv || process.argv; - const prefix = flag.startsWith('-') ? '' : (flag.length === 1 ? '-' : '--'); - const pos = argv.indexOf(prefix + flag); - const terminatorPos = argv.indexOf('--'); - return pos !== -1 && (terminatorPos === -1 ? true : pos < terminatorPos); +const EventEmitter = __webpack_require__(164); +const path = __webpack_require__(4); +const os = __webpack_require__(122); +const pMap = __webpack_require__(563); +const arrify = __webpack_require__(558); +const globby = __webpack_require__(566); +const hasGlob = __webpack_require__(758); +const cpFile = __webpack_require__(760); +const junk = __webpack_require__(770); +const pFilter = __webpack_require__(771); +const CpyError = __webpack_require__(773); + +const defaultOptions = { + ignoreJunk: true }; +class SourceFile { + constructor(relativePath, path) { + this.path = path; + this.relativePath = relativePath; + Object.freeze(this); + } -/***/ }), -/* 535 */ -/***/ (function(module, exports, __webpack_require__) { + get name() { + return path.basename(this.relativePath); + } -"use strict"; + get nameWithoutExtension() { + return path.basename(this.relativePath, path.extname(this.relativePath)); + } -const TEMPLATE_REGEX = /(?:\\(u[a-f\d]{4}|x[a-f\d]{2}|.))|(?:\{(~)?(\w+(?:\([^)]*\))?(?:\.\w+(?:\([^)]*\))?)*)(?:[ \t]|(?=\r?\n)))|(\})|((?:.|[\r\n\f])+?)/gi; -const STYLE_REGEX = /(?:^|\.)(\w+)(?:\(([^)]*)\))?/g; -const STRING_REGEX = /^(['"])((?:\\.|(?!\1)[^\\])*)\1$/; -const ESCAPE_REGEX = /\\(u[a-f\d]{4}|x[a-f\d]{2}|.)|([^\\])/gi; + get extension() { + return path.extname(this.relativePath).slice(1); + } +} -const ESCAPES = new Map([ - ['n', '\n'], - ['r', '\r'], - ['t', '\t'], - ['b', '\b'], - ['f', '\f'], - ['v', '\v'], - ['0', '\0'], - ['\\', '\\'], - ['e', '\u001B'], - ['a', '\u0007'] -]); +const preprocessSourcePath = (source, options) => path.resolve(options.cwd ? options.cwd : process.cwd(), source); -function unescape(c) { - if ((c[0] === 'u' && c.length === 5) || (c[0] === 'x' && c.length === 3)) { - return String.fromCharCode(parseInt(c.slice(1), 16)); - } +const preprocessDestinationPath = (source, destination, options) => { + let basename = path.basename(source); - return ESCAPES.get(c) || c; -} + if (typeof options.rename === 'string') { + basename = options.rename; + } else if (typeof options.rename === 'function') { + basename = options.rename(basename); + } -function parseArguments(name, args) { - const results = []; - const chunks = args.trim().split(/\s*,\s*/g); - let matches; + if (options.cwd) { + destination = path.resolve(options.cwd, destination); + } - for (const chunk of chunks) { - if (!isNaN(chunk)) { - results.push(Number(chunk)); - } else if ((matches = chunk.match(STRING_REGEX))) { - results.push(matches[2].replace(ESCAPE_REGEX, (m, escape, chr) => escape ? unescape(escape) : chr)); - } else { - throw new Error(`Invalid Chalk template style argument: ${chunk} (in style '${name}')`); - } + if (options.parents) { + const dirname = path.dirname(source); + const parsedDirectory = path.parse(dirname); + return path.join(destination, dirname.replace(parsedDirectory.root, path.sep), basename); } - return results; -} + return path.join(destination, basename); +}; -function parseStyle(style) { - STYLE_REGEX.lastIndex = 0; +module.exports = (source, destination, { + concurrency = (os.cpus().length || 1) * 2, + ...options +} = {}) => { + const progressEmitter = new EventEmitter(); - const results = []; - let matches; + options = { + ...defaultOptions, + ...options + }; - while ((matches = STYLE_REGEX.exec(style)) !== null) { - const name = matches[1]; + const promise = (async () => { + source = arrify(source); - if (matches[2]) { - const args = parseArguments(name, matches[2]); - results.push([name].concat(args)); - } else { - results.push([name]); + if (source.length === 0 || !destination) { + throw new CpyError('`source` and `destination` required'); } - } - return results; -} + const copyStatus = new Map(); + let completedFiles = 0; + let completedSize = 0; -function buildStyle(chalk, styles) { - const enabled = {}; + let files; + try { + files = await globby(source, options); - for (const layer of styles) { - for (const style of layer.styles) { - enabled[style[0]] = layer.inverse ? null : style.slice(1); + if (options.ignoreJunk) { + files = files.filter(file => junk.not(path.basename(file))); + } + } catch (error) { + throw new CpyError(`Cannot glob \`${source}\`: ${error.message}`, error); } - } - let current = chalk; - for (const styleName of Object.keys(enabled)) { - if (Array.isArray(enabled[styleName])) { - if (!(styleName in current)) { - throw new Error(`Unknown Chalk style: ${styleName}`); - } + if (files.length === 0 && !hasGlob(source)) { + throw new CpyError(`Cannot copy \`${source}\`: the file doesn't exist`); + } - if (enabled[styleName].length > 0) { - current = current[styleName].apply(current, enabled[styleName]); - } else { - current = current[styleName]; - } + let sources = files.map(sourcePath => new SourceFile(sourcePath, preprocessSourcePath(sourcePath, options))); + + if (options.filter !== undefined) { + const filteredSources = await pFilter(sources, options.filter, {concurrency: 1024}); + sources = filteredSources; } - } - return current; -} + if (sources.length === 0) { + progressEmitter.emit('progress', { + totalFiles: 0, + percent: 1, + completedFiles: 0, + completedSize: 0 + }); + } -module.exports = (chalk, tmp) => { - const styles = []; - const chunks = []; - let chunk = []; + const fileProgressHandler = event => { + const fileStatus = copyStatus.get(event.src) || {written: 0, percent: 0}; - // eslint-disable-next-line max-params - tmp.replace(TEMPLATE_REGEX, (m, escapeChar, inverse, style, close, chr) => { - if (escapeChar) { - chunk.push(unescape(escapeChar)); - } else if (style) { - const str = chunk.join(''); - chunk = []; - chunks.push(styles.length === 0 ? str : buildStyle(chalk, styles)(str)); - styles.push({inverse, styles: parseStyle(style)}); - } else if (close) { - if (styles.length === 0) { - throw new Error('Found extraneous } in Chalk template literal'); + if (fileStatus.written !== event.written || fileStatus.percent !== event.percent) { + completedSize -= fileStatus.written; + completedSize += event.written; + + if (event.percent === 1 && fileStatus.percent !== 1) { + completedFiles++; + } + + copyStatus.set(event.src, { + written: event.written, + percent: event.percent + }); + + progressEmitter.emit('progress', { + totalFiles: files.length, + percent: completedFiles / files.length, + completedFiles, + completedSize + }); } + }; - chunks.push(buildStyle(chalk, styles)(chunk.join(''))); - chunk = []; - styles.pop(); - } else { - chunk.push(chr); - } - }); + return pMap(sources, async source => { + const to = preprocessDestinationPath(source.relativePath, destination, options); - chunks.push(chunk.join('')); + try { + await cpFile(source.path, to, options).on('progress', fileProgressHandler); + } catch (error) { + throw new CpyError(`Cannot copy from \`${source.relativePath}\` to \`${to}\`: ${error.message}`, error); + } - if (styles.length > 0) { - const errMsg = `Chalk template literal is missing ${styles.length} closing bracket${styles.length === 1 ? '' : 's'} (\`}\`)`; - throw new Error(errMsg); - } + return to; + }, {concurrency}); + })(); - return chunks.join(''); + promise.on = (...arguments_) => { + progressEmitter.on(...arguments_); + return promise; + }; + + return promise; }; /***/ }), -/* 536 */ +/* 563 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const ansiRegex = __webpack_require__(537); +const AggregateError = __webpack_require__(564); -module.exports = string => typeof string === 'string' ? string.replace(ansiRegex(), '') : string; +module.exports = async ( + iterable, + mapper, + { + concurrency = Infinity, + stopOnError = true + } = {} +) => { + return new Promise((resolve, reject) => { + if (typeof mapper !== 'function') { + throw new TypeError('Mapper function is required'); + } + if (!(typeof concurrency === 'number' && concurrency >= 1)) { + throw new TypeError(`Expected \`concurrency\` to be a number from 1 and up, got \`${concurrency}\` (${typeof concurrency})`); + } -/***/ }), -/* 537 */ -/***/ (function(module, exports, __webpack_require__) { + const ret = []; + const errors = []; + const iterator = iterable[Symbol.iterator](); + let isRejected = false; + let isIterableDone = false; + let resolvingCount = 0; + let currentIndex = 0; -"use strict"; + const next = () => { + if (isRejected) { + return; + } + const nextItem = iterator.next(); + const i = currentIndex; + currentIndex++; -module.exports = ({onlyFirst = false} = {}) => { - const pattern = [ - '[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)', - '(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))' - ].join('|'); + if (nextItem.done) { + isIterableDone = true; - return new RegExp(pattern, onlyFirst ? undefined : 'g'); + if (resolvingCount === 0) { + if (!stopOnError && errors.length !== 0) { + reject(new AggregateError(errors)); + } else { + resolve(ret); + } + } + + return; + } + + resolvingCount++; + + (async () => { + try { + const element = await nextItem.value; + ret[i] = await mapper(element, i); + resolvingCount--; + next(); + } catch (error) { + if (stopOnError) { + isRejected = true; + reject(error); + } else { + errors.push(error); + resolvingCount--; + next(); + } + } + })(); + }; + + for (let i = 0; i < concurrency; i++) { + next(); + + if (isIterableDone) { + break; + } + } + }); }; /***/ }), -/* 538 */ +/* 564 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; +const indentString = __webpack_require__(565); +const cleanStack = __webpack_require__(344); -var defaults = __webpack_require__(539) -var combining = __webpack_require__(541) +const cleanInternalStack = stack => stack.replace(/\s+at .*aggregate-error\/index.js:\d+:\d+\)?/g, ''); -var DEFAULTS = { - nul: 0, - control: 0 -} +class AggregateError extends Error { + constructor(errors) { + if (!Array.isArray(errors)) { + throw new TypeError(`Expected input to be an Array, got ${typeof errors}`); + } -module.exports = function wcwidth(str) { - return wcswidth(str, DEFAULTS) -} + errors = [...errors].map(error => { + if (error instanceof Error) { + return error; + } -module.exports.config = function(opts) { - opts = defaults(opts || {}, DEFAULTS) - return function wcwidth(str) { - return wcswidth(str, opts) - } -} + if (error !== null && typeof error === 'object') { + // Handle plain error objects with message property and/or possibly other metadata + return Object.assign(new Error(error.message), error); + } -/* - * The following functions define the column width of an ISO 10646 - * character as follows: - * - The null character (U+0000) has a column width of 0. - * - Other C0/C1 control characters and DEL will lead to a return value - * of -1. - * - Non-spacing and enclosing combining characters (general category - * code Mn or Me in the - * Unicode database) have a column width of 0. - * - SOFT HYPHEN (U+00AD) has a column width of 1. - * - Other format characters (general category code Cf in the Unicode - * database) and ZERO WIDTH - * SPACE (U+200B) have a column width of 0. - * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF) - * have a column width of 0. - * - Spacing characters in the East Asian Wide (W) or East Asian - * Full-width (F) category as - * defined in Unicode Technical Report #11 have a column width of 2. - * - All remaining characters (including all printable ISO 8859-1 and - * WGL4 characters, Unicode control characters, etc.) have a column - * width of 1. - * This implementation assumes that characters are encoded in ISO 10646. -*/ + return new Error(error); + }); -function wcswidth(str, opts) { - if (typeof str !== 'string') return wcwidth(str, opts) + let message = errors + .map(error => { + // The `stack` property is not standardized, so we can't assume it exists + return typeof error.stack === 'string' ? cleanInternalStack(cleanStack(error.stack)) : String(error); + }) + .join('\n'); + message = '\n' + indentString(message, 4); + super(message); - var s = 0 - for (var i = 0; i < str.length; i++) { - var n = wcwidth(str.charCodeAt(i), opts) - if (n < 0) return -1 - s += n - } + this.name = 'AggregateError'; - return s -} + Object.defineProperty(this, '_errors', {value: errors}); + } -function wcwidth(ucs, opts) { - // test for 8-bit control characters - if (ucs === 0) return opts.nul - if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) return opts.control + * [Symbol.iterator]() { + for (const error of this._errors) { + yield error; + } + } +} - // binary search in table of non-spacing characters - if (bisearch(ucs)) return 0 +module.exports = AggregateError; - // if we arrive here, ucs is not a combining or C0/C1 control character - return 1 + - (ucs >= 0x1100 && - (ucs <= 0x115f || // Hangul Jamo init. consonants - ucs == 0x2329 || ucs == 0x232a || - (ucs >= 0x2e80 && ucs <= 0xa4cf && - ucs != 0x303f) || // CJK ... Yi - (ucs >= 0xac00 && ucs <= 0xd7a3) || // Hangul Syllables - (ucs >= 0xf900 && ucs <= 0xfaff) || // CJK Compatibility Ideographs - (ucs >= 0xfe10 && ucs <= 0xfe19) || // Vertical forms - (ucs >= 0xfe30 && ucs <= 0xfe6f) || // CJK Compatibility Forms - (ucs >= 0xff00 && ucs <= 0xff60) || // Fullwidth Forms - (ucs >= 0xffe0 && ucs <= 0xffe6) || - (ucs >= 0x20000 && ucs <= 0x2fffd) || - (ucs >= 0x30000 && ucs <= 0x3fffd))); -} -function bisearch(ucs) { - var min = 0 - var max = combining.length - 1 - var mid +/***/ }), +/* 565 */ +/***/ (function(module, exports, __webpack_require__) { - if (ucs < combining[0][0] || ucs > combining[max][1]) return false +"use strict"; - while (max >= min) { - mid = Math.floor((min + max) / 2) - if (ucs > combining[mid][1]) min = mid + 1 - else if (ucs < combining[mid][0]) max = mid - 1 - else return true - } - return false -} +module.exports = (string, count = 1, options) => { + options = { + indent: ' ', + includeEmptyLines: false, + ...options + }; + if (typeof string !== 'string') { + throw new TypeError( + `Expected \`input\` to be a \`string\`, got \`${typeof string}\`` + ); + } -/***/ }), -/* 539 */ -/***/ (function(module, exports, __webpack_require__) { + if (typeof count !== 'number') { + throw new TypeError( + `Expected \`count\` to be a \`number\`, got \`${typeof count}\`` + ); + } -var clone = __webpack_require__(540); + if (typeof options.indent !== 'string') { + throw new TypeError( + `Expected \`options.indent\` to be a \`string\`, got \`${typeof options.indent}\`` + ); + } -module.exports = function(options, defaults) { - options = options || {}; + if (count === 0) { + return string; + } - Object.keys(defaults).forEach(function(key) { - if (typeof options[key] === 'undefined') { - options[key] = clone(defaults[key]); - } - }); + const regex = options.includeEmptyLines ? /^/gm : /^(?!\s*$)/gm; - return options; + return string.replace(regex, options.indent.repeat(count)); }; + /***/ }), -/* 540 */ +/* 566 */ /***/ (function(module, exports, __webpack_require__) { -var clone = (function() { -'use strict'; - -/** - * Clones (copies) an Object using deep copying. - * - * This function supports circular references by default, but if you are certain - * there are no circular references in your object, you can save some CPU time - * by calling clone(obj, false). - * - * Caution: if `circular` is false and `parent` contains circular references, - * your program may enter an infinite loop and crash. - * - * @param `parent` - the object to be cloned - * @param `circular` - set to true if the object to be cloned may contain - * circular references. (optional - true by default) - * @param `depth` - set to a number if the object is only to be cloned to - * a particular depth. (optional - defaults to Infinity) - * @param `prototype` - sets the prototype to be used when cloning an object. - * (optional - defaults to parent prototype). -*/ -function clone(parent, circular, depth, prototype) { - var filter; - if (typeof circular === 'object') { - depth = circular.depth; - prototype = circular.prototype; - filter = circular.filter; - circular = circular.circular - } - // maintain two arrays for circular references, where corresponding parents - // and children have the same index - var allParents = []; - var allChildren = []; - - var useBuffer = typeof Buffer != 'undefined'; - - if (typeof circular == 'undefined') - circular = true; +"use strict"; - if (typeof depth == 'undefined') - depth = Infinity; +const fs = __webpack_require__(132); +const arrayUnion = __webpack_require__(567); +const glob = __webpack_require__(244); +const fastGlob = __webpack_require__(569); +const dirGlob = __webpack_require__(752); +const gitignore = __webpack_require__(755); - // recurse this function so we don't reset allParents and allChildren - function _clone(parent, depth) { - // cloning null always returns null - if (parent === null) - return null; +const DEFAULT_FILTER = () => false; - if (depth == 0) - return parent; +const isNegative = pattern => pattern[0] === '!'; - var child; - var proto; - if (typeof parent != 'object') { - return parent; - } +const assertPatternsInput = patterns => { + if (!patterns.every(x => typeof x === 'string')) { + throw new TypeError('Patterns must be a string or an array of strings'); + } +}; - if (clone.__isArray(parent)) { - child = []; - } else if (clone.__isRegExp(parent)) { - child = new RegExp(parent.source, __getRegExpFlags(parent)); - if (parent.lastIndex) child.lastIndex = parent.lastIndex; - } else if (clone.__isDate(parent)) { - child = new Date(parent.getTime()); - } else if (useBuffer && Buffer.isBuffer(parent)) { - if (Buffer.allocUnsafe) { - // Node.js >= 4.5.0 - child = Buffer.allocUnsafe(parent.length); - } else { - // Older Node.js versions - child = new Buffer(parent.length); - } - parent.copy(child); - return child; - } else { - if (typeof prototype == 'undefined') { - proto = Object.getPrototypeOf(parent); - child = Object.create(proto); - } - else { - child = Object.create(prototype); - proto = prototype; - } - } +const checkCwdOption = options => { + if (options && options.cwd && !fs.statSync(options.cwd).isDirectory()) { + throw new Error('The `cwd` option must be a path to a directory'); + } +}; - if (circular) { - var index = allParents.indexOf(parent); +const generateGlobTasks = (patterns, taskOptions) => { + patterns = arrayUnion([].concat(patterns)); + assertPatternsInput(patterns); + checkCwdOption(taskOptions); - if (index != -1) { - return allChildren[index]; - } - allParents.push(parent); - allChildren.push(child); - } + const globTasks = []; - for (var i in parent) { - var attrs; - if (proto) { - attrs = Object.getOwnPropertyDescriptor(proto, i); - } + taskOptions = Object.assign({ + ignore: [], + expandDirectories: true + }, taskOptions); - if (attrs && attrs.set == null) { - continue; - } - child[i] = _clone(parent[i], depth - 1); - } + patterns.forEach((pattern, i) => { + if (isNegative(pattern)) { + return; + } - return child; - } + const ignore = patterns + .slice(i) + .filter(isNegative) + .map(pattern => pattern.slice(1)); - return _clone(parent, depth); -} + const options = Object.assign({}, taskOptions, { + ignore: taskOptions.ignore.concat(ignore) + }); -/** - * Simple flat clone using prototype, accepts only objects, usefull for property - * override on FLAT configuration object (no nested props). - * - * USE WITH CAUTION! This may not behave as you wish if you do not know how this - * works. - */ -clone.clonePrototype = function clonePrototype(parent) { - if (parent === null) - return null; + globTasks.push({pattern, options}); + }); - var c = function () {}; - c.prototype = parent; - return new c(); + return globTasks; }; -// private utility functions +const globDirs = (task, fn) => { + let options = {}; + if (task.options.cwd) { + options.cwd = task.options.cwd; + } -function __objToStr(o) { - return Object.prototype.toString.call(o); -}; -clone.__objToStr = __objToStr; + if (Array.isArray(task.options.expandDirectories)) { + options = Object.assign(options, {files: task.options.expandDirectories}); + } else if (typeof task.options.expandDirectories === 'object') { + options = Object.assign(options, task.options.expandDirectories); + } -function __isDate(o) { - return typeof o === 'object' && __objToStr(o) === '[object Date]'; + return fn(task.pattern, options); }; -clone.__isDate = __isDate; -function __isArray(o) { - return typeof o === 'object' && __objToStr(o) === '[object Array]'; -}; -clone.__isArray = __isArray; +const getPattern = (task, fn) => task.options.expandDirectories ? globDirs(task, fn) : [task.pattern]; -function __isRegExp(o) { - return typeof o === 'object' && __objToStr(o) === '[object RegExp]'; -}; -clone.__isRegExp = __isRegExp; +const globToTask = task => glob => { + const {options} = task; + if (options.ignore && Array.isArray(options.ignore) && options.expandDirectories) { + options.ignore = dirGlob.sync(options.ignore); + } -function __getRegExpFlags(re) { - var flags = ''; - if (re.global) flags += 'g'; - if (re.ignoreCase) flags += 'i'; - if (re.multiline) flags += 'm'; - return flags; + return { + pattern: glob, + options + }; }; -clone.__getRegExpFlags = __getRegExpFlags; - -return clone; -})(); -if ( true && module.exports) { - module.exports = clone; -} +const globby = (patterns, options) => { + let globTasks; + try { + globTasks = generateGlobTasks(patterns, options); + } catch (error) { + return Promise.reject(error); + } -/***/ }), -/* 541 */ -/***/ (function(module, exports) { + const getTasks = Promise.all(globTasks.map(task => Promise.resolve(getPattern(task, dirGlob)) + .then(globs => Promise.all(globs.map(globToTask(task)))) + )) + .then(tasks => arrayUnion(...tasks)); -module.exports = [ - [ 0x0300, 0x036F ], [ 0x0483, 0x0486 ], [ 0x0488, 0x0489 ], - [ 0x0591, 0x05BD ], [ 0x05BF, 0x05BF ], [ 0x05C1, 0x05C2 ], - [ 0x05C4, 0x05C5 ], [ 0x05C7, 0x05C7 ], [ 0x0600, 0x0603 ], - [ 0x0610, 0x0615 ], [ 0x064B, 0x065E ], [ 0x0670, 0x0670 ], - [ 0x06D6, 0x06E4 ], [ 0x06E7, 0x06E8 ], [ 0x06EA, 0x06ED ], - [ 0x070F, 0x070F ], [ 0x0711, 0x0711 ], [ 0x0730, 0x074A ], - [ 0x07A6, 0x07B0 ], [ 0x07EB, 0x07F3 ], [ 0x0901, 0x0902 ], - [ 0x093C, 0x093C ], [ 0x0941, 0x0948 ], [ 0x094D, 0x094D ], - [ 0x0951, 0x0954 ], [ 0x0962, 0x0963 ], [ 0x0981, 0x0981 ], - [ 0x09BC, 0x09BC ], [ 0x09C1, 0x09C4 ], [ 0x09CD, 0x09CD ], - [ 0x09E2, 0x09E3 ], [ 0x0A01, 0x0A02 ], [ 0x0A3C, 0x0A3C ], - [ 0x0A41, 0x0A42 ], [ 0x0A47, 0x0A48 ], [ 0x0A4B, 0x0A4D ], - [ 0x0A70, 0x0A71 ], [ 0x0A81, 0x0A82 ], [ 0x0ABC, 0x0ABC ], - [ 0x0AC1, 0x0AC5 ], [ 0x0AC7, 0x0AC8 ], [ 0x0ACD, 0x0ACD ], - [ 0x0AE2, 0x0AE3 ], [ 0x0B01, 0x0B01 ], [ 0x0B3C, 0x0B3C ], - [ 0x0B3F, 0x0B3F ], [ 0x0B41, 0x0B43 ], [ 0x0B4D, 0x0B4D ], - [ 0x0B56, 0x0B56 ], [ 0x0B82, 0x0B82 ], [ 0x0BC0, 0x0BC0 ], - [ 0x0BCD, 0x0BCD ], [ 0x0C3E, 0x0C40 ], [ 0x0C46, 0x0C48 ], - [ 0x0C4A, 0x0C4D ], [ 0x0C55, 0x0C56 ], [ 0x0CBC, 0x0CBC ], - [ 0x0CBF, 0x0CBF ], [ 0x0CC6, 0x0CC6 ], [ 0x0CCC, 0x0CCD ], - [ 0x0CE2, 0x0CE3 ], [ 0x0D41, 0x0D43 ], [ 0x0D4D, 0x0D4D ], - [ 0x0DCA, 0x0DCA ], [ 0x0DD2, 0x0DD4 ], [ 0x0DD6, 0x0DD6 ], - [ 0x0E31, 0x0E31 ], [ 0x0E34, 0x0E3A ], [ 0x0E47, 0x0E4E ], - [ 0x0EB1, 0x0EB1 ], [ 0x0EB4, 0x0EB9 ], [ 0x0EBB, 0x0EBC ], - [ 0x0EC8, 0x0ECD ], [ 0x0F18, 0x0F19 ], [ 0x0F35, 0x0F35 ], - [ 0x0F37, 0x0F37 ], [ 0x0F39, 0x0F39 ], [ 0x0F71, 0x0F7E ], - [ 0x0F80, 0x0F84 ], [ 0x0F86, 0x0F87 ], [ 0x0F90, 0x0F97 ], - [ 0x0F99, 0x0FBC ], [ 0x0FC6, 0x0FC6 ], [ 0x102D, 0x1030 ], - [ 0x1032, 0x1032 ], [ 0x1036, 0x1037 ], [ 0x1039, 0x1039 ], - [ 0x1058, 0x1059 ], [ 0x1160, 0x11FF ], [ 0x135F, 0x135F ], - [ 0x1712, 0x1714 ], [ 0x1732, 0x1734 ], [ 0x1752, 0x1753 ], - [ 0x1772, 0x1773 ], [ 0x17B4, 0x17B5 ], [ 0x17B7, 0x17BD ], - [ 0x17C6, 0x17C6 ], [ 0x17C9, 0x17D3 ], [ 0x17DD, 0x17DD ], - [ 0x180B, 0x180D ], [ 0x18A9, 0x18A9 ], [ 0x1920, 0x1922 ], - [ 0x1927, 0x1928 ], [ 0x1932, 0x1932 ], [ 0x1939, 0x193B ], - [ 0x1A17, 0x1A18 ], [ 0x1B00, 0x1B03 ], [ 0x1B34, 0x1B34 ], - [ 0x1B36, 0x1B3A ], [ 0x1B3C, 0x1B3C ], [ 0x1B42, 0x1B42 ], - [ 0x1B6B, 0x1B73 ], [ 0x1DC0, 0x1DCA ], [ 0x1DFE, 0x1DFF ], - [ 0x200B, 0x200F ], [ 0x202A, 0x202E ], [ 0x2060, 0x2063 ], - [ 0x206A, 0x206F ], [ 0x20D0, 0x20EF ], [ 0x302A, 0x302F ], - [ 0x3099, 0x309A ], [ 0xA806, 0xA806 ], [ 0xA80B, 0xA80B ], - [ 0xA825, 0xA826 ], [ 0xFB1E, 0xFB1E ], [ 0xFE00, 0xFE0F ], - [ 0xFE20, 0xFE23 ], [ 0xFEFF, 0xFEFF ], [ 0xFFF9, 0xFFFB ], - [ 0x10A01, 0x10A03 ], [ 0x10A05, 0x10A06 ], [ 0x10A0C, 0x10A0F ], - [ 0x10A38, 0x10A3A ], [ 0x10A3F, 0x10A3F ], [ 0x1D167, 0x1D169 ], - [ 0x1D173, 0x1D182 ], [ 0x1D185, 0x1D18B ], [ 0x1D1AA, 0x1D1AD ], - [ 0x1D242, 0x1D244 ], [ 0xE0001, 0xE0001 ], [ 0xE0020, 0xE007F ], - [ 0xE0100, 0xE01EF ] -] + const getFilter = () => { + return Promise.resolve( + options && options.gitignore ? + gitignore({cwd: options.cwd, ignore: options.ignore}) : + DEFAULT_FILTER + ); + }; + + return getFilter() + .then(filter => { + return getTasks + .then(tasks => Promise.all(tasks.map(task => fastGlob(task.pattern, task.options)))) + .then(paths => arrayUnion(...paths)) + .then(paths => paths.filter(p => !filter(p))); + }); +}; + +module.exports = globby; +// TODO: Remove this for the next major release +module.exports.default = globby; + +module.exports.sync = (patterns, options) => { + const globTasks = generateGlobTasks(patterns, options); + + const getFilter = () => { + return options && options.gitignore ? + gitignore.sync({cwd: options.cwd, ignore: options.ignore}) : + DEFAULT_FILTER; + }; + + const tasks = globTasks.reduce((tasks, task) => { + const newTask = getPattern(task, dirGlob.sync).map(globToTask(task)); + return tasks.concat(newTask); + }, []); + + const filter = getFilter(); + return tasks.reduce( + (matches, task) => arrayUnion(matches, fastGlob.sync(task.pattern, task.options)), + [] + ).filter(p => !filter(p)); +}; + +module.exports.generateGlobTasks = generateGlobTasks; + +module.exports.hasMagic = (patterns, options) => [] + .concat(patterns) + .some(pattern => glob.hasMagic(pattern, options)); + +module.exports.gitignore = gitignore; /***/ }), -/* 542 */ +/* 567 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; +var arrayUniq = __webpack_require__(568); -module.exports = ({stream = process.stdout} = {}) => { - return Boolean( - stream && stream.isTTY && - process.env.TERM !== 'dumb' && - !('CI' in process.env) - ); +module.exports = function () { + return arrayUniq([].concat.apply([], arguments)); }; /***/ }), -/* 543 */ +/* 568 */ /***/ (function(module, exports, __webpack_require__) { -var Stream = __webpack_require__(173) - -module.exports = MuteStream +"use strict"; -// var out = new MuteStream(process.stdout) -// argument auto-pipes -function MuteStream (opts) { - Stream.apply(this) - opts = opts || {} - this.writable = this.readable = true - this.muted = false - this.on('pipe', this._onpipe) - this.replace = opts.replace - // For readline-type situations - // This much at the start of a line being redrawn after a ctrl char - // is seen (such as backspace) won't be redrawn as the replacement - this._prompt = opts.prompt || null - this._hadControl = false -} +// there's 3 implementations written in increasing order of efficiency -MuteStream.prototype = Object.create(Stream.prototype) +// 1 - no Set type is defined +function uniqNoSet(arr) { + var ret = []; -Object.defineProperty(MuteStream.prototype, 'constructor', { - value: MuteStream, - enumerable: false -}) + for (var i = 0; i < arr.length; i++) { + if (ret.indexOf(arr[i]) === -1) { + ret.push(arr[i]); + } + } -MuteStream.prototype.mute = function () { - this.muted = true + return ret; } -MuteStream.prototype.unmute = function () { - this.muted = false +// 2 - a simple Set type is defined +function uniqSet(arr) { + var seen = new Set(); + return arr.filter(function (el) { + if (!seen.has(el)) { + seen.add(el); + return true; + } + + return false; + }); } -Object.defineProperty(MuteStream.prototype, '_onpipe', { - value: onPipe, - enumerable: false, - writable: true, - configurable: true -}) +// 3 - a standard Set type is defined and it has a forEach method +function uniqSetWithForEach(arr) { + var ret = []; -function onPipe (src) { - this._src = src + (new Set(arr)).forEach(function (el) { + ret.push(el); + }); + + return ret; } -Object.defineProperty(MuteStream.prototype, 'isTTY', { - get: getIsTTY, - set: setIsTTY, - enumerable: true, - configurable: true -}) +// V8 currently has a broken implementation +// https://github.com/joyent/node/issues/8449 +function doesForEachActuallyWork() { + var ret = false; -function getIsTTY () { - return( (this._dest) ? this._dest.isTTY - : (this._src) ? this._src.isTTY - : false - ) + (new Set([true])).forEach(function (el) { + ret = el; + }); + + return ret === true; } -// basically just get replace the getter/setter with a regular value -function setIsTTY (isTTY) { - Object.defineProperty(this, 'isTTY', { - value: isTTY, - enumerable: true, - writable: true, - configurable: true - }) +if ('Set' in global) { + if (typeof Set.prototype.forEach === 'function' && doesForEachActuallyWork()) { + module.exports = uniqSetWithForEach; + } else { + module.exports = uniqSet; + } +} else { + module.exports = uniqNoSet; } -Object.defineProperty(MuteStream.prototype, 'rows', { - get: function () { - return( this._dest ? this._dest.rows - : this._src ? this._src.rows - : undefined ) - }, enumerable: true, configurable: true }) -Object.defineProperty(MuteStream.prototype, 'columns', { - get: function () { - return( this._dest ? this._dest.columns - : this._src ? this._src.columns - : undefined ) - }, enumerable: true, configurable: true }) +/***/ }), +/* 569 */ +/***/ (function(module, exports, __webpack_require__) { + +const pkg = __webpack_require__(570); + +module.exports = pkg.async; +module.exports.default = pkg.async; + +module.exports.async = pkg.async; +module.exports.sync = pkg.sync; +module.exports.stream = pkg.stream; + +module.exports.generateTasks = pkg.generateTasks; + + +/***/ }), +/* 570 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var optionsManager = __webpack_require__(571); +var taskManager = __webpack_require__(572); +var reader_async_1 = __webpack_require__(723); +var reader_stream_1 = __webpack_require__(747); +var reader_sync_1 = __webpack_require__(748); +var arrayUtils = __webpack_require__(750); +var streamUtils = __webpack_require__(751); +/** + * Synchronous API. + */ +function sync(source, opts) { + assertPatternsInput(source); + var works = getWorks(source, reader_sync_1.default, opts); + return arrayUtils.flatten(works); +} +exports.sync = sync; +/** + * Asynchronous API. + */ +function async(source, opts) { + try { + assertPatternsInput(source); + } + catch (error) { + return Promise.reject(error); + } + var works = getWorks(source, reader_async_1.default, opts); + return Promise.all(works).then(arrayUtils.flatten); +} +exports.async = async; +/** + * Stream API. + */ +function stream(source, opts) { + assertPatternsInput(source); + var works = getWorks(source, reader_stream_1.default, opts); + return streamUtils.merge(works); +} +exports.stream = stream; +/** + * Return a set of tasks based on provided patterns. + */ +function generateTasks(source, opts) { + assertPatternsInput(source); + var patterns = [].concat(source); + var options = optionsManager.prepare(opts); + return taskManager.generate(patterns, options); +} +exports.generateTasks = generateTasks; +/** + * Returns a set of works based on provided tasks and class of the reader. + */ +function getWorks(source, _Reader, opts) { + var patterns = [].concat(source); + var options = optionsManager.prepare(opts); + var tasks = taskManager.generate(patterns, options); + var reader = new _Reader(options); + return tasks.map(reader.read, reader); +} +function assertPatternsInput(source) { + if ([].concat(source).every(isString)) { + return; + } + throw new TypeError('Patterns must be a string or an array of strings'); +} +function isString(source) { + /* tslint:disable-next-line strict-type-predicates */ + return typeof source === 'string'; +} + + +/***/ }), +/* 571 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +function prepare(options) { + var opts = __assign({ cwd: process.cwd(), deep: true, ignore: [], dot: false, stats: false, onlyFiles: true, onlyDirectories: false, followSymlinkedDirectories: true, unique: true, markDirectories: false, absolute: false, nobrace: false, brace: true, noglobstar: false, globstar: true, noext: false, extension: true, nocase: false, case: true, matchBase: false, transform: null }, options); + if (opts.onlyDirectories) { + opts.onlyFiles = false; + } + opts.brace = !opts.nobrace; + opts.globstar = !opts.noglobstar; + opts.extension = !opts.noext; + opts.case = !opts.nocase; + if (options) { + opts.brace = ('brace' in options ? options.brace : opts.brace); + opts.globstar = ('globstar' in options ? options.globstar : opts.globstar); + opts.extension = ('extension' in options ? options.extension : opts.extension); + opts.case = ('case' in options ? options.case : opts.case); + } + return opts; +} +exports.prepare = prepare; + + +/***/ }), +/* 572 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var patternUtils = __webpack_require__(573); +/** + * Generate tasks based on parent directory of each pattern. + */ +function generate(patterns, options) { + var unixPatterns = patterns.map(patternUtils.unixifyPattern); + var unixIgnore = options.ignore.map(patternUtils.unixifyPattern); + var positivePatterns = getPositivePatterns(unixPatterns); + var negativePatterns = getNegativePatternsAsPositive(unixPatterns, unixIgnore); + /** + * When the `case` option is disabled, all patterns must be marked as dynamic, because we cannot check filepath + * directly (without read directory). + */ + var staticPatterns = !options.case ? [] : positivePatterns.filter(patternUtils.isStaticPattern); + var dynamicPatterns = !options.case ? positivePatterns : positivePatterns.filter(patternUtils.isDynamicPattern); + var staticTasks = convertPatternsToTasks(staticPatterns, negativePatterns, /* dynamic */ false); + var dynamicTasks = convertPatternsToTasks(dynamicPatterns, negativePatterns, /* dynamic */ true); + return staticTasks.concat(dynamicTasks); +} +exports.generate = generate; +/** + * Convert patterns to tasks based on parent directory of each pattern. + */ +function convertPatternsToTasks(positive, negative, dynamic) { + var positivePatternsGroup = groupPatternsByBaseDirectory(positive); + // When we have a global group – there is no reason to divide the patterns into independent tasks. + // In this case, the global task covers the rest. + if ('.' in positivePatternsGroup) { + var task = convertPatternGroupToTask('.', positive, negative, dynamic); + return [task]; + } + return convertPatternGroupsToTasks(positivePatternsGroup, negative, dynamic); +} +exports.convertPatternsToTasks = convertPatternsToTasks; +/** + * Return only positive patterns. + */ +function getPositivePatterns(patterns) { + return patternUtils.getPositivePatterns(patterns); +} +exports.getPositivePatterns = getPositivePatterns; +/** + * Return only negative patterns. + */ +function getNegativePatternsAsPositive(patterns, ignore) { + var negative = patternUtils.getNegativePatterns(patterns).concat(ignore); + var positive = negative.map(patternUtils.convertToPositivePattern); + return positive; +} +exports.getNegativePatternsAsPositive = getNegativePatternsAsPositive; +/** + * Group patterns by base directory of each pattern. + */ +function groupPatternsByBaseDirectory(patterns) { + return patterns.reduce(function (collection, pattern) { + var base = patternUtils.getBaseDirectory(pattern); + if (base in collection) { + collection[base].push(pattern); + } + else { + collection[base] = [pattern]; + } + return collection; + }, {}); +} +exports.groupPatternsByBaseDirectory = groupPatternsByBaseDirectory; +/** + * Convert group of patterns to tasks. + */ +function convertPatternGroupsToTasks(positive, negative, dynamic) { + return Object.keys(positive).map(function (base) { + return convertPatternGroupToTask(base, positive[base], negative, dynamic); + }); +} +exports.convertPatternGroupsToTasks = convertPatternGroupsToTasks; +/** + * Create a task for positive and negative patterns. + */ +function convertPatternGroupToTask(base, positive, negative, dynamic) { + return { + base: base, + dynamic: dynamic, + positive: positive, + negative: negative, + patterns: [].concat(positive, negative.map(patternUtils.convertToNegativePattern)) + }; +} +exports.convertPatternGroupToTask = convertPatternGroupToTask; + + +/***/ }), +/* 573 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var path = __webpack_require__(4); +var globParent = __webpack_require__(574); +var isGlob = __webpack_require__(266); +var micromatch = __webpack_require__(577); +var GLOBSTAR = '**'; +/** + * Return true for static pattern. + */ +function isStaticPattern(pattern) { + return !isDynamicPattern(pattern); +} +exports.isStaticPattern = isStaticPattern; +/** + * Return true for pattern that looks like glob. + */ +function isDynamicPattern(pattern) { + return isGlob(pattern, { strict: false }); +} +exports.isDynamicPattern = isDynamicPattern; +/** + * Convert a windows «path» to a unix-style «path». + */ +function unixifyPattern(pattern) { + return pattern.replace(/\\/g, '/'); +} +exports.unixifyPattern = unixifyPattern; +/** + * Returns negative pattern as positive pattern. + */ +function convertToPositivePattern(pattern) { + return isNegativePattern(pattern) ? pattern.slice(1) : pattern; +} +exports.convertToPositivePattern = convertToPositivePattern; +/** + * Returns positive pattern as negative pattern. + */ +function convertToNegativePattern(pattern) { + return '!' + pattern; +} +exports.convertToNegativePattern = convertToNegativePattern; +/** + * Return true if provided pattern is negative pattern. + */ +function isNegativePattern(pattern) { + return pattern.startsWith('!') && pattern[1] !== '('; +} +exports.isNegativePattern = isNegativePattern; +/** + * Return true if provided pattern is positive pattern. + */ +function isPositivePattern(pattern) { + return !isNegativePattern(pattern); +} +exports.isPositivePattern = isPositivePattern; +/** + * Extracts negative patterns from array of patterns. + */ +function getNegativePatterns(patterns) { + return patterns.filter(isNegativePattern); +} +exports.getNegativePatterns = getNegativePatterns; +/** + * Extracts positive patterns from array of patterns. + */ +function getPositivePatterns(patterns) { + return patterns.filter(isPositivePattern); +} +exports.getPositivePatterns = getPositivePatterns; +/** + * Extract base directory from provided pattern. + */ +function getBaseDirectory(pattern) { + return globParent(pattern); +} +exports.getBaseDirectory = getBaseDirectory; +/** + * Return true if provided pattern has globstar. + */ +function hasGlobStar(pattern) { + return pattern.indexOf(GLOBSTAR) !== -1; +} +exports.hasGlobStar = hasGlobStar; +/** + * Return true if provided pattern ends with slash and globstar. + */ +function endsWithSlashGlobStar(pattern) { + return pattern.endsWith('/' + GLOBSTAR); +} +exports.endsWithSlashGlobStar = endsWithSlashGlobStar; +/** + * Returns «true» when pattern ends with a slash and globstar or the last partial of the pattern is static pattern. + */ +function isAffectDepthOfReadingPattern(pattern) { + var basename = path.basename(pattern); + return endsWithSlashGlobStar(pattern) || isStaticPattern(basename); +} +exports.isAffectDepthOfReadingPattern = isAffectDepthOfReadingPattern; +/** + * Return naive depth of provided pattern without depth of the base directory. + */ +function getNaiveDepth(pattern) { + var base = getBaseDirectory(pattern); + var patternDepth = pattern.split('/').length; + var patternBaseDepth = base.split('/').length; + /** + * This is a hack for pattern that has no base directory. + * + * This is related to the `*\something\*` pattern. + */ + if (base === '.') { + return patternDepth - patternBaseDepth; + } + return patternDepth - patternBaseDepth - 1; +} +exports.getNaiveDepth = getNaiveDepth; +/** + * Return max naive depth of provided patterns without depth of the base directory. + */ +function getMaxNaivePatternsDepth(patterns) { + return patterns.reduce(function (max, pattern) { + var depth = getNaiveDepth(pattern); + return depth > max ? depth : max; + }, 0); +} +exports.getMaxNaivePatternsDepth = getMaxNaivePatternsDepth; +/** + * Make RegExp for provided pattern. + */ +function makeRe(pattern, options) { + return micromatch.makeRe(pattern, options); +} +exports.makeRe = makeRe; +/** + * Convert patterns to regexps. + */ +function convertPatternsToRe(patterns, options) { + return patterns.map(function (pattern) { return makeRe(pattern, options); }); +} +exports.convertPatternsToRe = convertPatternsToRe; +/** + * Returns true if the entry match any of the given RegExp's. + */ +function matchAny(entry, patternsRe) { + return patternsRe.some(function (patternRe) { return patternRe.test(entry); }); +} +exports.matchAny = matchAny; + + +/***/ }), +/* 574 */ +/***/ (function(module, exports, __webpack_require__) { +"use strict"; -MuteStream.prototype.pipe = function (dest, options) { - this._dest = dest - return Stream.prototype.pipe.call(this, dest, options) -} -MuteStream.prototype.pause = function () { - if (this._src) return this._src.pause() -} +var path = __webpack_require__(4); +var isglob = __webpack_require__(575); +var pathDirname = __webpack_require__(576); +var isWin32 = __webpack_require__(122).platform() === 'win32'; -MuteStream.prototype.resume = function () { - if (this._src) return this._src.resume() -} +module.exports = function globParent(str) { + // flip windows path separators + if (isWin32 && str.indexOf('/') < 0) str = str.split('\\').join('/'); -MuteStream.prototype.write = function (c) { - if (this.muted) { - if (!this.replace) return true - if (c.match(/^\u001b/)) { - if(c.indexOf(this._prompt) === 0) { - c = c.substr(this._prompt.length); - c = c.replace(/./g, this.replace); - c = this._prompt + c; - } - this._hadControl = true - return this.emit('data', c) - } else { - if (this._prompt && this._hadControl && - c.indexOf(this._prompt) === 0) { - this._hadControl = false - this.emit('data', this._prompt) - c = c.substr(this._prompt.length) - } - c = c.toString().replace(/./g, this.replace) - } - } - this.emit('data', c) -} + // special case for strings ending in enclosure containing path separator + if (/[\{\[].*[\/]*.*[\}\]]$/.test(str)) str += '/'; -MuteStream.prototype.end = function (c) { - if (this.muted) { - if (c && this.replace) { - c = c.toString().replace(/./g, this.replace) - } else { - c = null - } - } - if (c) this.emit('data', c) - this.emit('end') -} + // preserves full path in case of trailing path separator + str += 'a'; -function proxy (fn) { return function () { - var d = this._dest - var s = this._src - if (d && d[fn]) d[fn].apply(d, arguments) - if (s && s[fn]) s[fn].apply(s, arguments) -}} + // remove path parts that are globby + do {str = pathDirname.posix(str)} + while (isglob(str) || /(^|[^\\])([\{\[]|\([^\)]+$)/.test(str)); -MuteStream.prototype.destroy = proxy('destroy') -MuteStream.prototype.destroySoon = proxy('destroySoon') -MuteStream.prototype.close = proxy('close') + // remove escape chars and return result + return str.replace(/\\([\*\?\|\[\]\(\)\{\}])/g, '$1'); +}; /***/ }), -/* 544 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* 575 */ +/***/ (function(module, exports, __webpack_require__) { -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ResetCommand", function() { return ResetCommand; }); -/* harmony import */ var dedent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2); -/* harmony import */ var dedent__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(dedent__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(240); -/* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(del__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var ora__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(521); -/* harmony import */ var ora__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(ora__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_3__); -/* harmony import */ var _utils_bazel__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(413); -/* harmony import */ var _utils_fs__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(231); -/* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(220); -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. +/*! + * is-glob + * + * Copyright (c) 2014-2016, Jon Schlinkert. + * Licensed under the MIT License. */ +var isExtglob = __webpack_require__(267); +module.exports = function isGlob(str) { + if (typeof str !== 'string' || str === '') { + return false; + } + if (isExtglob(str)) return true; + var regex = /(\\).|([*?]|\[.*\]|\{.*\}|\(.*\|.*\)|^!)/; + var match; + while ((match = regex.exec(str))) { + if (match[2]) return true; + str = str.slice(match.index + match[0].length); + } + return false; +}; -const ResetCommand = { - description: 'Deletes node_modules and output directories, resets internal and disk caches, and stops Bazel server', - name: 'reset', - reportTiming: { - group: 'scripts/kbn reset', - id: 'total' - }, +/***/ }), +/* 576 */ +/***/ (function(module, exports, __webpack_require__) { - async run(projects) { - _utils_log__WEBPACK_IMPORTED_MODULE_6__["log"].warning(dedent__WEBPACK_IMPORTED_MODULE_0___default.a` - In most cases, 'yarn kbn clean' is all that should be needed to recover a consistent state when - problems arise. If you need to use this command, please let us know, as it should not be necessary. - `); - const toDelete = []; +"use strict"; - for (const project of projects.values()) { - if (await Object(_utils_fs__WEBPACK_IMPORTED_MODULE_5__["isDirectory"])(project.nodeModulesLocation)) { - toDelete.push({ - cwd: project.path, - pattern: Object(path__WEBPACK_IMPORTED_MODULE_3__["relative"])(project.path, project.nodeModulesLocation) - }); - } - if (await Object(_utils_fs__WEBPACK_IMPORTED_MODULE_5__["isDirectory"])(project.targetLocation)) { - toDelete.push({ - cwd: project.path, - pattern: Object(path__WEBPACK_IMPORTED_MODULE_3__["relative"])(project.path, project.targetLocation) - }); - } +var path = __webpack_require__(4); +var inspect = __webpack_require__(113).inspect; - const { - extraPatterns - } = project.getCleanConfig(); +function assertPath(path) { + if (typeof path !== 'string') { + throw new TypeError('Path must be a string. Received ' + inspect(path)); + } +} - if (extraPatterns) { - toDelete.push({ - cwd: project.path, - pattern: extraPatterns - }); +function posix(path) { + assertPath(path); + if (path.length === 0) + return '.'; + var code = path.charCodeAt(0); + var hasRoot = (code === 47/*/*/); + var end = -1; + var matchedSlash = true; + for (var i = path.length - 1; i >= 1; --i) { + code = path.charCodeAt(i); + if (code === 47/*/*/) { + if (!matchedSlash) { + end = i; + break; } - } // Runs Bazel hard clean and deletes Bazel Cache Folders - - - if (await Object(_utils_bazel__WEBPACK_IMPORTED_MODULE_4__["isBazelBinAvailable"])()) { - // Hard cleaning bazel - await Object(_utils_bazel__WEBPACK_IMPORTED_MODULE_4__["runBazel"])(['clean', '--expunge']); - _utils_log__WEBPACK_IMPORTED_MODULE_6__["log"].success('Hard cleaned bazel'); // Deletes Bazel Cache Folders - - await del__WEBPACK_IMPORTED_MODULE_1___default()([await Object(_utils_bazel__WEBPACK_IMPORTED_MODULE_4__["getBazelDiskCacheFolder"])(), await Object(_utils_bazel__WEBPACK_IMPORTED_MODULE_4__["getBazelRepositoryCacheFolder"])()], { - force: true - }); - _utils_log__WEBPACK_IMPORTED_MODULE_6__["log"].success('Removed disk caches'); + } else { + // We saw the first non-path separator + matchedSlash = false; } + } - if (toDelete.length === 0) { - return; - } - /** - * In order to avoid patterns like `/build` in packages from accidentally - * impacting files outside the package we use `process.chdir()` to change - * the cwd to the package and execute `del()` without the `force` option - * so it will check that each file being deleted is within the package. - * - * `del()` does support a `cwd` option, but it's only for resolving the - * patterns and does not impact the cwd check. - */ + if (end === -1) + return hasRoot ? '/' : '.'; + if (hasRoot && end === 1) + return '//'; + return path.slice(0, end); +} +function win32(path) { + assertPath(path); + var len = path.length; + if (len === 0) + return '.'; + var rootEnd = -1; + var end = -1; + var matchedSlash = true; + var offset = 0; + var code = path.charCodeAt(0); - const originalCwd = process.cwd(); + // Try to match a root + if (len > 1) { + if (code === 47/*/*/ || code === 92/*\*/) { + // Possible UNC root - try { - for (const { - pattern, - cwd - } of toDelete) { - process.chdir(cwd); - const promise = del__WEBPACK_IMPORTED_MODULE_1___default()(pattern); + rootEnd = offset = 1; - if (_utils_log__WEBPACK_IMPORTED_MODULE_6__["log"].wouldLogLevel('info')) { - ora__WEBPACK_IMPORTED_MODULE_2___default.a.promise(promise, Object(path__WEBPACK_IMPORTED_MODULE_3__["relative"])(originalCwd, Object(path__WEBPACK_IMPORTED_MODULE_3__["join"])(cwd, String(pattern)))); + code = path.charCodeAt(1); + if (code === 47/*/*/ || code === 92/*\*/) { + // Matched double path separator at beginning + var j = 2; + var last = j; + // Match 1 or more non-path separators + for (; j < len; ++j) { + code = path.charCodeAt(j); + if (code === 47/*/*/ || code === 92/*\*/) + break; } + if (j < len && j !== last) { + // Matched! + last = j; + // Match 1 or more path separators + for (; j < len; ++j) { + code = path.charCodeAt(j); + if (code !== 47/*/*/ && code !== 92/*\*/) + break; + } + if (j < len && j !== last) { + // Matched! + last = j; + // Match 1 or more non-path separators + for (; j < len; ++j) { + code = path.charCodeAt(j); + if (code === 47/*/*/ || code === 92/*\*/) + break; + } + if (j === len) { + // We matched a UNC root only + return path; + } + if (j !== last) { + // We matched a UNC root with leftovers - await promise; + // Offset by 1 to include the separator after the UNC root to + // treat it as a "normal root" on top of a (UNC) root + rootEnd = offset = j + 1; + } + } + } } - } finally { - process.chdir(originalCwd); - } - } - -}; - -/***/ }), -/* 545 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { - -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RunCommand", function() { return RunCommand; }); -/* harmony import */ var dedent__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2); -/* harmony import */ var dedent__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(dedent__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(341); -/* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(220); -/* harmony import */ var _utils_parallelize__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(546); -/* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(340); -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - - - - - -const RunCommand = { - description: 'Run script defined in package.json in each package that contains that script (only works on packages not using Bazel yet)', - name: 'run', - reportTiming: { - group: 'scripts/kbn run', - id: 'total' - }, - - async run(projects, projectGraph, { - extraArgs, - options - }) { - _utils_log__WEBPACK_IMPORTED_MODULE_2__["log"].warning(dedent__WEBPACK_IMPORTED_MODULE_0___default.a` - We are migrating packages into the Bazel build system and we will no longer support running npm scripts on - packages using 'yarn kbn run' on Bazel built packages. If the package you are trying to act on contains a - BUILD.bazel file please just use 'yarn kbn build' to build it or 'yarn kbn watch' to watch it - `); - const batchedProjects = Object(_utils_projects__WEBPACK_IMPORTED_MODULE_4__["topologicallyBatchProjects"])(projects, projectGraph); - - if (extraArgs.length === 0) { - throw new _utils_errors__WEBPACK_IMPORTED_MODULE_1__["CliError"]('No script specified'); - } + } else if ((code >= 65/*A*/ && code <= 90/*Z*/) || + (code >= 97/*a*/ && code <= 122/*z*/)) { + // Possible device root - const scriptName = extraArgs[0]; - const scriptArgs = extraArgs.slice(1); - await Object(_utils_parallelize__WEBPACK_IMPORTED_MODULE_3__["parallelizeBatches"])(batchedProjects, async project => { - if (!project.hasScript(scriptName)) { - if (!!options['skip-missing']) { - return; + code = path.charCodeAt(1); + if (path.charCodeAt(1) === 58/*:*/) { + rootEnd = offset = 2; + if (len > 2) { + code = path.charCodeAt(2); + if (code === 47/*/*/ || code === 92/*\*/) + rootEnd = offset = 3; } - - throw new _utils_errors__WEBPACK_IMPORTED_MODULE_1__["CliError"](`[${project.name}] no "${scriptName}" script defined. To skip packages without the "${scriptName}" script pass --skip-missing`); } - - _utils_log__WEBPACK_IMPORTED_MODULE_2__["log"].info(`[${project.name}] running "${scriptName}" script`); - await project.runScriptStreaming(scriptName, { - args: scriptArgs - }); - _utils_log__WEBPACK_IMPORTED_MODULE_2__["log"].success(`[${project.name}] complete`); - }); + } + } else if (code === 47/*/*/ || code === 92/*\*/) { + return path[0]; } -}; - -/***/ }), -/* 546 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { + for (var i = len - 1; i >= offset; --i) { + code = path.charCodeAt(i); + if (code === 47/*/*/ || code === 92/*\*/) { + if (!matchedSlash) { + end = i; + break; + } + } else { + // We saw the first non-path separator + matchedSlash = false; + } + } -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "parallelizeBatches", function() { return parallelizeBatches; }); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "parallelize", function() { return parallelize; }); -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ -async function parallelizeBatches(batches, fn) { - for (const batch of batches) { - // We need to make sure the entire batch has completed before we can move on - // to the next batch - await parallelize(batch, fn); + if (end === -1) { + if (rootEnd === -1) + return '.'; + else + end = rootEnd; } + return path.slice(0, end); } -async function parallelize(items, fn, concurrency = 4) { - if (items.length === 0) { - return; - } - - return new Promise((resolve, reject) => { - let activePromises = 0; - const values = items.slice(0); - async function scheduleItem(item) { - activePromises++; - - try { - await fn(item); - activePromises--; - - if (values.length > 0) { - // We have more work to do, so we schedule the next promise - scheduleItem(values.shift()); - } else if (activePromises === 0) { - // We have no more values left, and all items have completed, so we've - // completed all the work. - resolve(); - } - } catch (error) { - reject(error); - } - } +module.exports = process.platform === 'win32' ? win32 : posix; +module.exports.posix = posix; +module.exports.win32 = win32; - values.splice(0, concurrency).map(scheduleItem); - }); -} /***/ }), -/* 547 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* 577 */ +/***/ (function(module, exports, __webpack_require__) { "use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "WatchCommand", function() { return WatchCommand; }); -/* harmony import */ var _utils_bazel__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(413); -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. + + +/** + * Module dependencies */ -const WatchCommand = { - description: 'Runs a build in the Bazel built packages and keeps watching them for changes', - name: 'watch', - reportTiming: { - group: 'scripts/kbn watch', - id: 'total' - }, +var util = __webpack_require__(113); +var braces = __webpack_require__(578); +var toRegex = __webpack_require__(579); +var extend = __webpack_require__(691); - async run(projects, projectGraph, { - options - }) { - const runOffline = (options === null || options === void 0 ? void 0 : options.offline) === true; // Call bazel with the target to build all available packages and run it through iBazel to watch it for changes - // - // Note: --run_output=false arg will disable the iBazel notifications about gazelle and buildozer when running it - // Can also be solved by adding a root `.bazel_fix_commands.json` but its not needed at the moment +/** + * Local dependencies + */ - await Object(_utils_bazel__WEBPACK_IMPORTED_MODULE_0__["runIBazel"])(['--run_output=false', 'build', '//packages:build', '--show_result=1'], runOffline); - } +var compilers = __webpack_require__(693); +var parsers = __webpack_require__(719); +var cache = __webpack_require__(720); +var utils = __webpack_require__(721); +var MAX_LENGTH = 1024 * 64; -}; +/** + * The main function takes a list of strings and one or more + * glob patterns to use for matching. + * + * ```js + * var mm = require('micromatch'); + * mm(list, patterns[, options]); + * + * console.log(mm(['a.js', 'a.txt'], ['*.js'])); + * //=> [ 'a.js' ] + * ``` + * @param {Array} `list` A list of strings to match + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Array} Returns an array of matches + * @summary false + * @api public + */ -/***/ }), -/* 548 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +function micromatch(list, patterns, options) { + patterns = utils.arrayify(patterns); + list = utils.arrayify(list); -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "runCommand", function() { return runCommand; }); -/* harmony import */ var _kbn_dev_utils_ci_stats_reporter__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(131); -/* harmony import */ var _kbn_dev_utils_ci_stats_reporter__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_kbn_dev_utils_ci_stats_reporter__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(341); -/* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(220); -/* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(340); -/* harmony import */ var _utils_projects_tree__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(412); -/* harmony import */ var _utils_kibana__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(549); -function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } + var len = patterns.length; + if (list.length === 0 || len === 0) { + return []; + } -function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } + if (len === 1) { + return micromatch.match(list, patterns[0], options); + } -function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + var omit = []; + var keep = []; + var idx = -1; -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ + while (++idx < len) { + var pattern = patterns[idx]; + if (typeof pattern === 'string' && pattern.charCodeAt(0) === 33 /* ! */) { + omit.push.apply(omit, micromatch.match(list, pattern.slice(1), options)); + } else { + keep.push.apply(keep, micromatch.match(list, pattern, options)); + } + } + var matches = utils.diff(keep, omit); + if (!options || options.nodupes !== false) { + return utils.unique(matches); + } + return matches; +} +/** + * Similar to the main function, but `pattern` must be a string. + * + * ```js + * var mm = require('micromatch'); + * mm.match(list, pattern[, options]); + * + * console.log(mm.match(['a.a', 'a.aa', 'a.b', 'a.c'], '*.a')); + * //=> ['a.a', 'a.aa'] + * ``` + * @param {Array} `list` Array of strings to match + * @param {String} `pattern` Glob pattern to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Array} Returns an array of matches + * @api public + */ +micromatch.match = function(list, pattern, options) { + if (Array.isArray(pattern)) { + throw new TypeError('expected pattern to be a string'); + } -process.env.CI_STATS_NESTED_TIMING = 'true'; -async function runCommand(command, config) { - const runStartTime = Date.now(); - let kbn; + var unixify = utils.unixify(options); + var isMatch = memoize('match', pattern, options, micromatch.matcher); + var matches = []; - try { - _utils_log__WEBPACK_IMPORTED_MODULE_2__["log"].debug(`Running [${command.name}] command from [${config.rootPath}]`); - kbn = await _utils_kibana__WEBPACK_IMPORTED_MODULE_5__["Kibana"].loadFrom(config.rootPath); - const projects = kbn.getFilteredProjects({ - skipKibanaPlugins: Boolean(config.options['skip-kibana-plugins']), - ossOnly: Boolean(config.options.oss), - exclude: toArray(config.options.exclude), - include: toArray(config.options.include) - }); + list = utils.arrayify(list); + var len = list.length; + var idx = -1; - if (projects.size === 0) { - _utils_log__WEBPACK_IMPORTED_MODULE_2__["log"].error(`There are no projects found. Double check project name(s) in '-i/--include' and '-e/--exclude' filters.`); - return process.exit(1); + while (++idx < len) { + var ele = list[idx]; + if (ele === pattern || isMatch(ele)) { + matches.push(utils.value(ele, unixify, options)); } + } - const projectGraph = Object(_utils_projects__WEBPACK_IMPORTED_MODULE_3__["buildProjectGraph"])(projects); - _utils_log__WEBPACK_IMPORTED_MODULE_2__["log"].debug(`Found ${projects.size.toString()} projects`); - _utils_log__WEBPACK_IMPORTED_MODULE_2__["log"].debug(Object(_utils_projects_tree__WEBPACK_IMPORTED_MODULE_4__["renderProjectsTree"])(config.rootPath, projects)); - await command.run(projects, projectGraph, _objectSpread(_objectSpread({}, config), {}, { - kbn - })); + // if no options were passed, uniquify results and return + if (typeof options === 'undefined') { + return utils.unique(matches); + } - if (command.reportTiming) { - 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: true - } - }] - }); + if (matches.length === 0) { + if (options.failglob === true) { + throw new Error('no matches found for "' + pattern + '"'); } - } catch (error) { - if (command.reportTiming) { - // if we don't have a kbn object then things are too broken to report on - if (kbn) { - 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); - } - } + if (options.nonull === true || options.nullglob === true) { + return [options.unescape ? utils.unescape(pattern) : pattern]; } + } - _utils_log__WEBPACK_IMPORTED_MODULE_2__["log"].error(`[${command.name}] failed:`); + // if `opts.ignore` was defined, diff ignored list + if (options.ignore) { + matches = micromatch.not(matches, options.ignore, options); + } - if (error instanceof _utils_errors__WEBPACK_IMPORTED_MODULE_1__["CliError"]) { - _utils_log__WEBPACK_IMPORTED_MODULE_2__["log"].error(error.message); - const metaOutput = Object.entries(error.meta).map(([key, value]) => `${key}: ${value}`).join('\n'); + return options.nodupes !== false ? utils.unique(matches) : matches; +}; - if (metaOutput) { - _utils_log__WEBPACK_IMPORTED_MODULE_2__["log"].info('Additional debugging info:\n'); - _utils_log__WEBPACK_IMPORTED_MODULE_2__["log"].indent(2); - _utils_log__WEBPACK_IMPORTED_MODULE_2__["log"].info(metaOutput); - _utils_log__WEBPACK_IMPORTED_MODULE_2__["log"].indent(-2); - } - } else { - _utils_log__WEBPACK_IMPORTED_MODULE_2__["log"].error(error); - } +/** + * Returns true if the specified `string` matches the given glob `pattern`. + * + * ```js + * var mm = require('micromatch'); + * mm.isMatch(string, pattern[, options]); + * + * console.log(mm.isMatch('a.a', '*.a')); + * //=> true + * console.log(mm.isMatch('a.b', '*.a')); + * //=> false + * ``` + * @param {String} `string` String to match + * @param {String} `pattern` Glob pattern to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Boolean} Returns true if the string matches the glob pattern. + * @api public + */ - process.exit(1); +micromatch.isMatch = function(str, pattern, options) { + if (typeof str !== 'string') { + throw new TypeError('expected a string: "' + util.inspect(str) + '"'); } -} -function toArray(value) { - if (value == null) { - return []; + if (isEmptyString(str) || isEmptyString(pattern)) { + return false; } - return Array.isArray(value) ? value : [value]; -} + var equals = utils.equalsPattern(options); + if (equals(str)) { + return true; + } -/***/ }), -/* 549 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { + var isMatch = memoize('isMatch', pattern, options, micromatch.matcher); + return isMatch(str); +}; -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Kibana", function() { return Kibana; }); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(132); -/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(fs__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var multimatch__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(550); -/* harmony import */ var multimatch__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(multimatch__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var is_path_inside__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(333); -/* harmony import */ var is_path_inside__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(is_path_inside__WEBPACK_IMPORTED_MODULE_3__); -/* harmony import */ var _yarn_lock__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(408); -/* harmony import */ var _projects__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(340); -/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(553); -function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } +/** + * Returns true if some of the strings in the given `list` match any of the + * given glob `patterns`. + * + * ```js + * var mm = require('micromatch'); + * mm.some(list, patterns[, options]); + * + * console.log(mm.some(['foo.js', 'bar.js'], ['*.js', '!foo.js'])); + * // true + * console.log(mm.some(['foo.js'], ['*.js', '!foo.js'])); + * // false + * ``` + * @param {String|Array} `list` The string or array of strings to test. Returns as soon as the first match is found. + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Boolean} Returns true if any patterns match `str` + * @api public + */ -function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } +micromatch.some = function(list, patterns, options) { + if (typeof list === 'string') { + list = [list]; + } + for (var i = 0; i < list.length; i++) { + if (micromatch(list[i], patterns, options).length === 1) { + return true; + } + } + return false; +}; -function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } +/** + * Returns true if every string in the given `list` matches + * any of the given glob `patterns`. + * + * ```js + * var mm = require('micromatch'); + * mm.every(list, patterns[, options]); + * + * console.log(mm.every('foo.js', ['foo.js'])); + * // true + * console.log(mm.every(['foo.js', 'bar.js'], ['*.js'])); + * // true + * console.log(mm.every(['foo.js', 'bar.js'], ['*.js', '!foo.js'])); + * // false + * console.log(mm.every(['foo.js'], ['*.js', '!foo.js'])); + * // false + * ``` + * @param {String|Array} `list` The string or array of strings to test. + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Boolean} Returns true if any patterns match `str` + * @api public + */ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. +micromatch.every = function(list, patterns, options) { + if (typeof list === 'string') { + list = [list]; + } + for (var i = 0; i < list.length; i++) { + if (micromatch(list[i], patterns, options).length !== 1) { + return false; + } + } + return true; +}; + +/** + * Returns true if **any** of the given glob `patterns` + * match the specified `string`. + * + * ```js + * var mm = require('micromatch'); + * mm.any(string, patterns[, options]); + * + * console.log(mm.any('a.a', ['b.*', '*.a'])); + * //=> true + * console.log(mm.any('a.a', 'b.*')); + * //=> false + * ``` + * @param {String|Array} `str` The string to test. + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Boolean} Returns true if any patterns match `str` + * @api public */ +micromatch.any = function(str, patterns, options) { + if (typeof str !== 'string') { + throw new TypeError('expected a string: "' + util.inspect(str) + '"'); + } + if (isEmptyString(str) || isEmptyString(patterns)) { + return false; + } + if (typeof patterns === 'string') { + patterns = [patterns]; + } + for (var i = 0; i < patterns.length; i++) { + if (micromatch.isMatch(str, patterns[i], options)) { + return true; + } + } + return false; +}; +/** + * Returns true if **all** of the given `patterns` match + * the specified string. + * + * ```js + * var mm = require('micromatch'); + * mm.all(string, patterns[, options]); + * + * console.log(mm.all('foo.js', ['foo.js'])); + * // true + * + * console.log(mm.all('foo.js', ['*.js', '!foo.js'])); + * // false + * + * console.log(mm.all('foo.js', ['*.js', 'foo.js'])); + * // true + * + * console.log(mm.all('foo.js', ['*.js', 'f*', '*o*', '*o.js'])); + * // true + * ``` + * @param {String|Array} `str` The string to test. + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Boolean} Returns true if any patterns match `str` + * @api public + */ +micromatch.all = function(str, patterns, options) { + if (typeof str !== 'string') { + throw new TypeError('expected a string: "' + util.inspect(str) + '"'); + } + if (typeof patterns === 'string') { + patterns = [patterns]; + } + for (var i = 0; i < patterns.length; i++) { + if (!micromatch.isMatch(str, patterns[i], options)) { + return false; + } + } + return true; +}; /** - * Helper class for dealing with a set of projects as children of - * the Kibana project. The kbn/pm is currently implemented to be - * more generic, where everything is an operation of generic projects, - * but that leads to exceptions where we need the kibana project and - * do things like `project.get('kibana')!`. + * Returns a list of strings that _**do not match any**_ of the given `patterns`. * - * Using this helper we can restructre the generic list of projects - * as a Kibana object which encapulates all the projects in the - * workspace and knows about the root Kibana project. + * ```js + * var mm = require('micromatch'); + * mm.not(list, patterns[, options]); + * + * console.log(mm.not(['a.a', 'b.b', 'c.c'], '*.a')); + * //=> ['b.b', 'c.c'] + * ``` + * @param {Array} `list` Array of strings to match. + * @param {String|Array} `patterns` One or more glob pattern to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Array} Returns an array of strings that **do not match** the given patterns. + * @api public */ -class Kibana { - static async loadFrom(rootPath) { - return new Kibana(await Object(_projects__WEBPACK_IMPORTED_MODULE_5__["getProjects"])(rootPath, Object(_config__WEBPACK_IMPORTED_MODULE_6__["getProjectPaths"])({ - rootPath - }))); +micromatch.not = function(list, patterns, options) { + var opts = extend({}, options); + var ignore = opts.ignore; + delete opts.ignore; + + var unixify = utils.unixify(opts); + list = utils.arrayify(list).map(unixify); + + var matches = utils.diff(list, micromatch(list, patterns, opts)); + if (ignore) { + matches = utils.diff(matches, micromatch(list, ignore)); } - constructor(allWorkspaceProjects) { - this.allWorkspaceProjects = allWorkspaceProjects; + return opts.nodupes !== false ? utils.unique(matches) : matches; +}; - _defineProperty(this, "kibanaProject", void 0); +/** + * Returns true if the given `string` contains the given pattern. Similar + * to [.isMatch](#isMatch) but the pattern can match any part of the string. + * + * ```js + * var mm = require('micromatch'); + * mm.contains(string, pattern[, options]); + * + * console.log(mm.contains('aa/bb/cc', '*b')); + * //=> true + * console.log(mm.contains('aa/bb/cc', '*d')); + * //=> false + * ``` + * @param {String} `str` The string to match. + * @param {String|Array} `patterns` Glob pattern to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Boolean} Returns true if the patter matches any part of `str`. + * @api public + */ - const kibanaProject = allWorkspaceProjects.get('kibana'); +micromatch.contains = function(str, patterns, options) { + if (typeof str !== 'string') { + throw new TypeError('expected a string: "' + util.inspect(str) + '"'); + } - if (!kibanaProject) { - throw new TypeError('Unable to create Kibana object without all projects, including the Kibana project.'); + if (typeof patterns === 'string') { + if (isEmptyString(str) || isEmptyString(patterns)) { + return false; } - this.kibanaProject = kibanaProject; + var equals = utils.equalsPattern(patterns, options); + if (equals(str)) { + return true; + } + var contains = utils.containsPattern(patterns, options); + if (contains(str)) { + return true; + } } - /** make an absolute path by resolving subPath relative to the kibana repo */ + var opts = extend({}, options, {contains: true}); + return micromatch.any(str, patterns, opts); +}; - getAbsolute(...subPath) { - return path__WEBPACK_IMPORTED_MODULE_0___default.a.resolve(this.kibanaProject.path, ...subPath); - } - /** convert an absolute path to a relative path, relative to the kibana repo */ +/** + * Returns true if the given pattern and options should enable + * the `matchBase` option. + * @return {Boolean} + * @api private + */ +micromatch.matchBase = function(pattern, options) { + if (pattern && pattern.indexOf('/') !== -1 || !options) return false; + return options.basename === true || options.matchBase === true; +}; - getRelative(absolute) { - return path__WEBPACK_IMPORTED_MODULE_0___default.a.relative(this.kibanaProject.path, absolute); +/** + * Filter the keys of the given object with the given `glob` pattern + * and `options`. Does not attempt to match nested keys. If you need this feature, + * use [glob-object][] instead. + * + * ```js + * var mm = require('micromatch'); + * mm.matchKeys(object, patterns[, options]); + * + * var obj = { aa: 'a', ab: 'b', ac: 'c' }; + * console.log(mm.matchKeys(obj, '*b')); + * //=> { ab: 'b' } + * ``` + * @param {Object} `object` The object with keys to filter. + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Object} Returns an object with only keys that match the given patterns. + * @api public + */ + +micromatch.matchKeys = function(obj, patterns, options) { + if (!utils.isObject(obj)) { + throw new TypeError('expected the first argument to be an object'); } - /** get a copy of the map of all projects in the kibana workspace */ + var keys = micromatch(Object.keys(obj), patterns, options); + return utils.pick(obj, keys); +}; +/** + * Returns a memoized matcher function from the given glob `pattern` and `options`. + * The returned function takes a string to match as its only argument and returns + * true if the string is a match. + * + * ```js + * var mm = require('micromatch'); + * mm.matcher(pattern[, options]); + * + * var isMatch = mm.matcher('*.!(*a)'); + * console.log(isMatch('a.a')); + * //=> false + * console.log(isMatch('a.b')); + * //=> true + * ``` + * @param {String} `pattern` Glob pattern + * @param {Object} `options` See available [options](#options) for changing how matches are performed. + * @return {Function} Returns a matcher function. + * @api public + */ - getAllProjects() { - return new Map(this.allWorkspaceProjects); +micromatch.matcher = function matcher(pattern, options) { + if (Array.isArray(pattern)) { + return compose(pattern, options, matcher); } - /** determine if a project with the given name exists */ - - hasProject(name) { - return this.allWorkspaceProjects.has(name); + // if pattern is a regex + if (pattern instanceof RegExp) { + return test(pattern); } - /** get a specific project, throws if the name is not known (use hasProject() first) */ - - getProject(name) { - const project = this.allWorkspaceProjects.get(name); + // if pattern is invalid + if (!utils.isString(pattern)) { + throw new TypeError('expected pattern to be an array, string or regex'); + } - if (!project) { - throw new Error(`No package with name "${name}" in the workspace`); + // if pattern is a non-glob string + if (!utils.hasSpecialChars(pattern)) { + if (options && options.nocase === true) { + pattern = pattern.toLowerCase(); } + return utils.matchPath(pattern, options); + } - return project; + // if pattern is a glob string + var re = micromatch.makeRe(pattern, options); + + // if `options.matchBase` or `options.basename` is defined + if (micromatch.matchBase(pattern, options)) { + return utils.matchBasename(re, options); } - /** get a project and all of the projects it depends on in a ProjectMap */ + function test(regex) { + var equals = utils.equalsPattern(options); + var unixify = utils.unixify(options); - getProjectAndDeps(name) { - const project = this.getProject(name); - return Object(_projects__WEBPACK_IMPORTED_MODULE_5__["includeTransitiveProjects"])([project], this.allWorkspaceProjects); + return function(str) { + if (equals(str)) { + return true; + } + + if (regex.test(unixify(str))) { + return true; + } + return false; + }; } - /** filter the projects to just those matching certain paths/include/exclude tags */ + var fn = test(re); + Object.defineProperty(fn, 'result', { + configurable: true, + enumerable: false, + value: re.result + }); + return fn; +}; - getFilteredProjects(options) { - const allProjects = this.getAllProjects(); - const filteredProjects = new Map(); - const pkgJsonPaths = Array.from(allProjects.values()).map(p => p.packageJsonLocation); - const filteredPkgJsonGlobs = Object(_config__WEBPACK_IMPORTED_MODULE_6__["getProjectPaths"])(_objectSpread(_objectSpread({}, options), {}, { - rootPath: this.kibanaProject.path - })).map(g => path__WEBPACK_IMPORTED_MODULE_0___default.a.resolve(g, 'package.json')); - const matchingPkgJsonPaths = multimatch__WEBPACK_IMPORTED_MODULE_2___default()(pkgJsonPaths, filteredPkgJsonGlobs); +/** + * Returns an array of matches captured by `pattern` in `string, or `null` if the pattern did not match. + * + * ```js + * var mm = require('micromatch'); + * mm.capture(pattern, string[, options]); + * + * console.log(mm.capture('test/*.js', 'test/foo.js')); + * //=> ['foo'] + * console.log(mm.capture('test/*.js', 'foo/bar.css')); + * //=> null + * ``` + * @param {String} `pattern` Glob pattern to use for matching. + * @param {String} `string` String to match + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Boolean} Returns an array of captures if the string matches the glob pattern, otherwise `null`. + * @api public + */ - for (const project of allProjects.values()) { - const pathMatches = matchingPkgJsonPaths.includes(project.packageJsonLocation); - const notExcluded = !options.exclude.includes(project.name); - const isIncluded = !options.include.length || options.include.includes(project.name); +micromatch.capture = function(pattern, str, options) { + var re = micromatch.makeRe(pattern, extend({capture: true}, options)); + var unixify = utils.unixify(options); - if (pathMatches && notExcluded && isIncluded) { - filteredProjects.set(project.name, project); + function match() { + return function(string) { + var match = re.exec(unixify(string)); + if (!match) { + return null; } - } - return filteredProjects; + return match.slice(1); + }; } - isPartOfRepo(project) { - return project.path === this.kibanaProject.path || is_path_inside__WEBPACK_IMPORTED_MODULE_3___default()(project.path, this.kibanaProject.path); + var capture = memoize('capture', pattern, options, match); + return capture(str); +}; + +/** + * Create a regular expression from the given glob `pattern`. + * + * ```js + * var mm = require('micromatch'); + * mm.makeRe(pattern[, options]); + * + * console.log(mm.makeRe('*.js')); + * //=> /^(?:(\.[\\\/])?(?!\.)(?=.)[^\/]*?\.js)$/ + * ``` + * @param {String} `pattern` A glob pattern to convert to regex. + * @param {Object} `options` See available [options](#options) for changing how matches are performed. + * @return {RegExp} Returns a regex created from the given pattern. + * @api public + */ + +micromatch.makeRe = function(pattern, options) { + if (typeof pattern !== 'string') { + throw new TypeError('expected pattern to be a string'); } - isOutsideRepo(project) { - return !this.isPartOfRepo(project); + if (pattern.length > MAX_LENGTH) { + throw new Error('expected pattern to be less than ' + MAX_LENGTH + ' characters'); } - resolveAllProductionDependencies(yarnLock, log) { - const kibanaDeps = Object(_yarn_lock__WEBPACK_IMPORTED_MODULE_4__["resolveDepsForProject"])({ - project: this.kibanaProject, - yarnLock, - kbn: this, - includeDependentProject: true, - productionDepsOnly: true, - log + function makeRe() { + var result = micromatch.create(pattern, options); + var ast_array = []; + var output = result.map(function(obj) { + obj.ast.state = obj.state; + ast_array.push(obj.ast); + return obj.output; }); - const xpackDeps = Object(_yarn_lock__WEBPACK_IMPORTED_MODULE_4__["resolveDepsForProject"])({ - project: this.getProject('x-pack'), - yarnLock, - kbn: this, - includeDependentProject: true, - productionDepsOnly: true, - log + + var regex = toRegex(output.join('|'), options); + Object.defineProperty(regex, 'result', { + configurable: true, + enumerable: false, + value: ast_array }); - return new Map([...kibanaDeps.entries(), ...xpackDeps.entries()]); + return regex; } - getUuid() { - try { - return fs__WEBPACK_IMPORTED_MODULE_1___default.a.readFileSync(this.getAbsolute('data/uuid'), 'utf-8').trim(); - } catch (error) { - if (error.code === 'ENOENT') { - return undefined; - } + return memoize('makeRe', pattern, options, makeRe); +}; - throw error; - } - } +/** + * Expand the given brace `pattern`. + * + * ```js + * var mm = require('micromatch'); + * console.log(mm.braces('foo/{a,b}/bar')); + * //=> ['foo/(a|b)/bar'] + * + * console.log(mm.braces('foo/{a,b}/bar', {expand: true})); + * //=> ['foo/(a|b)/bar'] + * ``` + * @param {String} `pattern` String with brace pattern to expand. + * @param {Object} `options` Any [options](#options) to change how expansion is performed. See the [braces][] library for all available options. + * @return {Array} + * @api public + */ -} +micromatch.braces = function(pattern, options) { + if (typeof pattern !== 'string' && !Array.isArray(pattern)) { + throw new TypeError('expected pattern to be an array or string'); + } -/***/ }), -/* 550 */ -/***/ (function(module, exports, __webpack_require__) { + function expand() { + if (options && options.nobrace === true || !/\{.*\}/.test(pattern)) { + return utils.arrayify(pattern); + } + return braces(pattern, options); + } -"use strict"; + return memoize('braces', pattern, options, expand); +}; -const minimatch = __webpack_require__(247); -const arrayUnion = __webpack_require__(242); -const arrayDiffer = __webpack_require__(551); -const arrify = __webpack_require__(552); +/** + * Proxy to the [micromatch.braces](#method), for parity with + * minimatch. + */ -module.exports = (list, patterns, options = {}) => { - list = arrify(list); - patterns = arrify(patterns); +micromatch.braceExpand = function(pattern, options) { + var opts = extend({}, options, {expand: true}); + return micromatch.braces(pattern, opts); +}; - if (list.length === 0 || patterns.length === 0) { - return []; - } +/** + * Parses the given glob `pattern` and returns an array of abstract syntax + * trees (ASTs), with the compiled `output` and optional source `map` on + * each AST. + * + * ```js + * var mm = require('micromatch'); + * mm.create(pattern[, options]); + * + * console.log(mm.create('abc/*.js')); + * // [{ options: { source: 'string', sourcemap: true }, + * // state: {}, + * // compilers: + * // { ... }, + * // output: '(\\.[\\\\\\/])?abc\\/(?!\\.)(?=.)[^\\/]*?\\.js', + * // ast: + * // { type: 'root', + * // errors: [], + * // nodes: + * // [ ... ], + * // dot: false, + * // input: 'abc/*.js' }, + * // parsingErrors: [], + * // map: + * // { version: 3, + * // sources: [ 'string' ], + * // names: [], + * // mappings: 'AAAA,GAAG,EAAC,kBAAC,EAAC,EAAE', + * // sourcesContent: [ 'abc/*.js' ] }, + * // position: { line: 1, column: 28 }, + * // content: {}, + * // files: {}, + * // idx: 6 }] + * ``` + * @param {String} `pattern` Glob pattern to parse and compile. + * @param {Object} `options` Any [options](#options) to change how parsing and compiling is performed. + * @return {Object} Returns an object with the parsed AST, compiled string and optional source map. + * @api public + */ - return patterns.reduce((result, pattern) => { - let process = arrayUnion; +micromatch.create = function(pattern, options) { + return memoize('create', pattern, options, function() { + function create(str, opts) { + return micromatch.compile(micromatch.parse(str, opts), opts); + } - if (pattern[0] === '!') { - pattern = pattern.slice(1); - process = arrayDiffer; - } + pattern = micromatch.braces(pattern, options); + var len = pattern.length; + var idx = -1; + var res = []; - return process(result, minimatch.match(list, pattern, options)); - }, []); + while (++idx < len) { + res.push(create(pattern[idx], options)); + } + return res; + }); }; +/** + * Parse the given `str` with the given `options`. + * + * ```js + * var mm = require('micromatch'); + * mm.parse(pattern[, options]); + * + * var ast = mm.parse('a/{b,c}/d'); + * console.log(ast); + * // { type: 'root', + * // errors: [], + * // input: 'a/{b,c}/d', + * // nodes: + * // [ { type: 'bos', val: '' }, + * // { type: 'text', val: 'a/' }, + * // { type: 'brace', + * // nodes: + * // [ { type: 'brace.open', val: '{' }, + * // { type: 'text', val: 'b,c' }, + * // { type: 'brace.close', val: '}' } ] }, + * // { type: 'text', val: '/d' }, + * // { type: 'eos', val: '' } ] } + * ``` + * @param {String} `str` + * @param {Object} `options` + * @return {Object} Returns an AST + * @api public + */ -/***/ }), -/* 551 */ -/***/ (function(module, exports, __webpack_require__) { +micromatch.parse = function(pattern, options) { + if (typeof pattern !== 'string') { + throw new TypeError('expected a string'); + } -"use strict"; + function parse() { + var snapdragon = utils.instantiate(null, options); + parsers(snapdragon, options); + var ast = snapdragon.parse(pattern, options); + utils.define(ast, 'snapdragon', snapdragon); + ast.input = pattern; + return ast; + } -const arrayDiffer = (array, ...values) => { - const rest = new Set([].concat(...values)); - return array.filter(element => !rest.has(element)); + return memoize('parse', pattern, options, parse); }; -module.exports = arrayDiffer; +/** + * Compile the given `ast` or string with the given `options`. + * + * ```js + * var mm = require('micromatch'); + * mm.compile(ast[, options]); + * + * var ast = mm.parse('a/{b,c}/d'); + * console.log(mm.compile(ast)); + * // { options: { source: 'string' }, + * // state: {}, + * // compilers: + * // { eos: [Function], + * // noop: [Function], + * // bos: [Function], + * // brace: [Function], + * // 'brace.open': [Function], + * // text: [Function], + * // 'brace.close': [Function] }, + * // output: [ 'a/(b|c)/d' ], + * // ast: + * // { ... }, + * // parsingErrors: [] } + * ``` + * @param {Object|String} `ast` + * @param {Object} `options` + * @return {Object} Returns an object that has an `output` property with the compiled string. + * @api public + */ +micromatch.compile = function(ast, options) { + if (typeof ast === 'string') { + ast = micromatch.parse(ast, options); + } -/***/ }), -/* 552 */ -/***/ (function(module, exports, __webpack_require__) { + return memoize('compile', ast.input, options, function() { + var snapdragon = utils.instantiate(ast, options); + compilers(snapdragon, options); + return snapdragon.compile(ast, options); + }); +}; -"use strict"; +/** + * Clear the regex cache. + * + * ```js + * mm.clearCache(); + * ``` + * @api public + */ +micromatch.clearCache = function() { + micromatch.cache.caches = {}; +}; -const arrify = value => { - if (value === null || value === undefined) { - return []; - } +/** + * Returns true if the given value is effectively an empty string + */ - if (Array.isArray(value)) { - return value; - } +function isEmptyString(val) { + return String(val) === '' || String(val) === './'; +} - if (typeof value === 'string') { - return [value]; - } +/** + * Compose a matcher function with the given patterns. + * This allows matcher functions to be compiled once and + * called multiple times. + */ - if (typeof value[Symbol.iterator] === 'function') { - return [...value]; - } +function compose(patterns, options, matcher) { + var matchers; - return [value]; -}; + return memoize('compose', String(patterns), options, function() { + return function(file) { + // delay composition until it's invoked the first time, + // after that it won't be called again + if (!matchers) { + matchers = []; + for (var i = 0; i < patterns.length; i++) { + matchers.push(matcher(patterns[i], options)); + } + } -module.exports = arrify; + var len = matchers.length; + while (len--) { + if (matchers[len](file) === true) { + return true; + } + } + return false; + }; + }); +} +/** + * Memoize a generated regex or function. A unique key is generated + * from the `type` (usually method name), the `pattern`, and + * user-defined options. + */ -/***/ }), -/* 553 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +function memoize(type, pattern, options, fn) { + var key = utils.createKey(type + '=' + pattern, options); -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getProjectPaths", function() { return getProjectPaths; }); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_0__); -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ + if (options && options.cache === false) { + return fn(pattern, options); + } + + if (cache.has(type, key)) { + return cache.get(type, key); + } + var val = fn(pattern, options); + cache.set(type, key, val); + return val; +} /** - * Returns all the paths where plugins are located + * Expose compiler, parser and cache on `micromatch` */ -function getProjectPaths({ - rootPath, - ossOnly, - skipKibanaPlugins -}) { - const projectPaths = [rootPath, Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'packages/*')]; // This is needed in order to install the dependencies for the declared - // plugin functional used in the selenium functional tests. - // As we are now using the webpack dll for the client vendors dependencies - // when we run the plugin functional tests against the distributable - // dependencies used by such plugins like @eui, react and react-dom can't - // be loaded from the dll as the context is different from the one declared - // into the webpack dll reference plugin. - // In anyway, have a plugin declaring their own dependencies is the - // correct and the expect behavior. - - projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'test/plugin_functional/plugins/*')); - projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'test/interpreter_functional/plugins/*')); - projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'test/server_integration/__fixtures__/plugins/*')); - projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'examples/*')); - if (!ossOnly) { - projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'x-pack')); - projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'x-pack/plugins/*')); - projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'x-pack/legacy/plugins/*')); - projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'x-pack/test/functional_with_es_ssl/fixtures/plugins/*')); - } +micromatch.compilers = compilers; +micromatch.parsers = parsers; +micromatch.caches = cache.caches; - if (!skipKibanaPlugins) { - projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, '../kibana-extra/*')); - projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, '../kibana-extra/*/packages/*')); - projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, '../kibana-extra/*/plugins/*')); - projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'plugins/*')); - projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'plugins/*/packages/*')); - projectPaths.push(Object(path__WEBPACK_IMPORTED_MODULE_0__["resolve"])(rootPath, 'plugins/*/plugins/*')); - } +/** + * Expose `micromatch` + * @type {Function} + */ + +module.exports = micromatch; - return projectPaths; -} /***/ }), -/* 554 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* 578 */ +/***/ (function(module, exports, __webpack_require__) { "use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony import */ var _build_bazel_production_projects__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(555); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildBazelProductionProjects", function() { return _build_bazel_production_projects__WEBPACK_IMPORTED_MODULE_0__["buildBazelProductionProjects"]; }); -/* harmony import */ var _build_non_bazel_production_projects__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(796); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildNonBazelProductionProjects", function() { return _build_non_bazel_production_projects__WEBPACK_IMPORTED_MODULE_1__["buildNonBazelProductionProjects"]; }); -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. +/** + * Module dependencies */ +var toRegex = __webpack_require__(579); +var unique = __webpack_require__(599); +var extend = __webpack_require__(600); +/** + * Local dependencies + */ -/***/ }), -/* 555 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +var compilers = __webpack_require__(602); +var parsers = __webpack_require__(617); +var Braces = __webpack_require__(622); +var utils = __webpack_require__(603); +var MAX_LENGTH = 1024 * 64; +var cache = {}; -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "buildBazelProductionProjects", function() { return buildBazelProductionProjects; }); -/* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(556); -/* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(cpy__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var globby__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(768); -/* harmony import */ var globby__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(globby__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var _build_non_bazel_production_projects__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(796); -/* harmony import */ var _utils_bazel__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(413); -/* harmony import */ var _utils_fs__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(231); -/* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(220); -/* harmony import */ var _utils_package_json__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(343); -/* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(340); -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. +/** + * Convert the given `braces` pattern into a regex-compatible string. By default, only one string is generated for every input string. Set `options.expand` to true to return an array of patterns (similar to Bash or minimatch. Before using `options.expand`, it's recommended that you read the [performance notes](#performance)). + * + * ```js + * var braces = require('braces'); + * console.log(braces('{a,b,c}')); + * //=> ['(a|b|c)'] + * + * console.log(braces('{a,b,c}', {expand: true})); + * //=> ['a', 'b', 'c'] + * ``` + * @param {String} `str` + * @param {Object} `options` + * @return {String} + * @api public */ +function braces(pattern, options) { + var key = utils.createKey(String(pattern), options); + var arr = []; + var disabled = options && options.cache === false; + if (!disabled && cache.hasOwnProperty(key)) { + return cache[key]; + } + if (Array.isArray(pattern)) { + for (var i = 0; i < pattern.length; i++) { + arr.push.apply(arr, braces.create(pattern[i], options)); + } + } else { + arr = braces.create(pattern, options); + } + if (options && options.nodupes === true) { + arr = unique(arr); + } + if (!disabled) { + cache[key] = arr; + } + return arr; +} +/** + * Expands a brace pattern into an array. This method is called by the main [braces](#braces) function when `options.expand` is true. Before using this method it's recommended that you read the [performance notes](#performance)) and advantages of using [.optimize](#optimize) instead. + * + * ```js + * var braces = require('braces'); + * console.log(braces.expand('a/{b,c}/d')); + * //=> ['a/b/d', 'a/c/d']; + * ``` + * @param {String} `pattern` Brace pattern + * @param {Object} `options` + * @return {Array} Returns an array of expanded values. + * @api public + */ +braces.expand = function(pattern, options) { + return braces.create(pattern, extend({}, options, {expand: true})); +}; +/** + * Expands a brace pattern into a regex-compatible, optimized string. This method is called by the main [braces](#braces) function by default. + * + * ```js + * var braces = require('braces'); + * console.log(braces.expand('a/{b,c}/d')); + * //=> ['a/(b|c)/d'] + * ``` + * @param {String} `pattern` Brace pattern + * @param {Object} `options` + * @return {Array} Returns an array of expanded values. + * @api public + */ -async function buildBazelProductionProjects({ - kibanaRoot, - buildRoot, - onlyOSS -}) { - const projects = await Object(_utils_projects__WEBPACK_IMPORTED_MODULE_8__["getBazelProjectsOnly"])(await Object(_build_non_bazel_production_projects__WEBPACK_IMPORTED_MODULE_3__["getProductionProjects"])(kibanaRoot, onlyOSS)); - const projectNames = [...projects.values()].map(project => project.name); - _utils_log__WEBPACK_IMPORTED_MODULE_6__["log"].info(`Preparing Bazel projects production build for [${projectNames.join(', ')}]`); - await Object(_utils_bazel__WEBPACK_IMPORTED_MODULE_4__["runBazel"])(['build', '//packages:build']); - _utils_log__WEBPACK_IMPORTED_MODULE_6__["log"].info(`All Bazel projects production builds for [${projectNames.join(', ')}] are complete`); +braces.optimize = function(pattern, options) { + return braces.create(pattern, options); +}; - for (const project of projects.values()) { - await copyToBuild(project, kibanaRoot, buildRoot); - await applyCorrectPermissions(project, kibanaRoot, buildRoot); - } -} /** - * Copy all the project's files from its Bazel dist directory into the - * project build folder. + * Processes a brace pattern and returns either an expanded array (if `options.expand` is true), a highly optimized regex-compatible string. This method is called by the main [braces](#braces) function. * - * When copying all the files into the build, we exclude `node_modules` because - * we want the Kibana build to be responsible for actually installing all - * dependencies. The primary reason for allowing the Kibana build process to - * manage dependencies is that it will "dedupe" them, so we don't include - * unnecessary copies of dependencies. We also exclude every related Bazel build - * files in order to get the most cleaner package module we can in the final distributable. + * ```js + * var braces = require('braces'); + * console.log(braces.create('user-{200..300}/project-{a,b,c}-{1..10}')) + * //=> 'user-(20[0-9]|2[1-9][0-9]|300)/project-(a|b|c)-([1-9]|10)' + * ``` + * @param {String} `pattern` Brace pattern + * @param {Object} `options` + * @return {Array} Returns an array of expanded values. + * @api public */ -async function copyToBuild(project, kibanaRoot, buildRoot) { - // We want the package to have the same relative location within the build - const relativeProjectPath = Object(path__WEBPACK_IMPORTED_MODULE_2__["relative"])(kibanaRoot, project.path); - const buildProjectPath = Object(path__WEBPACK_IMPORTED_MODULE_2__["resolve"])(buildRoot, relativeProjectPath); - await cpy__WEBPACK_IMPORTED_MODULE_0___default()(['**/*'], buildProjectPath, { - cwd: Object(path__WEBPACK_IMPORTED_MODULE_2__["join"])(kibanaRoot, 'bazel-bin', 'packages', Object(path__WEBPACK_IMPORTED_MODULE_2__["basename"])(buildProjectPath), 'npm_module'), - dot: true, - onlyFiles: true, - parents: true - }); // If a project is using an intermediate build directory, we special-case our - // handling of `package.json`, as the project build process might have copied - // (a potentially modified) `package.json` into the intermediate build - // directory already. If so, we want to use that `package.json` as the basis - // for creating the production-ready `package.json`. If it's not present in - // the intermediate build, we fall back to using the project's already defined - // `package.json`. +braces.create = function(pattern, options) { + if (typeof pattern !== 'string') { + throw new TypeError('expected a string'); + } - const packageJson = (await Object(_utils_fs__WEBPACK_IMPORTED_MODULE_5__["isFile"])(Object(path__WEBPACK_IMPORTED_MODULE_2__["join"])(buildProjectPath, 'package.json'))) ? await Object(_utils_package_json__WEBPACK_IMPORTED_MODULE_7__["readPackageJson"])(buildProjectPath) : project.json; - const preparedPackageJson = Object(_utils_package_json__WEBPACK_IMPORTED_MODULE_7__["createProductionPackageJson"])(packageJson); - await Object(_utils_package_json__WEBPACK_IMPORTED_MODULE_7__["writePackageJson"])(buildProjectPath, preparedPackageJson); -} + var maxLength = (options && options.maxLength) || MAX_LENGTH; + if (pattern.length >= maxLength) { + throw new Error('expected pattern to be less than ' + maxLength + ' characters'); + } -async function applyCorrectPermissions(project, kibanaRoot, buildRoot) { - const relativeProjectPath = Object(path__WEBPACK_IMPORTED_MODULE_2__["relative"])(kibanaRoot, project.path); - const buildProjectPath = Object(path__WEBPACK_IMPORTED_MODULE_2__["resolve"])(buildRoot, relativeProjectPath); - const allPluginPaths = await globby__WEBPACK_IMPORTED_MODULE_1___default()([`**/*`], { - onlyFiles: false, - cwd: buildProjectPath, - dot: true - }); + function create() { + if (pattern === '' || pattern.length < 3) { + return [pattern]; + } - for (const pluginPath of allPluginPaths) { - const resolvedPluginPath = Object(path__WEBPACK_IMPORTED_MODULE_2__["resolve"])(buildProjectPath, pluginPath); + if (utils.isEmptySets(pattern)) { + return []; + } - if (await Object(_utils_fs__WEBPACK_IMPORTED_MODULE_5__["isFile"])(resolvedPluginPath)) { - await Object(_utils_fs__WEBPACK_IMPORTED_MODULE_5__["chmod"])(resolvedPluginPath, 0o644); + if (utils.isQuotedString(pattern)) { + return [pattern.slice(1, -1)]; } - if (await Object(_utils_fs__WEBPACK_IMPORTED_MODULE_5__["isDirectory"])(resolvedPluginPath)) { - await Object(_utils_fs__WEBPACK_IMPORTED_MODULE_5__["chmod"])(resolvedPluginPath, 0o755); + var proto = new Braces(options); + var result = !options || options.expand !== true + ? proto.optimize(pattern, options) + : proto.expand(pattern, options); + + // get the generated pattern(s) + var arr = result.output; + + // filter out empty strings if specified + if (options && options.noempty === true) { + arr = arr.filter(Boolean); } - } -} -/***/ }), -/* 556 */ -/***/ (function(module, exports, __webpack_require__) { + // filter out duplicates if specified + if (options && options.nodupes === true) { + arr = unique(arr); + } -"use strict"; + Object.defineProperty(arr, 'result', { + enumerable: false, + value: result + }); -const EventEmitter = __webpack_require__(164); -const path = __webpack_require__(4); -const os = __webpack_require__(122); -const pMap = __webpack_require__(557); -const arrify = __webpack_require__(552); -const globby = __webpack_require__(560); -const hasGlob = __webpack_require__(752); -const cpFile = __webpack_require__(754); -const junk = __webpack_require__(764); -const pFilter = __webpack_require__(765); -const CpyError = __webpack_require__(767); + return arr; + } -const defaultOptions = { - ignoreJunk: true + return memoize('create', pattern, options, create); }; -class SourceFile { - constructor(relativePath, path) { - this.path = path; - this.relativePath = relativePath; - Object.freeze(this); - } +/** + * Create a regular expression from the given string `pattern`. + * + * ```js + * var braces = require('braces'); + * + * console.log(braces.makeRe('id-{200..300}')); + * //=> /^(?:id-(20[0-9]|2[1-9][0-9]|300))$/ + * ``` + * @param {String} `pattern` The pattern to convert to regex. + * @param {Object} `options` + * @return {RegExp} + * @api public + */ - get name() { - return path.basename(this.relativePath); - } +braces.makeRe = function(pattern, options) { + if (typeof pattern !== 'string') { + throw new TypeError('expected a string'); + } - get nameWithoutExtension() { - return path.basename(this.relativePath, path.extname(this.relativePath)); - } + var maxLength = (options && options.maxLength) || MAX_LENGTH; + if (pattern.length >= maxLength) { + throw new Error('expected pattern to be less than ' + maxLength + ' characters'); + } - get extension() { - return path.extname(this.relativePath).slice(1); - } -} + function makeRe() { + var arr = braces(pattern, options); + var opts = extend({strictErrors: false}, options); + return toRegex(arr, opts); + } -const preprocessSourcePath = (source, options) => path.resolve(options.cwd ? options.cwd : process.cwd(), source); + return memoize('makeRe', pattern, options, makeRe); +}; -const preprocessDestinationPath = (source, destination, options) => { - let basename = path.basename(source); +/** + * Parse the given `str` with the given `options`. + * + * ```js + * var braces = require('braces'); + * var ast = braces.parse('a/{b,c}/d'); + * console.log(ast); + * // { type: 'root', + * // errors: [], + * // input: 'a/{b,c}/d', + * // nodes: + * // [ { type: 'bos', val: '' }, + * // { type: 'text', val: 'a/' }, + * // { type: 'brace', + * // nodes: + * // [ { type: 'brace.open', val: '{' }, + * // { type: 'text', val: 'b,c' }, + * // { type: 'brace.close', val: '}' } ] }, + * // { type: 'text', val: '/d' }, + * // { type: 'eos', val: '' } ] } + * ``` + * @param {String} `pattern` Brace pattern to parse + * @param {Object} `options` + * @return {Object} Returns an AST + * @api public + */ - if (typeof options.rename === 'string') { - basename = options.rename; - } else if (typeof options.rename === 'function') { - basename = options.rename(basename); - } +braces.parse = function(pattern, options) { + var proto = new Braces(options); + return proto.parse(pattern, options); +}; - if (options.cwd) { - destination = path.resolve(options.cwd, destination); - } +/** + * Compile the given `ast` or string with the given `options`. + * + * ```js + * var braces = require('braces'); + * var ast = braces.parse('a/{b,c}/d'); + * console.log(braces.compile(ast)); + * // { options: { source: 'string' }, + * // state: {}, + * // compilers: + * // { eos: [Function], + * // noop: [Function], + * // bos: [Function], + * // brace: [Function], + * // 'brace.open': [Function], + * // text: [Function], + * // 'brace.close': [Function] }, + * // output: [ 'a/(b|c)/d' ], + * // ast: + * // { ... }, + * // parsingErrors: [] } + * ``` + * @param {Object|String} `ast` AST from [.parse](#parse). If a string is passed it will be parsed first. + * @param {Object} `options` + * @return {Object} Returns an object that has an `output` property with the compiled string. + * @api public + */ - if (options.parents) { - const dirname = path.dirname(source); - const parsedDirectory = path.parse(dirname); - return path.join(destination, dirname.replace(parsedDirectory.root, path.sep), basename); - } +braces.compile = function(ast, options) { + var proto = new Braces(options); + return proto.compile(ast, options); +}; - return path.join(destination, basename); +/** + * Clear the regex cache. + * + * ```js + * braces.clearCache(); + * ``` + * @api public + */ + +braces.clearCache = function() { + cache = braces.cache = {}; }; -module.exports = (source, destination, { - concurrency = (os.cpus().length || 1) * 2, - ...options -} = {}) => { - const progressEmitter = new EventEmitter(); +/** + * Memoize a generated regex or function. A unique key is generated + * from the method name, pattern, and user-defined options. Set + * options.memoize to false to disable. + */ - options = { - ...defaultOptions, - ...options - }; +function memoize(type, pattern, options, fn) { + var key = utils.createKey(type + ':' + pattern, options); + var disabled = options && options.cache === false; + if (disabled) { + braces.clearCache(); + return fn(pattern, options); + } - const promise = (async () => { - source = arrify(source); + if (cache.hasOwnProperty(key)) { + return cache[key]; + } - if (source.length === 0 || !destination) { - throw new CpyError('`source` and `destination` required'); - } + var res = fn(pattern, options); + cache[key] = res; + return res; +} - const copyStatus = new Map(); - let completedFiles = 0; - let completedSize = 0; +/** + * Expose `Braces` constructor and methods + * @type {Function} + */ - let files; - try { - files = await globby(source, options); +braces.Braces = Braces; +braces.compilers = compilers; +braces.parsers = parsers; +braces.cache = cache; - if (options.ignoreJunk) { - files = files.filter(file => junk.not(path.basename(file))); - } - } catch (error) { - throw new CpyError(`Cannot glob \`${source}\`: ${error.message}`, error); - } +/** + * Expose `braces` + * @type {Function} + */ - if (files.length === 0 && !hasGlob(source)) { - throw new CpyError(`Cannot copy \`${source}\`: the file doesn't exist`); - } +module.exports = braces; - let sources = files.map(sourcePath => new SourceFile(sourcePath, preprocessSourcePath(sourcePath, options))); - if (options.filter !== undefined) { - const filteredSources = await pFilter(sources, options.filter, {concurrency: 1024}); - sources = filteredSources; - } +/***/ }), +/* 579 */ +/***/ (function(module, exports, __webpack_require__) { - if (sources.length === 0) { - progressEmitter.emit('progress', { - totalFiles: 0, - percent: 1, - completedFiles: 0, - completedSize: 0 - }); - } +"use strict"; - const fileProgressHandler = event => { - const fileStatus = copyStatus.get(event.src) || {written: 0, percent: 0}; - if (fileStatus.written !== event.written || fileStatus.percent !== event.percent) { - completedSize -= fileStatus.written; - completedSize += event.written; +var safe = __webpack_require__(580); +var define = __webpack_require__(586); +var extend = __webpack_require__(592); +var not = __webpack_require__(596); +var MAX_LENGTH = 1024 * 64; - if (event.percent === 1 && fileStatus.percent !== 1) { - completedFiles++; - } +/** + * Session cache + */ - copyStatus.set(event.src, { - written: event.written, - percent: event.percent - }); +var cache = {}; - progressEmitter.emit('progress', { - totalFiles: files.length, - percent: completedFiles / files.length, - completedFiles, - completedSize - }); - } - }; +/** + * Create a regular expression from the given `pattern` string. + * + * @param {String|RegExp} `pattern` Pattern can be a string or regular expression. + * @param {Object} `options` + * @return {RegExp} + * @api public + */ - return pMap(sources, async source => { - const to = preprocessDestinationPath(source.relativePath, destination, options); +module.exports = function(patterns, options) { + if (!Array.isArray(patterns)) { + return makeRe(patterns, options); + } + return makeRe(patterns.join('|'), options); +}; + +/** + * Create a regular expression from the given `pattern` string. + * + * @param {String|RegExp} `pattern` Pattern can be a string or regular expression. + * @param {Object} `options` + * @return {RegExp} + * @api public + */ + +function makeRe(pattern, options) { + if (pattern instanceof RegExp) { + return pattern; + } + + if (typeof pattern !== 'string') { + throw new TypeError('expected a string'); + } + + if (pattern.length > MAX_LENGTH) { + throw new Error('expected pattern to be less than ' + MAX_LENGTH + ' characters'); + } + + var key = pattern; + // do this before shallow cloning options, it's a lot faster + if (!options || (options && options.cache !== false)) { + key = createKey(pattern, options); + + if (cache.hasOwnProperty(key)) { + return cache[key]; + } + } - try { - await cpFile(source.path, to, options).on('progress', fileProgressHandler); - } catch (error) { - throw new CpyError(`Cannot copy from \`${source.relativePath}\` to \`${to}\`: ${error.message}`, error); - } + var opts = extend({}, options); + if (opts.contains === true) { + if (opts.negate === true) { + opts.strictNegate = false; + } else { + opts.strict = false; + } + } - return to; - }, {concurrency}); - })(); + if (opts.strict === false) { + opts.strictOpen = false; + opts.strictClose = false; + } - promise.on = (...arguments_) => { - progressEmitter.on(...arguments_); - return promise; - }; + var open = opts.strictOpen !== false ? '^' : ''; + var close = opts.strictClose !== false ? '$' : ''; + var flags = opts.flags || ''; + var regex; - return promise; -}; + if (opts.nocase === true && !/i/.test(flags)) { + flags += 'i'; + } + try { + if (opts.negate || typeof opts.strictNegate === 'boolean') { + pattern = not.create(pattern, opts); + } -/***/ }), -/* 557 */ -/***/ (function(module, exports, __webpack_require__) { + var str = open + '(?:' + pattern + ')' + close; + regex = new RegExp(str, flags); -"use strict"; + if (opts.safe === true && safe(regex) === false) { + throw new Error('potentially unsafe regular expression: ' + regex.source); + } -const AggregateError = __webpack_require__(558); + } catch (err) { + if (opts.strictErrors === true || opts.safe === true) { + err.key = key; + err.pattern = pattern; + err.originalOptions = options; + err.createdOptions = opts; + throw err; + } -module.exports = async ( - iterable, - mapper, - { - concurrency = Infinity, - stopOnError = true - } = {} -) => { - return new Promise((resolve, reject) => { - if (typeof mapper !== 'function') { - throw new TypeError('Mapper function is required'); - } + try { + regex = new RegExp('^' + pattern.replace(/(\W)/g, '\\$1') + '$'); + } catch (err) { + regex = /.^/; //<= match nothing + } + } - if (!(typeof concurrency === 'number' && concurrency >= 1)) { - throw new TypeError(`Expected \`concurrency\` to be a number from 1 and up, got \`${concurrency}\` (${typeof concurrency})`); - } + if (opts.cache !== false) { + memoize(regex, key, pattern, opts); + } + return regex; +} - const ret = []; - const errors = []; - const iterator = iterable[Symbol.iterator](); - let isRejected = false; - let isIterableDone = false; - let resolvingCount = 0; - let currentIndex = 0; +/** + * Memoize generated regex. This can result in dramatic speed improvements + * and simplify debugging by adding options and pattern to the regex. It can be + * disabled by passing setting `options.cache` to false. + */ - const next = () => { - if (isRejected) { - return; - } +function memoize(regex, key, pattern, options) { + define(regex, 'cached', true); + define(regex, 'pattern', pattern); + define(regex, 'options', options); + define(regex, 'key', key); + cache[key] = regex; +} - const nextItem = iterator.next(); - const i = currentIndex; - currentIndex++; +/** + * Create the key to use for memoization. The key is generated + * by iterating over the options and concatenating key-value pairs + * to the pattern string. + */ - if (nextItem.done) { - isIterableDone = true; +function createKey(pattern, options) { + if (!options) return pattern; + var key = pattern; + for (var prop in options) { + if (options.hasOwnProperty(prop)) { + key += ';' + prop + '=' + String(options[prop]); + } + } + return key; +} - if (resolvingCount === 0) { - if (!stopOnError && errors.length !== 0) { - reject(new AggregateError(errors)); - } else { - resolve(ret); - } - } +/** + * Expose `makeRe` + */ - return; - } +module.exports.makeRe = makeRe; - resolvingCount++; - (async () => { - try { - const element = await nextItem.value; - ret[i] = await mapper(element, i); - resolvingCount--; - next(); - } catch (error) { - if (stopOnError) { - isRejected = true; - reject(error); - } else { - errors.push(error); - resolvingCount--; - next(); - } - } - })(); - }; +/***/ }), +/* 580 */ +/***/ (function(module, exports, __webpack_require__) { - for (let i = 0; i < concurrency; i++) { - next(); +var parse = __webpack_require__(581); +var types = parse.types; - if (isIterableDone) { - break; - } - } - }); +module.exports = function (re, opts) { + if (!opts) opts = {}; + var replimit = opts.limit === undefined ? 25 : opts.limit; + + if (isRegExp(re)) re = re.source; + else if (typeof re !== 'string') re = String(re); + + try { re = parse(re) } + catch (err) { return false } + + var reps = 0; + return (function walk (node, starHeight) { + if (node.type === types.REPETITION) { + starHeight ++; + reps ++; + if (starHeight > 1) return false; + if (reps > replimit) return false; + } + + if (node.options) { + for (var i = 0, len = node.options.length; i < len; i++) { + var ok = walk({ stack: node.options[i] }, starHeight); + if (!ok) return false; + } + } + var stack = node.stack || (node.value && node.value.stack); + if (!stack) return true; + + for (var i = 0; i < stack.length; i++) { + var ok = walk(stack[i], starHeight); + if (!ok) return false; + } + + return true; + })(re, 0); }; +function isRegExp (x) { + return {}.toString.call(x) === '[object RegExp]'; +} + /***/ }), -/* 558 */ +/* 581 */ /***/ (function(module, exports, __webpack_require__) { -"use strict"; +var util = __webpack_require__(582); +var types = __webpack_require__(583); +var sets = __webpack_require__(584); +var positions = __webpack_require__(585); -const indentString = __webpack_require__(559); -const cleanStack = __webpack_require__(338); -const cleanInternalStack = stack => stack.replace(/\s+at .*aggregate-error\/index.js:\d+:\d+\)?/g, ''); +module.exports = function(regexpStr) { + var i = 0, l, c, + start = { type: types.ROOT, stack: []}, -class AggregateError extends Error { - constructor(errors) { - if (!Array.isArray(errors)) { - throw new TypeError(`Expected input to be an Array, got ${typeof errors}`); - } + // Keep track of last clause/group and stack. + lastGroup = start, + last = start.stack, + groupStack = []; - errors = [...errors].map(error => { - if (error instanceof Error) { - return error; - } - if (error !== null && typeof error === 'object') { - // Handle plain error objects with message property and/or possibly other metadata - return Object.assign(new Error(error.message), error); - } + var repeatErr = function(i) { + util.error(regexpStr, 'Nothing to repeat at column ' + (i - 1)); + }; - return new Error(error); - }); + // Decode a few escaped characters. + var str = util.strToChars(regexpStr); + l = str.length; - let message = errors - .map(error => { - // The `stack` property is not standardized, so we can't assume it exists - return typeof error.stack === 'string' ? cleanInternalStack(cleanStack(error.stack)) : String(error); - }) - .join('\n'); - message = '\n' + indentString(message, 4); - super(message); + // Iterate through each character in string. + while (i < l) { + c = str[i++]; - this.name = 'AggregateError'; + switch (c) { + // Handle escaped characters, inclues a few sets. + case '\\': + c = str[i++]; - Object.defineProperty(this, '_errors', {value: errors}); - } + switch (c) { + case 'b': + last.push(positions.wordBoundary()); + break; - * [Symbol.iterator]() { - for (const error of this._errors) { - yield error; - } - } -} + case 'B': + last.push(positions.nonWordBoundary()); + break; -module.exports = AggregateError; + case 'w': + last.push(sets.words()); + break; + case 'W': + last.push(sets.notWords()); + break; -/***/ }), -/* 559 */ -/***/ (function(module, exports, __webpack_require__) { + case 'd': + last.push(sets.ints()); + break; -"use strict"; + case 'D': + last.push(sets.notInts()); + break; + case 's': + last.push(sets.whitespace()); + break; -module.exports = (string, count = 1, options) => { - options = { - indent: ' ', - includeEmptyLines: false, - ...options - }; + case 'S': + last.push(sets.notWhitespace()); + break; - if (typeof string !== 'string') { - throw new TypeError( - `Expected \`input\` to be a \`string\`, got \`${typeof string}\`` - ); - } + default: + // Check if c is integer. + // In which case it's a reference. + if (/\d/.test(c)) { + last.push({ type: types.REFERENCE, value: parseInt(c, 10) }); - if (typeof count !== 'number') { - throw new TypeError( - `Expected \`count\` to be a \`number\`, got \`${typeof count}\`` - ); - } + // Escaped character. + } else { + last.push({ type: types.CHAR, value: c.charCodeAt(0) }); + } + } - if (typeof options.indent !== 'string') { - throw new TypeError( - `Expected \`options.indent\` to be a \`string\`, got \`${typeof options.indent}\`` - ); - } + break; - if (count === 0) { - return string; - } - const regex = options.includeEmptyLines ? /^/gm : /^(?!\s*$)/gm; + // Positionals. + case '^': + last.push(positions.begin()); + break; - return string.replace(regex, options.indent.repeat(count)); -}; + case '$': + last.push(positions.end()); + break; -/***/ }), -/* 560 */ -/***/ (function(module, exports, __webpack_require__) { + // Handle custom sets. + case '[': + // Check if this class is 'anti' i.e. [^abc]. + var not; + if (str[i] === '^') { + not = true; + i++; + } else { + not = false; + } -"use strict"; + // Get all the characters in class. + var classTokens = util.tokenizeClass(str.slice(i), regexpStr); -const fs = __webpack_require__(132); -const arrayUnion = __webpack_require__(561); -const glob = __webpack_require__(244); -const fastGlob = __webpack_require__(563); -const dirGlob = __webpack_require__(746); -const gitignore = __webpack_require__(749); + // Increase index by length of class. + i += classTokens[1]; + last.push({ + type: types.SET, + set: classTokens[0], + not: not, + }); -const DEFAULT_FILTER = () => false; + break; -const isNegative = pattern => pattern[0] === '!'; -const assertPatternsInput = patterns => { - if (!patterns.every(x => typeof x === 'string')) { - throw new TypeError('Patterns must be a string or an array of strings'); - } -}; + // Class of any character except \n. + case '.': + last.push(sets.anyChar()); + break; -const checkCwdOption = options => { - if (options && options.cwd && !fs.statSync(options.cwd).isDirectory()) { - throw new Error('The `cwd` option must be a path to a directory'); - } -}; -const generateGlobTasks = (patterns, taskOptions) => { - patterns = arrayUnion([].concat(patterns)); - assertPatternsInput(patterns); - checkCwdOption(taskOptions); + // Push group onto stack. + case '(': + // Create group. + var group = { + type: types.GROUP, + stack: [], + remember: true, + }; - const globTasks = []; + c = str[i]; - taskOptions = Object.assign({ - ignore: [], - expandDirectories: true - }, taskOptions); + // If if this is a special kind of group. + if (c === '?') { + c = str[i + 1]; + i += 2; - patterns.forEach((pattern, i) => { - if (isNegative(pattern)) { - return; - } + // Match if followed by. + if (c === '=') { + group.followedBy = true; - const ignore = patterns - .slice(i) - .filter(isNegative) - .map(pattern => pattern.slice(1)); + // Match if not followed by. + } else if (c === '!') { + group.notFollowedBy = true; - const options = Object.assign({}, taskOptions, { - ignore: taskOptions.ignore.concat(ignore) - }); + } else if (c !== ':') { + util.error(regexpStr, + 'Invalid group, character \'' + c + + '\' after \'?\' at column ' + (i - 1)); + } - globTasks.push({pattern, options}); - }); + group.remember = false; + } - return globTasks; -}; + // Insert subgroup into current group stack. + last.push(group); -const globDirs = (task, fn) => { - let options = {}; - if (task.options.cwd) { - options.cwd = task.options.cwd; - } + // Remember the current group for when the group closes. + groupStack.push(lastGroup); - if (Array.isArray(task.options.expandDirectories)) { - options = Object.assign(options, {files: task.options.expandDirectories}); - } else if (typeof task.options.expandDirectories === 'object') { - options = Object.assign(options, task.options.expandDirectories); - } + // Make this new group the current group. + lastGroup = group; + last = group.stack; + break; - return fn(task.pattern, options); -}; -const getPattern = (task, fn) => task.options.expandDirectories ? globDirs(task, fn) : [task.pattern]; + // Pop group out of stack. + case ')': + if (groupStack.length === 0) { + util.error(regexpStr, 'Unmatched ) at column ' + (i - 1)); + } + lastGroup = groupStack.pop(); -const globToTask = task => glob => { - const {options} = task; - if (options.ignore && Array.isArray(options.ignore) && options.expandDirectories) { - options.ignore = dirGlob.sync(options.ignore); - } + // Check if this group has a PIPE. + // To get back the correct last stack. + last = lastGroup.options ? + lastGroup.options[lastGroup.options.length - 1] : lastGroup.stack; + break; - return { - pattern: glob, - options - }; -}; -const globby = (patterns, options) => { - let globTasks; + // Use pipe character to give more choices. + case '|': + // Create array where options are if this is the first PIPE + // in this clause. + if (!lastGroup.options) { + lastGroup.options = [lastGroup.stack]; + delete lastGroup.stack; + } - try { - globTasks = generateGlobTasks(patterns, options); - } catch (error) { - return Promise.reject(error); - } + // Create a new stack and add to options for rest of clause. + var stack = []; + lastGroup.options.push(stack); + last = stack; + break; - const getTasks = Promise.all(globTasks.map(task => Promise.resolve(getPattern(task, dirGlob)) - .then(globs => Promise.all(globs.map(globToTask(task)))) - )) - .then(tasks => arrayUnion(...tasks)); - const getFilter = () => { - return Promise.resolve( - options && options.gitignore ? - gitignore({cwd: options.cwd, ignore: options.ignore}) : - DEFAULT_FILTER - ); - }; + // Repetition. + // For every repetition, remove last element from last stack + // then insert back a RANGE object. + // This design is chosen because there could be more than + // one repetition symbols in a regex i.e. `a?+{2,3}`. + case '{': + var rs = /^(\d+)(,(\d+)?)?\}/.exec(str.slice(i)), min, max; + if (rs !== null) { + if (last.length === 0) { + repeatErr(i); + } + min = parseInt(rs[1], 10); + max = rs[2] ? rs[3] ? parseInt(rs[3], 10) : Infinity : min; + i += rs[0].length; + + last.push({ + type: types.REPETITION, + min: min, + max: max, + value: last.pop(), + }); + } else { + last.push({ + type: types.CHAR, + value: 123, + }); + } + break; - return getFilter() - .then(filter => { - return getTasks - .then(tasks => Promise.all(tasks.map(task => fastGlob(task.pattern, task.options)))) - .then(paths => arrayUnion(...paths)) - .then(paths => paths.filter(p => !filter(p))); - }); -}; + case '?': + if (last.length === 0) { + repeatErr(i); + } + last.push({ + type: types.REPETITION, + min: 0, + max: 1, + value: last.pop(), + }); + break; -module.exports = globby; -// TODO: Remove this for the next major release -module.exports.default = globby; + case '+': + if (last.length === 0) { + repeatErr(i); + } + last.push({ + type: types.REPETITION, + min: 1, + max: Infinity, + value: last.pop(), + }); + break; -module.exports.sync = (patterns, options) => { - const globTasks = generateGlobTasks(patterns, options); + case '*': + if (last.length === 0) { + repeatErr(i); + } + last.push({ + type: types.REPETITION, + min: 0, + max: Infinity, + value: last.pop(), + }); + break; - const getFilter = () => { - return options && options.gitignore ? - gitignore.sync({cwd: options.cwd, ignore: options.ignore}) : - DEFAULT_FILTER; - }; - const tasks = globTasks.reduce((tasks, task) => { - const newTask = getPattern(task, dirGlob.sync).map(globToTask(task)); - return tasks.concat(newTask); - }, []); + // Default is a character that is not `\[](){}?+*^$`. + default: + last.push({ + type: types.CHAR, + value: c.charCodeAt(0), + }); + } - const filter = getFilter(); - return tasks.reduce( - (matches, task) => arrayUnion(matches, fastGlob.sync(task.pattern, task.options)), - [] - ).filter(p => !filter(p)); -}; + } -module.exports.generateGlobTasks = generateGlobTasks; + // Check if any groups have not been closed. + if (groupStack.length !== 0) { + util.error(regexpStr, 'Unterminated group'); + } -module.exports.hasMagic = (patterns, options) => [] - .concat(patterns) - .some(pattern => glob.hasMagic(pattern, options)); + return start; +}; -module.exports.gitignore = gitignore; +module.exports.types = types; /***/ }), -/* 561 */ +/* 582 */ /***/ (function(module, exports, __webpack_require__) { -"use strict"; - -var arrayUniq = __webpack_require__(562); +var types = __webpack_require__(583); +var sets = __webpack_require__(584); -module.exports = function () { - return arrayUniq([].concat.apply([], arguments)); -}; +// All of these are private and only used by randexp. +// It's assumed that they will always be called with the correct input. -/***/ }), -/* 562 */ -/***/ (function(module, exports, __webpack_require__) { +var CTRL = '@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^ ?'; +var SLSH = { '0': 0, 't': 9, 'n': 10, 'v': 11, 'f': 12, 'r': 13 }; -"use strict"; +/** + * Finds character representations in str and convert all to + * their respective characters + * + * @param {String} str + * @return {String} + */ +exports.strToChars = function(str) { + /* jshint maxlen: false */ + var chars_regex = /(\[\\b\])|(\\)?\\(?:u([A-F0-9]{4})|x([A-F0-9]{2})|(0?[0-7]{2})|c([@A-Z\[\\\]\^?])|([0tnvfr]))/g; + str = str.replace(chars_regex, function(s, b, lbs, a16, b16, c8, dctrl, eslsh) { + if (lbs) { + return s; + } + var code = b ? 8 : + a16 ? parseInt(a16, 16) : + b16 ? parseInt(b16, 16) : + c8 ? parseInt(c8, 8) : + dctrl ? CTRL.indexOf(dctrl) : + SLSH[eslsh]; -// there's 3 implementations written in increasing order of efficiency + var c = String.fromCharCode(code); -// 1 - no Set type is defined -function uniqNoSet(arr) { - var ret = []; + // Escape special regex characters. + if (/[\[\]{}\^$.|?*+()]/.test(c)) { + c = '\\' + c; + } - for (var i = 0; i < arr.length; i++) { - if (ret.indexOf(arr[i]) === -1) { - ret.push(arr[i]); - } - } + return c; + }); - return ret; -} + return str; +}; -// 2 - a simple Set type is defined -function uniqSet(arr) { - var seen = new Set(); - return arr.filter(function (el) { - if (!seen.has(el)) { - seen.add(el); - return true; - } - return false; - }); -} +/** + * turns class into tokens + * reads str until it encounters a ] not preceeded by a \ + * + * @param {String} str + * @param {String} regexpStr + * @return {Array., Number>} + */ +exports.tokenizeClass = function(str, regexpStr) { + /* jshint maxlen: false */ + var tokens = []; + var regexp = /\\(?:(w)|(d)|(s)|(W)|(D)|(S))|((?:(?:\\)(.)|([^\]\\]))-(?:\\)?([^\]]))|(\])|(?:\\)?(.)/g; + var rs, c; -// 3 - a standard Set type is defined and it has a forEach method -function uniqSetWithForEach(arr) { - var ret = []; - (new Set(arr)).forEach(function (el) { - ret.push(el); - }); + while ((rs = regexp.exec(str)) != null) { + if (rs[1]) { + tokens.push(sets.words()); - return ret; -} + } else if (rs[2]) { + tokens.push(sets.ints()); -// V8 currently has a broken implementation -// https://github.com/joyent/node/issues/8449 -function doesForEachActuallyWork() { - var ret = false; + } else if (rs[3]) { + tokens.push(sets.whitespace()); - (new Set([true])).forEach(function (el) { - ret = el; - }); + } else if (rs[4]) { + tokens.push(sets.notWords()); - return ret === true; -} + } else if (rs[5]) { + tokens.push(sets.notInts()); -if ('Set' in global) { - if (typeof Set.prototype.forEach === 'function' && doesForEachActuallyWork()) { - module.exports = uniqSetWithForEach; - } else { - module.exports = uniqSet; - } -} else { - module.exports = uniqNoSet; -} + } else if (rs[6]) { + tokens.push(sets.notWhitespace()); + } else if (rs[7]) { + tokens.push({ + type: types.RANGE, + from: (rs[8] || rs[9]).charCodeAt(0), + to: rs[10].charCodeAt(0), + }); -/***/ }), -/* 563 */ -/***/ (function(module, exports, __webpack_require__) { + } else if (c = rs[12]) { + tokens.push({ + type: types.CHAR, + value: c.charCodeAt(0), + }); -const pkg = __webpack_require__(564); + } else { + return [tokens, regexp.lastIndex]; + } + } -module.exports = pkg.async; -module.exports.default = pkg.async; + exports.error(regexpStr, 'Unterminated character class'); +}; -module.exports.async = pkg.async; -module.exports.sync = pkg.sync; -module.exports.stream = pkg.stream; -module.exports.generateTasks = pkg.generateTasks; +/** + * Shortcut to throw errors. + * + * @param {String} regexp + * @param {String} msg + */ +exports.error = function(regexp, msg) { + throw new SyntaxError('Invalid regular expression: /' + regexp + '/: ' + msg); +}; /***/ }), -/* 564 */ -/***/ (function(module, exports, __webpack_require__) { +/* 583 */ +/***/ (function(module, exports) { -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -var optionsManager = __webpack_require__(565); -var taskManager = __webpack_require__(566); -var reader_async_1 = __webpack_require__(717); -var reader_stream_1 = __webpack_require__(741); -var reader_sync_1 = __webpack_require__(742); -var arrayUtils = __webpack_require__(744); -var streamUtils = __webpack_require__(745); -/** - * Synchronous API. - */ -function sync(source, opts) { - assertPatternsInput(source); - var works = getWorks(source, reader_sync_1.default, opts); - return arrayUtils.flatten(works); -} -exports.sync = sync; -/** - * Asynchronous API. - */ -function async(source, opts) { - try { - assertPatternsInput(source); - } - catch (error) { - return Promise.reject(error); - } - var works = getWorks(source, reader_async_1.default, opts); - return Promise.all(works).then(arrayUtils.flatten); -} -exports.async = async; -/** - * Stream API. - */ -function stream(source, opts) { - assertPatternsInput(source); - var works = getWorks(source, reader_stream_1.default, opts); - return streamUtils.merge(works); -} -exports.stream = stream; -/** - * Return a set of tasks based on provided patterns. - */ -function generateTasks(source, opts) { - assertPatternsInput(source); - var patterns = [].concat(source); - var options = optionsManager.prepare(opts); - return taskManager.generate(patterns, options); -} -exports.generateTasks = generateTasks; -/** - * Returns a set of works based on provided tasks and class of the reader. - */ -function getWorks(source, _Reader, opts) { - var patterns = [].concat(source); - var options = optionsManager.prepare(opts); - var tasks = taskManager.generate(patterns, options); - var reader = new _Reader(options); - return tasks.map(reader.read, reader); -} -function assertPatternsInput(source) { - if ([].concat(source).every(isString)) { - return; - } - throw new TypeError('Patterns must be a string or an array of strings'); -} -function isString(source) { - /* tslint:disable-next-line strict-type-predicates */ - return typeof source === 'string'; -} +module.exports = { + ROOT : 0, + GROUP : 1, + POSITION : 2, + SET : 3, + RANGE : 4, + REPETITION : 5, + REFERENCE : 6, + CHAR : 7, +}; /***/ }), -/* 565 */ +/* 584 */ /***/ (function(module, exports, __webpack_require__) { -"use strict"; - -var __assign = (this && this.__assign) || function () { - __assign = Object.assign || function(t) { - for (var s, i = 1, n = arguments.length; i < n; i++) { - s = arguments[i]; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) - t[p] = s[p]; - } - return t; - }; - return __assign.apply(this, arguments); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -function prepare(options) { - var opts = __assign({ cwd: process.cwd(), deep: true, ignore: [], dot: false, stats: false, onlyFiles: true, onlyDirectories: false, followSymlinkedDirectories: true, unique: true, markDirectories: false, absolute: false, nobrace: false, brace: true, noglobstar: false, globstar: true, noext: false, extension: true, nocase: false, case: true, matchBase: false, transform: null }, options); - if (opts.onlyDirectories) { - opts.onlyFiles = false; - } - opts.brace = !opts.nobrace; - opts.globstar = !opts.noglobstar; - opts.extension = !opts.noext; - opts.case = !opts.nocase; - if (options) { - opts.brace = ('brace' in options ? options.brace : opts.brace); - opts.globstar = ('globstar' in options ? options.globstar : opts.globstar); - opts.extension = ('extension' in options ? options.extension : opts.extension); - opts.case = ('case' in options ? options.case : opts.case); - } - return opts; -} -exports.prepare = prepare; +var types = __webpack_require__(583); +var INTS = function() { + return [{ type: types.RANGE , from: 48, to: 57 }]; +}; -/***/ }), -/* 566 */ -/***/ (function(module, exports, __webpack_require__) { +var WORDS = function() { + return [ + { type: types.CHAR, value: 95 }, + { type: types.RANGE, from: 97, to: 122 }, + { type: types.RANGE, from: 65, to: 90 } + ].concat(INTS()); +}; -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -var patternUtils = __webpack_require__(567); -/** - * Generate tasks based on parent directory of each pattern. - */ -function generate(patterns, options) { - var unixPatterns = patterns.map(patternUtils.unixifyPattern); - var unixIgnore = options.ignore.map(patternUtils.unixifyPattern); - var positivePatterns = getPositivePatterns(unixPatterns); - var negativePatterns = getNegativePatternsAsPositive(unixPatterns, unixIgnore); - /** - * When the `case` option is disabled, all patterns must be marked as dynamic, because we cannot check filepath - * directly (without read directory). - */ - var staticPatterns = !options.case ? [] : positivePatterns.filter(patternUtils.isStaticPattern); - var dynamicPatterns = !options.case ? positivePatterns : positivePatterns.filter(patternUtils.isDynamicPattern); - var staticTasks = convertPatternsToTasks(staticPatterns, negativePatterns, /* dynamic */ false); - var dynamicTasks = convertPatternsToTasks(dynamicPatterns, negativePatterns, /* dynamic */ true); - return staticTasks.concat(dynamicTasks); -} -exports.generate = generate; -/** - * Convert patterns to tasks based on parent directory of each pattern. - */ -function convertPatternsToTasks(positive, negative, dynamic) { - var positivePatternsGroup = groupPatternsByBaseDirectory(positive); - // When we have a global group – there is no reason to divide the patterns into independent tasks. - // In this case, the global task covers the rest. - if ('.' in positivePatternsGroup) { - var task = convertPatternGroupToTask('.', positive, negative, dynamic); - return [task]; - } - return convertPatternGroupsToTasks(positivePatternsGroup, negative, dynamic); -} -exports.convertPatternsToTasks = convertPatternsToTasks; -/** - * Return only positive patterns. - */ -function getPositivePatterns(patterns) { - return patternUtils.getPositivePatterns(patterns); -} -exports.getPositivePatterns = getPositivePatterns; -/** - * Return only negative patterns. - */ -function getNegativePatternsAsPositive(patterns, ignore) { - var negative = patternUtils.getNegativePatterns(patterns).concat(ignore); - var positive = negative.map(patternUtils.convertToPositivePattern); - return positive; -} -exports.getNegativePatternsAsPositive = getNegativePatternsAsPositive; -/** - * Group patterns by base directory of each pattern. - */ -function groupPatternsByBaseDirectory(patterns) { - return patterns.reduce(function (collection, pattern) { - var base = patternUtils.getBaseDirectory(pattern); - if (base in collection) { - collection[base].push(pattern); - } - else { - collection[base] = [pattern]; - } - return collection; - }, {}); -} -exports.groupPatternsByBaseDirectory = groupPatternsByBaseDirectory; -/** - * Convert group of patterns to tasks. - */ -function convertPatternGroupsToTasks(positive, negative, dynamic) { - return Object.keys(positive).map(function (base) { - return convertPatternGroupToTask(base, positive[base], negative, dynamic); - }); -} -exports.convertPatternGroupsToTasks = convertPatternGroupsToTasks; -/** - * Create a task for positive and negative patterns. - */ -function convertPatternGroupToTask(base, positive, negative, dynamic) { - return { - base: base, - dynamic: dynamic, - positive: positive, - negative: negative, - patterns: [].concat(positive, negative.map(patternUtils.convertToNegativePattern)) - }; -} -exports.convertPatternGroupToTask = convertPatternGroupToTask; +var WHITESPACE = function() { + return [ + { type: types.CHAR, value: 9 }, + { type: types.CHAR, value: 10 }, + { type: types.CHAR, value: 11 }, + { type: types.CHAR, value: 12 }, + { type: types.CHAR, value: 13 }, + { type: types.CHAR, value: 32 }, + { type: types.CHAR, value: 160 }, + { type: types.CHAR, value: 5760 }, + { type: types.CHAR, value: 6158 }, + { type: types.CHAR, value: 8192 }, + { type: types.CHAR, value: 8193 }, + { type: types.CHAR, value: 8194 }, + { type: types.CHAR, value: 8195 }, + { type: types.CHAR, value: 8196 }, + { type: types.CHAR, value: 8197 }, + { type: types.CHAR, value: 8198 }, + { type: types.CHAR, value: 8199 }, + { type: types.CHAR, value: 8200 }, + { type: types.CHAR, value: 8201 }, + { type: types.CHAR, value: 8202 }, + { type: types.CHAR, value: 8232 }, + { type: types.CHAR, value: 8233 }, + { type: types.CHAR, value: 8239 }, + { type: types.CHAR, value: 8287 }, + { type: types.CHAR, value: 12288 }, + { type: types.CHAR, value: 65279 } + ]; +}; + +var NOTANYCHAR = function() { + return [ + { type: types.CHAR, value: 10 }, + { type: types.CHAR, value: 13 }, + { type: types.CHAR, value: 8232 }, + { type: types.CHAR, value: 8233 }, + ]; +}; + +// Predefined class objects. +exports.words = function() { + return { type: types.SET, set: WORDS(), not: false }; +}; + +exports.notWords = function() { + return { type: types.SET, set: WORDS(), not: true }; +}; + +exports.ints = function() { + return { type: types.SET, set: INTS(), not: false }; +}; + +exports.notInts = function() { + return { type: types.SET, set: INTS(), not: true }; +}; + +exports.whitespace = function() { + return { type: types.SET, set: WHITESPACE(), not: false }; +}; + +exports.notWhitespace = function() { + return { type: types.SET, set: WHITESPACE(), not: true }; +}; + +exports.anyChar = function() { + return { type: types.SET, set: NOTANYCHAR(), not: true }; +}; /***/ }), -/* 567 */ +/* 585 */ /***/ (function(module, exports, __webpack_require__) { -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -var path = __webpack_require__(4); -var globParent = __webpack_require__(568); -var isGlob = __webpack_require__(266); -var micromatch = __webpack_require__(571); -var GLOBSTAR = '**'; -/** - * Return true for static pattern. - */ -function isStaticPattern(pattern) { - return !isDynamicPattern(pattern); -} -exports.isStaticPattern = isStaticPattern; -/** - * Return true for pattern that looks like glob. - */ -function isDynamicPattern(pattern) { - return isGlob(pattern, { strict: false }); -} -exports.isDynamicPattern = isDynamicPattern; -/** - * Convert a windows «path» to a unix-style «path». - */ -function unixifyPattern(pattern) { - return pattern.replace(/\\/g, '/'); -} -exports.unixifyPattern = unixifyPattern; -/** - * Returns negative pattern as positive pattern. - */ -function convertToPositivePattern(pattern) { - return isNegativePattern(pattern) ? pattern.slice(1) : pattern; -} -exports.convertToPositivePattern = convertToPositivePattern; -/** - * Returns positive pattern as negative pattern. - */ -function convertToNegativePattern(pattern) { - return '!' + pattern; -} -exports.convertToNegativePattern = convertToNegativePattern; -/** - * Return true if provided pattern is negative pattern. - */ -function isNegativePattern(pattern) { - return pattern.startsWith('!') && pattern[1] !== '('; -} -exports.isNegativePattern = isNegativePattern; -/** - * Return true if provided pattern is positive pattern. - */ -function isPositivePattern(pattern) { - return !isNegativePattern(pattern); -} -exports.isPositivePattern = isPositivePattern; -/** - * Extracts negative patterns from array of patterns. - */ -function getNegativePatterns(patterns) { - return patterns.filter(isNegativePattern); -} -exports.getNegativePatterns = getNegativePatterns; -/** - * Extracts positive patterns from array of patterns. - */ -function getPositivePatterns(patterns) { - return patterns.filter(isPositivePattern); -} -exports.getPositivePatterns = getPositivePatterns; -/** - * Extract base directory from provided pattern. - */ -function getBaseDirectory(pattern) { - return globParent(pattern); -} -exports.getBaseDirectory = getBaseDirectory; -/** - * Return true if provided pattern has globstar. - */ -function hasGlobStar(pattern) { - return pattern.indexOf(GLOBSTAR) !== -1; -} -exports.hasGlobStar = hasGlobStar; -/** - * Return true if provided pattern ends with slash and globstar. - */ -function endsWithSlashGlobStar(pattern) { - return pattern.endsWith('/' + GLOBSTAR); -} -exports.endsWithSlashGlobStar = endsWithSlashGlobStar; -/** - * Returns «true» when pattern ends with a slash and globstar or the last partial of the pattern is static pattern. - */ -function isAffectDepthOfReadingPattern(pattern) { - var basename = path.basename(pattern); - return endsWithSlashGlobStar(pattern) || isStaticPattern(basename); -} -exports.isAffectDepthOfReadingPattern = isAffectDepthOfReadingPattern; -/** - * Return naive depth of provided pattern without depth of the base directory. - */ -function getNaiveDepth(pattern) { - var base = getBaseDirectory(pattern); - var patternDepth = pattern.split('/').length; - var patternBaseDepth = base.split('/').length; - /** - * This is a hack for pattern that has no base directory. - * - * This is related to the `*\something\*` pattern. - */ - if (base === '.') { - return patternDepth - patternBaseDepth; - } - return patternDepth - patternBaseDepth - 1; -} -exports.getNaiveDepth = getNaiveDepth; -/** - * Return max naive depth of provided patterns without depth of the base directory. - */ -function getMaxNaivePatternsDepth(patterns) { - return patterns.reduce(function (max, pattern) { - var depth = getNaiveDepth(pattern); - return depth > max ? depth : max; - }, 0); -} -exports.getMaxNaivePatternsDepth = getMaxNaivePatternsDepth; -/** - * Make RegExp for provided pattern. - */ -function makeRe(pattern, options) { - return micromatch.makeRe(pattern, options); -} -exports.makeRe = makeRe; -/** - * Convert patterns to regexps. - */ -function convertPatternsToRe(patterns, options) { - return patterns.map(function (pattern) { return makeRe(pattern, options); }); -} -exports.convertPatternsToRe = convertPatternsToRe; -/** - * Returns true if the entry match any of the given RegExp's. - */ -function matchAny(entry, patternsRe) { - return patternsRe.some(function (patternRe) { return patternRe.test(entry); }); -} -exports.matchAny = matchAny; +var types = __webpack_require__(583); + +exports.wordBoundary = function() { + return { type: types.POSITION, value: 'b' }; +}; + +exports.nonWordBoundary = function() { + return { type: types.POSITION, value: 'B' }; +}; + +exports.begin = function() { + return { type: types.POSITION, value: '^' }; +}; + +exports.end = function() { + return { type: types.POSITION, value: '$' }; +}; /***/ }), -/* 568 */ +/* 586 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; +/*! + * define-property + * + * Copyright (c) 2015-2018, Jon Schlinkert. + * Released under the MIT License. + */ -var path = __webpack_require__(4); -var isglob = __webpack_require__(569); -var pathDirname = __webpack_require__(570); -var isWin32 = __webpack_require__(122).platform() === 'win32'; -module.exports = function globParent(str) { - // flip windows path separators - if (isWin32 && str.indexOf('/') < 0) str = str.split('\\').join('/'); +var isobject = __webpack_require__(587); +var isDescriptor = __webpack_require__(588); +var define = (typeof Reflect !== 'undefined' && Reflect.defineProperty) + ? Reflect.defineProperty + : Object.defineProperty; - // special case for strings ending in enclosure containing path separator - if (/[\{\[].*[\/]*.*[\}\]]$/.test(str)) str += '/'; +module.exports = function defineProperty(obj, key, val) { + if (!isobject(obj) && typeof obj !== 'function' && !Array.isArray(obj)) { + throw new TypeError('expected an object, function, or array'); + } - // preserves full path in case of trailing path separator - str += 'a'; + if (typeof key !== 'string') { + throw new TypeError('expected "key" to be a string'); + } - // remove path parts that are globby - do {str = pathDirname.posix(str)} - while (isglob(str) || /(^|[^\\])([\{\[]|\([^\)]+$)/.test(str)); + if (isDescriptor(val)) { + define(obj, key, val); + return obj; + } - // remove escape chars and return result - return str.replace(/\\([\*\?\|\[\]\(\)\{\}])/g, '$1'); + define(obj, key, { + configurable: true, + enumerable: false, + writable: true, + value: val + }); + + return obj; }; /***/ }), -/* 569 */ +/* 587 */ /***/ (function(module, exports, __webpack_require__) { +"use strict"; /*! - * is-glob + * isobject * - * Copyright (c) 2014-2016, Jon Schlinkert. - * Licensed under the MIT License. + * Copyright (c) 2014-2017, Jon Schlinkert. + * Released under the MIT License. */ -var isExtglob = __webpack_require__(267); -module.exports = function isGlob(str) { - if (typeof str !== 'string' || str === '') { - return false; - } - if (isExtglob(str)) return true; +module.exports = function isObject(val) { + return val != null && typeof val === 'object' && Array.isArray(val) === false; +}; - var regex = /(\\).|([*?]|\[.*\]|\{.*\}|\(.*\|.*\)|^!)/; - var match; - while ((match = regex.exec(str))) { - if (match[2]) return true; - str = str.slice(match.index + match[0].length); +/***/ }), +/* 588 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +/*! + * is-descriptor + * + * Copyright (c) 2015-2017, Jon Schlinkert. + * Released under the MIT License. + */ + + + +var typeOf = __webpack_require__(589); +var isAccessor = __webpack_require__(590); +var isData = __webpack_require__(591); + +module.exports = function isDescriptor(obj, key) { + if (typeOf(obj) !== 'object') { + return false; } - return false; + if ('get' in obj) { + return isAccessor(obj, key); + } + return isData(obj, key); }; /***/ }), -/* 570 */ -/***/ (function(module, exports, __webpack_require__) { +/* 589 */ +/***/ (function(module, exports) { -"use strict"; +var toString = Object.prototype.toString; +module.exports = function kindOf(val) { + if (val === void 0) return 'undefined'; + if (val === null) return 'null'; -var path = __webpack_require__(4); -var inspect = __webpack_require__(113).inspect; + var type = typeof val; + if (type === 'boolean') return 'boolean'; + if (type === 'string') return 'string'; + if (type === 'number') return 'number'; + if (type === 'symbol') return 'symbol'; + if (type === 'function') { + return isGeneratorFn(val) ? 'generatorfunction' : 'function'; + } -function assertPath(path) { - if (typeof path !== 'string') { - throw new TypeError('Path must be a string. Received ' + inspect(path)); + if (isArray(val)) return 'array'; + if (isBuffer(val)) return 'buffer'; + if (isArguments(val)) return 'arguments'; + if (isDate(val)) return 'date'; + if (isError(val)) return 'error'; + if (isRegexp(val)) return 'regexp'; + + switch (ctorName(val)) { + case 'Symbol': return 'symbol'; + case 'Promise': return 'promise'; + + // Set, Map, WeakSet, WeakMap + case 'WeakMap': return 'weakmap'; + case 'WeakSet': return 'weakset'; + case 'Map': return 'map'; + case 'Set': return 'set'; + + // 8-bit typed arrays + case 'Int8Array': return 'int8array'; + case 'Uint8Array': return 'uint8array'; + case 'Uint8ClampedArray': return 'uint8clampedarray'; + + // 16-bit typed arrays + case 'Int16Array': return 'int16array'; + case 'Uint16Array': return 'uint16array'; + + // 32-bit typed arrays + case 'Int32Array': return 'int32array'; + case 'Uint32Array': return 'uint32array'; + case 'Float32Array': return 'float32array'; + case 'Float64Array': return 'float64array'; } -} -function posix(path) { - assertPath(path); - if (path.length === 0) - return '.'; - var code = path.charCodeAt(0); - var hasRoot = (code === 47/*/*/); - var end = -1; - var matchedSlash = true; - for (var i = path.length - 1; i >= 1; --i) { - code = path.charCodeAt(i); - if (code === 47/*/*/) { - if (!matchedSlash) { - end = i; - break; - } - } else { - // We saw the first non-path separator - matchedSlash = false; - } + if (isGeneratorObj(val)) { + return 'generator'; } - if (end === -1) - return hasRoot ? '/' : '.'; - if (hasRoot && end === 1) - return '//'; - return path.slice(0, end); + // Non-plain objects + type = toString.call(val); + switch (type) { + case '[object Object]': return 'object'; + // iterators + case '[object Map Iterator]': return 'mapiterator'; + case '[object Set Iterator]': return 'setiterator'; + case '[object String Iterator]': return 'stringiterator'; + case '[object Array Iterator]': return 'arrayiterator'; + } + + // other + return type.slice(8, -1).toLowerCase().replace(/\s/g, ''); +}; + +function ctorName(val) { + return typeof val.constructor === 'function' ? val.constructor.name : null; } -function win32(path) { - assertPath(path); - var len = path.length; - if (len === 0) - return '.'; - var rootEnd = -1; - var end = -1; - var matchedSlash = true; - var offset = 0; - var code = path.charCodeAt(0); +function isArray(val) { + if (Array.isArray) return Array.isArray(val); + return val instanceof Array; +} - // Try to match a root - if (len > 1) { - if (code === 47/*/*/ || code === 92/*\*/) { - // Possible UNC root +function isError(val) { + return val instanceof Error || (typeof val.message === 'string' && val.constructor && typeof val.constructor.stackTraceLimit === 'number'); +} - rootEnd = offset = 1; +function isDate(val) { + if (val instanceof Date) return true; + return typeof val.toDateString === 'function' + && typeof val.getDate === 'function' + && typeof val.setDate === 'function'; +} - code = path.charCodeAt(1); - if (code === 47/*/*/ || code === 92/*\*/) { - // Matched double path separator at beginning - var j = 2; - var last = j; - // Match 1 or more non-path separators - for (; j < len; ++j) { - code = path.charCodeAt(j); - if (code === 47/*/*/ || code === 92/*\*/) - break; - } - if (j < len && j !== last) { - // Matched! - last = j; - // Match 1 or more path separators - for (; j < len; ++j) { - code = path.charCodeAt(j); - if (code !== 47/*/*/ && code !== 92/*\*/) - break; - } - if (j < len && j !== last) { - // Matched! - last = j; - // Match 1 or more non-path separators - for (; j < len; ++j) { - code = path.charCodeAt(j); - if (code === 47/*/*/ || code === 92/*\*/) - break; - } - if (j === len) { - // We matched a UNC root only - return path; - } - if (j !== last) { - // We matched a UNC root with leftovers +function isRegexp(val) { + if (val instanceof RegExp) return true; + return typeof val.flags === 'string' + && typeof val.ignoreCase === 'boolean' + && typeof val.multiline === 'boolean' + && typeof val.global === 'boolean'; +} - // Offset by 1 to include the separator after the UNC root to - // treat it as a "normal root" on top of a (UNC) root - rootEnd = offset = j + 1; - } - } - } - } - } else if ((code >= 65/*A*/ && code <= 90/*Z*/) || - (code >= 97/*a*/ && code <= 122/*z*/)) { - // Possible device root +function isGeneratorFn(name, val) { + return ctorName(name) === 'GeneratorFunction'; +} - code = path.charCodeAt(1); - if (path.charCodeAt(1) === 58/*:*/) { - rootEnd = offset = 2; - if (len > 2) { - code = path.charCodeAt(2); - if (code === 47/*/*/ || code === 92/*\*/) - rootEnd = offset = 3; - } - } - } - } else if (code === 47/*/*/ || code === 92/*\*/) { - return path[0]; - } +function isGeneratorObj(val) { + return typeof val.throw === 'function' + && typeof val.return === 'function' + && typeof val.next === 'function'; +} - for (var i = len - 1; i >= offset; --i) { - code = path.charCodeAt(i); - if (code === 47/*/*/ || code === 92/*\*/) { - if (!matchedSlash) { - end = i; - break; - } - } else { - // We saw the first non-path separator - matchedSlash = false; +function isArguments(val) { + try { + if (typeof val.length === 'number' && typeof val.callee === 'function') { + return true; + } + } catch (err) { + if (err.message.indexOf('callee') !== -1) { + return true; } } + return false; +} - if (end === -1) { - if (rootEnd === -1) - return '.'; - else - end = rootEnd; +/** + * If you need to support Safari 5-7 (8-10 yr-old browser), + * take a look at https://github.com/feross/is-buffer + */ + +function isBuffer(val) { + if (val.constructor && typeof val.constructor.isBuffer === 'function') { + return val.constructor.isBuffer(val); } - return path.slice(0, end); + return false; } -module.exports = process.platform === 'win32' ? win32 : posix; -module.exports.posix = posix; -module.exports.win32 = win32; - /***/ }), -/* 571 */ +/* 590 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - - -/** - * Module dependencies +/*! + * is-accessor-descriptor + * + * Copyright (c) 2015-2017, Jon Schlinkert. + * Released under the MIT License. */ -var util = __webpack_require__(113); -var braces = __webpack_require__(572); -var toRegex = __webpack_require__(573); -var extend = __webpack_require__(685); -/** - * Local dependencies - */ -var compilers = __webpack_require__(687); -var parsers = __webpack_require__(713); -var cache = __webpack_require__(714); -var utils = __webpack_require__(715); -var MAX_LENGTH = 1024 * 64; +var typeOf = __webpack_require__(589); -/** - * The main function takes a list of strings and one or more - * glob patterns to use for matching. - * - * ```js - * var mm = require('micromatch'); - * mm(list, patterns[, options]); - * - * console.log(mm(['a.js', 'a.txt'], ['*.js'])); - * //=> [ 'a.js' ] - * ``` - * @param {Array} `list` A list of strings to match - * @param {String|Array} `patterns` One or more glob patterns to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Array} Returns an array of matches - * @summary false - * @api public - */ +// accessor descriptor properties +var accessor = { + get: 'function', + set: 'function', + configurable: 'boolean', + enumerable: 'boolean' +}; -function micromatch(list, patterns, options) { - patterns = utils.arrayify(patterns); - list = utils.arrayify(list); +function isAccessorDescriptor(obj, prop) { + if (typeof prop === 'string') { + var val = Object.getOwnPropertyDescriptor(obj, prop); + return typeof val !== 'undefined'; + } - var len = patterns.length; - if (list.length === 0 || len === 0) { - return []; + if (typeOf(obj) !== 'object') { + return false; } - if (len === 1) { - return micromatch.match(list, patterns[0], options); + if (has(obj, 'value') || has(obj, 'writable')) { + return false; } - var omit = []; - var keep = []; - var idx = -1; + if (!has(obj, 'get') || typeof obj.get !== 'function') { + return false; + } - while (++idx < len) { - var pattern = patterns[idx]; + // tldr: it's valid to have "set" be undefined + // "set" might be undefined if `Object.getOwnPropertyDescriptor` + // was used to get the value, and only `get` was defined by the user + if (has(obj, 'set') && typeof obj[key] !== 'function' && typeof obj[key] !== 'undefined') { + return false; + } - if (typeof pattern === 'string' && pattern.charCodeAt(0) === 33 /* ! */) { - omit.push.apply(omit, micromatch.match(list, pattern.slice(1), options)); - } else { - keep.push.apply(keep, micromatch.match(list, pattern, options)); + for (var key in obj) { + if (!accessor.hasOwnProperty(key)) { + continue; } - } - var matches = utils.diff(keep, omit); - if (!options || options.nodupes !== false) { - return utils.unique(matches); + if (typeOf(obj[key]) === accessor[key]) { + continue; + } + + if (typeof obj[key] !== 'undefined') { + return false; + } } + return true; +} - return matches; +function has(obj, key) { + return {}.hasOwnProperty.call(obj, key); } /** - * Similar to the main function, but `pattern` must be a string. - * - * ```js - * var mm = require('micromatch'); - * mm.match(list, pattern[, options]); - * - * console.log(mm.match(['a.a', 'a.aa', 'a.b', 'a.c'], '*.a')); - * //=> ['a.a', 'a.aa'] - * ``` - * @param {Array} `list` Array of strings to match - * @param {String} `pattern` Glob pattern to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Array} Returns an array of matches - * @api public + * Expose `isAccessorDescriptor` */ -micromatch.match = function(list, pattern, options) { - if (Array.isArray(pattern)) { - throw new TypeError('expected pattern to be a string'); - } - - var unixify = utils.unixify(options); - var isMatch = memoize('match', pattern, options, micromatch.matcher); - var matches = []; - - list = utils.arrayify(list); - var len = list.length; - var idx = -1; +module.exports = isAccessorDescriptor; - while (++idx < len) { - var ele = list[idx]; - if (ele === pattern || isMatch(ele)) { - matches.push(utils.value(ele, unixify, options)); - } - } - // if no options were passed, uniquify results and return - if (typeof options === 'undefined') { - return utils.unique(matches); - } +/***/ }), +/* 591 */ +/***/ (function(module, exports, __webpack_require__) { - if (matches.length === 0) { - if (options.failglob === true) { - throw new Error('no matches found for "' + pattern + '"'); - } - if (options.nonull === true || options.nullglob === true) { - return [options.unescape ? utils.unescape(pattern) : pattern]; - } - } +"use strict"; +/*! + * is-data-descriptor + * + * Copyright (c) 2015-2017, Jon Schlinkert. + * Released under the MIT License. + */ - // if `opts.ignore` was defined, diff ignored list - if (options.ignore) { - matches = micromatch.not(matches, options.ignore, options); - } - return options.nodupes !== false ? utils.unique(matches) : matches; -}; -/** - * Returns true if the specified `string` matches the given glob `pattern`. - * - * ```js - * var mm = require('micromatch'); - * mm.isMatch(string, pattern[, options]); - * - * console.log(mm.isMatch('a.a', '*.a')); - * //=> true - * console.log(mm.isMatch('a.b', '*.a')); - * //=> false - * ``` - * @param {String} `string` String to match - * @param {String} `pattern` Glob pattern to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Boolean} Returns true if the string matches the glob pattern. - * @api public - */ +var typeOf = __webpack_require__(589); -micromatch.isMatch = function(str, pattern, options) { - if (typeof str !== 'string') { - throw new TypeError('expected a string: "' + util.inspect(str) + '"'); - } +module.exports = function isDataDescriptor(obj, prop) { + // data descriptor properties + var data = { + configurable: 'boolean', + enumerable: 'boolean', + writable: 'boolean' + }; - if (isEmptyString(str) || isEmptyString(pattern)) { + if (typeOf(obj) !== 'object') { return false; } - var equals = utils.equalsPattern(options); - if (equals(str)) { - return true; + if (typeof prop === 'string') { + var val = Object.getOwnPropertyDescriptor(obj, prop); + return typeof val !== 'undefined'; } - var isMatch = memoize('isMatch', pattern, options, micromatch.matcher); - return isMatch(str); -}; + if (!('value' in obj) && !('writable' in obj)) { + return false; + } -/** - * Returns true if some of the strings in the given `list` match any of the - * given glob `patterns`. - * - * ```js - * var mm = require('micromatch'); - * mm.some(list, patterns[, options]); - * - * console.log(mm.some(['foo.js', 'bar.js'], ['*.js', '!foo.js'])); - * // true - * console.log(mm.some(['foo.js'], ['*.js', '!foo.js'])); - * // false - * ``` - * @param {String|Array} `list` The string or array of strings to test. Returns as soon as the first match is found. - * @param {String|Array} `patterns` One or more glob patterns to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Boolean} Returns true if any patterns match `str` - * @api public - */ + for (var key in obj) { + if (key === 'value') continue; -micromatch.some = function(list, patterns, options) { - if (typeof list === 'string') { - list = [list]; - } - for (var i = 0; i < list.length; i++) { - if (micromatch(list[i], patterns, options).length === 1) { - return true; + if (!data.hasOwnProperty(key)) { + continue; } - } - return false; -}; -/** - * Returns true if every string in the given `list` matches - * any of the given glob `patterns`. - * - * ```js - * var mm = require('micromatch'); - * mm.every(list, patterns[, options]); - * - * console.log(mm.every('foo.js', ['foo.js'])); - * // true - * console.log(mm.every(['foo.js', 'bar.js'], ['*.js'])); - * // true - * console.log(mm.every(['foo.js', 'bar.js'], ['*.js', '!foo.js'])); - * // false - * console.log(mm.every(['foo.js'], ['*.js', '!foo.js'])); - * // false - * ``` - * @param {String|Array} `list` The string or array of strings to test. - * @param {String|Array} `patterns` One or more glob patterns to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Boolean} Returns true if any patterns match `str` - * @api public - */ + if (typeOf(obj[key]) === data[key]) { + continue; + } -micromatch.every = function(list, patterns, options) { - if (typeof list === 'string') { - list = [list]; - } - for (var i = 0; i < list.length; i++) { - if (micromatch(list[i], patterns, options).length !== 1) { + if (typeof obj[key] !== 'undefined') { return false; } } return true; }; -/** - * Returns true if **any** of the given glob `patterns` - * match the specified `string`. - * - * ```js - * var mm = require('micromatch'); - * mm.any(string, patterns[, options]); - * - * console.log(mm.any('a.a', ['b.*', '*.a'])); - * //=> true - * console.log(mm.any('a.a', 'b.*')); - * //=> false - * ``` - * @param {String|Array} `str` The string to test. - * @param {String|Array} `patterns` One or more glob patterns to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Boolean} Returns true if any patterns match `str` - * @api public - */ - -micromatch.any = function(str, patterns, options) { - if (typeof str !== 'string') { - throw new TypeError('expected a string: "' + util.inspect(str) + '"'); - } - if (isEmptyString(str) || isEmptyString(patterns)) { - return false; - } +/***/ }), +/* 592 */ +/***/ (function(module, exports, __webpack_require__) { - if (typeof patterns === 'string') { - patterns = [patterns]; - } +"use strict"; - for (var i = 0; i < patterns.length; i++) { - if (micromatch.isMatch(str, patterns[i], options)) { - return true; - } - } - return false; -}; -/** - * Returns true if **all** of the given `patterns` match - * the specified string. - * - * ```js - * var mm = require('micromatch'); - * mm.all(string, patterns[, options]); - * - * console.log(mm.all('foo.js', ['foo.js'])); - * // true - * - * console.log(mm.all('foo.js', ['*.js', '!foo.js'])); - * // false - * - * console.log(mm.all('foo.js', ['*.js', 'foo.js'])); - * // true - * - * console.log(mm.all('foo.js', ['*.js', 'f*', '*o*', '*o.js'])); - * // true - * ``` - * @param {String|Array} `str` The string to test. - * @param {String|Array} `patterns` One or more glob patterns to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Boolean} Returns true if any patterns match `str` - * @api public - */ +var isExtendable = __webpack_require__(593); +var assignSymbols = __webpack_require__(595); -micromatch.all = function(str, patterns, options) { - if (typeof str !== 'string') { - throw new TypeError('expected a string: "' + util.inspect(str) + '"'); +module.exports = Object.assign || function(obj/*, objects*/) { + if (obj === null || typeof obj === 'undefined') { + throw new TypeError('Cannot convert undefined or null to object'); } - if (typeof patterns === 'string') { - patterns = [patterns]; + if (!isObject(obj)) { + obj = {}; } - for (var i = 0; i < patterns.length; i++) { - if (!micromatch.isMatch(str, patterns[i], options)) { - return false; + for (var i = 1; i < arguments.length; i++) { + var val = arguments[i]; + if (isString(val)) { + val = toObject(val); + } + if (isObject(val)) { + assign(obj, val); + assignSymbols(obj, val); } } - return true; + return obj; }; +function assign(a, b) { + for (var key in b) { + if (hasOwn(b, key)) { + a[key] = b[key]; + } + } +} + +function isString(val) { + return (val && typeof val === 'string'); +} + +function toObject(str) { + var obj = {}; + for (var i in str) { + obj[i] = str[i]; + } + return obj; +} + +function isObject(val) { + return (val && typeof val === 'object') || isExtendable(val); +} + /** - * Returns a list of strings that _**do not match any**_ of the given `patterns`. - * - * ```js - * var mm = require('micromatch'); - * mm.not(list, patterns[, options]); - * - * console.log(mm.not(['a.a', 'b.b', 'c.c'], '*.a')); - * //=> ['b.b', 'c.c'] - * ``` - * @param {Array} `list` Array of strings to match. - * @param {String|Array} `patterns` One or more glob pattern to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Array} Returns an array of strings that **do not match** the given patterns. - * @api public + * Returns true if the given `key` is an own property of `obj`. */ -micromatch.not = function(list, patterns, options) { - var opts = extend({}, options); - var ignore = opts.ignore; - delete opts.ignore; +function hasOwn(obj, key) { + return Object.prototype.hasOwnProperty.call(obj, key); +} - var unixify = utils.unixify(opts); - list = utils.arrayify(list).map(unixify); +function isEnum(obj, key) { + return Object.prototype.propertyIsEnumerable.call(obj, key); +} - var matches = utils.diff(list, micromatch(list, patterns, opts)); - if (ignore) { - matches = utils.diff(matches, micromatch(list, ignore)); - } - return opts.nodupes !== false ? utils.unique(matches) : matches; -}; +/***/ }), +/* 593 */ +/***/ (function(module, exports, __webpack_require__) { -/** - * Returns true if the given `string` contains the given pattern. Similar - * to [.isMatch](#isMatch) but the pattern can match any part of the string. - * - * ```js - * var mm = require('micromatch'); - * mm.contains(string, pattern[, options]); +"use strict"; +/*! + * is-extendable * - * console.log(mm.contains('aa/bb/cc', '*b')); - * //=> true - * console.log(mm.contains('aa/bb/cc', '*d')); - * //=> false - * ``` - * @param {String} `str` The string to match. - * @param {String|Array} `patterns` Glob pattern to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Boolean} Returns true if the patter matches any part of `str`. - * @api public + * Copyright (c) 2015-2017, Jon Schlinkert. + * Released under the MIT License. */ -micromatch.contains = function(str, patterns, options) { - if (typeof str !== 'string') { - throw new TypeError('expected a string: "' + util.inspect(str) + '"'); - } - if (typeof patterns === 'string') { - if (isEmptyString(str) || isEmptyString(patterns)) { - return false; - } - var equals = utils.equalsPattern(patterns, options); - if (equals(str)) { - return true; - } - var contains = utils.containsPattern(patterns, options); - if (contains(str)) { - return true; - } - } +var isPlainObject = __webpack_require__(594); - var opts = extend({}, options, {contains: true}); - return micromatch.any(str, patterns, opts); +module.exports = function isExtendable(val) { + return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); }; -/** - * Returns true if the given pattern and options should enable - * the `matchBase` option. - * @return {Boolean} - * @api private - */ -micromatch.matchBase = function(pattern, options) { - if (pattern && pattern.indexOf('/') !== -1 || !options) return false; - return options.basename === true || options.matchBase === true; -}; +/***/ }), +/* 594 */ +/***/ (function(module, exports, __webpack_require__) { -/** - * Filter the keys of the given object with the given `glob` pattern - * and `options`. Does not attempt to match nested keys. If you need this feature, - * use [glob-object][] instead. - * - * ```js - * var mm = require('micromatch'); - * mm.matchKeys(object, patterns[, options]); +"use strict"; +/*! + * is-plain-object * - * var obj = { aa: 'a', ab: 'b', ac: 'c' }; - * console.log(mm.matchKeys(obj, '*b')); - * //=> { ab: 'b' } - * ``` - * @param {Object} `object` The object with keys to filter. - * @param {String|Array} `patterns` One or more glob patterns to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Object} Returns an object with only keys that match the given patterns. - * @api public + * Copyright (c) 2014-2017, Jon Schlinkert. + * Released under the MIT License. */ -micromatch.matchKeys = function(obj, patterns, options) { - if (!utils.isObject(obj)) { - throw new TypeError('expected the first argument to be an object'); + + +var isObject = __webpack_require__(587); + +function isObjectObject(o) { + return isObject(o) === true + && Object.prototype.toString.call(o) === '[object Object]'; +} + +module.exports = function isPlainObject(o) { + var ctor,prot; + + if (isObjectObject(o) === false) return false; + + // If has modified constructor + ctor = o.constructor; + if (typeof ctor !== 'function') return false; + + // If has modified prototype + prot = ctor.prototype; + if (isObjectObject(prot) === false) return false; + + // If constructor does not have an Object-specific method + if (prot.hasOwnProperty('isPrototypeOf') === false) { + return false; } - var keys = micromatch(Object.keys(obj), patterns, options); - return utils.pick(obj, keys); + + // Most likely a plain Object + return true; }; -/** - * Returns a memoized matcher function from the given glob `pattern` and `options`. - * The returned function takes a string to match as its only argument and returns - * true if the string is a match. - * - * ```js - * var mm = require('micromatch'); - * mm.matcher(pattern[, options]); + +/***/ }), +/* 595 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +/*! + * assign-symbols * - * var isMatch = mm.matcher('*.!(*a)'); - * console.log(isMatch('a.a')); - * //=> false - * console.log(isMatch('a.b')); - * //=> true - * ``` - * @param {String} `pattern` Glob pattern - * @param {Object} `options` See available [options](#options) for changing how matches are performed. - * @return {Function} Returns a matcher function. - * @api public + * Copyright (c) 2015, Jon Schlinkert. + * Licensed under the MIT License. */ -micromatch.matcher = function matcher(pattern, options) { - if (Array.isArray(pattern)) { - return compose(pattern, options, matcher); - } - // if pattern is a regex - if (pattern instanceof RegExp) { - return test(pattern); - } - // if pattern is invalid - if (!utils.isString(pattern)) { - throw new TypeError('expected pattern to be an array, string or regex'); +module.exports = function(receiver, objects) { + if (receiver === null || typeof receiver === 'undefined') { + throw new TypeError('expected first argument to be an object.'); } - // if pattern is a non-glob string - if (!utils.hasSpecialChars(pattern)) { - if (options && options.nocase === true) { - pattern = pattern.toLowerCase(); - } - return utils.matchPath(pattern, options); + if (typeof objects === 'undefined' || typeof Symbol === 'undefined') { + return receiver; } - // if pattern is a glob string - var re = micromatch.makeRe(pattern, options); - - // if `options.matchBase` or `options.basename` is defined - if (micromatch.matchBase(pattern, options)) { - return utils.matchBasename(re, options); + if (typeof Object.getOwnPropertySymbols !== 'function') { + return receiver; } - function test(regex) { - var equals = utils.equalsPattern(options); - var unixify = utils.unixify(options); + var isEnumerable = Object.prototype.propertyIsEnumerable; + var target = Object(receiver); + var len = arguments.length, i = 0; - return function(str) { - if (equals(str)) { - return true; - } + while (++i < len) { + var provider = Object(arguments[i]); + var names = Object.getOwnPropertySymbols(provider); - if (regex.test(unixify(str))) { - return true; + for (var j = 0; j < names.length; j++) { + var key = names[j]; + + if (isEnumerable.call(provider, key)) { + target[key] = provider[key]; } - return false; - }; + } } - - var fn = test(re); - Object.defineProperty(fn, 'result', { - configurable: true, - enumerable: false, - value: re.result - }); - return fn; + return target; }; + +/***/ }), +/* 596 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var extend = __webpack_require__(597); +var safe = __webpack_require__(580); + /** - * Returns an array of matches captured by `pattern` in `string, or `null` if the pattern did not match. + * The main export is a function that takes a `pattern` string and an `options` object. * * ```js - * var mm = require('micromatch'); - * mm.capture(pattern, string[, options]); - * - * console.log(mm.capture('test/*.js', 'test/foo.js')); - * //=> ['foo'] - * console.log(mm.capture('test/*.js', 'foo/bar.css')); - * //=> null + & var not = require('regex-not'); + & console.log(not('foo')); + & //=> /^(?:(?!^(?:foo)$).)*$/ * ``` - * @param {String} `pattern` Glob pattern to use for matching. - * @param {String} `string` String to match - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Boolean} Returns an array of captures if the string matches the glob pattern, otherwise `null`. + * + * @param {String} `pattern` + * @param {Object} `options` + * @return {RegExp} Converts the given `pattern` to a regex using the specified `options`. * @api public */ -micromatch.capture = function(pattern, str, options) { - var re = micromatch.makeRe(pattern, extend({capture: true}, options)); - var unixify = utils.unixify(options); - - function match() { - return function(string) { - var match = re.exec(unixify(string)); - if (!match) { - return null; - } - - return match.slice(1); - }; - } - - var capture = memoize('capture', pattern, options, match); - return capture(str); -}; +function toRegex(pattern, options) { + return new RegExp(toRegex.create(pattern, options)); +} /** - * Create a regular expression from the given glob `pattern`. + * Create a regex-compatible string from the given `pattern` and `options`. * * ```js - * var mm = require('micromatch'); - * mm.makeRe(pattern[, options]); - * - * console.log(mm.makeRe('*.js')); - * //=> /^(?:(\.[\\\/])?(?!\.)(?=.)[^\/]*?\.js)$/ + & var not = require('regex-not'); + & console.log(not.create('foo')); + & //=> '^(?:(?!^(?:foo)$).)*$' * ``` - * @param {String} `pattern` A glob pattern to convert to regex. - * @param {Object} `options` See available [options](#options) for changing how matches are performed. - * @return {RegExp} Returns a regex created from the given pattern. + * @param {String} `pattern` + * @param {Object} `options` + * @return {String} * @api public */ -micromatch.makeRe = function(pattern, options) { +toRegex.create = function(pattern, options) { if (typeof pattern !== 'string') { - throw new TypeError('expected pattern to be a string'); + throw new TypeError('expected a string'); } - if (pattern.length > MAX_LENGTH) { - throw new Error('expected pattern to be less than ' + MAX_LENGTH + ' characters'); + var opts = extend({}, options); + if (opts.contains === true) { + opts.strictNegate = false; } - function makeRe() { - var result = micromatch.create(pattern, options); - var ast_array = []; - var output = result.map(function(obj) { - obj.ast.state = obj.state; - ast_array.push(obj.ast); - return obj.output; - }); + var open = opts.strictOpen !== false ? '^' : ''; + var close = opts.strictClose !== false ? '$' : ''; + var endChar = opts.endChar ? opts.endChar : '+'; + var str = pattern; - var regex = toRegex(output.join('|'), options); - Object.defineProperty(regex, 'result', { - configurable: true, - enumerable: false, - value: ast_array - }); - return regex; + if (opts.strictNegate === false) { + str = '(?:(?!(?:' + pattern + ')).)' + endChar; + } else { + str = '(?:(?!^(?:' + pattern + ')$).)' + endChar; } - return memoize('makeRe', pattern, options, makeRe); + var res = open + str + close; + if (opts.safe === true && safe(res) === false) { + throw new Error('potentially unsafe regular expression: ' + res); + } + + return res; }; /** - * Expand the given brace `pattern`. - * - * ```js - * var mm = require('micromatch'); - * console.log(mm.braces('foo/{a,b}/bar')); - * //=> ['foo/(a|b)/bar'] - * - * console.log(mm.braces('foo/{a,b}/bar', {expand: true})); - * //=> ['foo/(a|b)/bar'] - * ``` - * @param {String} `pattern` String with brace pattern to expand. - * @param {Object} `options` Any [options](#options) to change how expansion is performed. See the [braces][] library for all available options. - * @return {Array} - * @api public + * Expose `toRegex` */ -micromatch.braces = function(pattern, options) { - if (typeof pattern !== 'string' && !Array.isArray(pattern)) { - throw new TypeError('expected pattern to be an array or string'); - } +module.exports = toRegex; - function expand() { - if (options && options.nobrace === true || !/\{.*\}/.test(pattern)) { - return utils.arrayify(pattern); - } - return braces(pattern, options); - } - return memoize('braces', pattern, options, expand); -}; +/***/ }), +/* 597 */ +/***/ (function(module, exports, __webpack_require__) { -/** - * Proxy to the [micromatch.braces](#method), for parity with - * minimatch. - */ +"use strict"; -micromatch.braceExpand = function(pattern, options) { - var opts = extend({}, options, {expand: true}); - return micromatch.braces(pattern, opts); -}; -/** - * Parses the given glob `pattern` and returns an array of abstract syntax - * trees (ASTs), with the compiled `output` and optional source `map` on - * each AST. - * - * ```js - * var mm = require('micromatch'); - * mm.create(pattern[, options]); - * - * console.log(mm.create('abc/*.js')); - * // [{ options: { source: 'string', sourcemap: true }, - * // state: {}, - * // compilers: - * // { ... }, - * // output: '(\\.[\\\\\\/])?abc\\/(?!\\.)(?=.)[^\\/]*?\\.js', - * // ast: - * // { type: 'root', - * // errors: [], - * // nodes: - * // [ ... ], - * // dot: false, - * // input: 'abc/*.js' }, - * // parsingErrors: [], - * // map: - * // { version: 3, - * // sources: [ 'string' ], - * // names: [], - * // mappings: 'AAAA,GAAG,EAAC,kBAAC,EAAC,EAAE', - * // sourcesContent: [ 'abc/*.js' ] }, - * // position: { line: 1, column: 28 }, - * // content: {}, - * // files: {}, - * // idx: 6 }] - * ``` - * @param {String} `pattern` Glob pattern to parse and compile. - * @param {Object} `options` Any [options](#options) to change how parsing and compiling is performed. - * @return {Object} Returns an object with the parsed AST, compiled string and optional source map. - * @api public - */ +var isExtendable = __webpack_require__(598); +var assignSymbols = __webpack_require__(595); -micromatch.create = function(pattern, options) { - return memoize('create', pattern, options, function() { - function create(str, opts) { - return micromatch.compile(micromatch.parse(str, opts), opts); +module.exports = Object.assign || function(obj/*, objects*/) { + if (obj === null || typeof obj === 'undefined') { + throw new TypeError('Cannot convert undefined or null to object'); + } + if (!isObject(obj)) { + obj = {}; + } + for (var i = 1; i < arguments.length; i++) { + var val = arguments[i]; + if (isString(val)) { + val = toObject(val); } - - pattern = micromatch.braces(pattern, options); - var len = pattern.length; - var idx = -1; - var res = []; - - while (++idx < len) { - res.push(create(pattern[idx], options)); + if (isObject(val)) { + assign(obj, val); + assignSymbols(obj, val); } - return res; - }); + } + return obj; }; -/** - * Parse the given `str` with the given `options`. - * - * ```js - * var mm = require('micromatch'); - * mm.parse(pattern[, options]); - * - * var ast = mm.parse('a/{b,c}/d'); - * console.log(ast); - * // { type: 'root', - * // errors: [], - * // input: 'a/{b,c}/d', - * // nodes: - * // [ { type: 'bos', val: '' }, - * // { type: 'text', val: 'a/' }, - * // { type: 'brace', - * // nodes: - * // [ { type: 'brace.open', val: '{' }, - * // { type: 'text', val: 'b,c' }, - * // { type: 'brace.close', val: '}' } ] }, - * // { type: 'text', val: '/d' }, - * // { type: 'eos', val: '' } ] } - * ``` - * @param {String} `str` - * @param {Object} `options` - * @return {Object} Returns an AST - * @api public - */ - -micromatch.parse = function(pattern, options) { - if (typeof pattern !== 'string') { - throw new TypeError('expected a string'); +function assign(a, b) { + for (var key in b) { + if (hasOwn(b, key)) { + a[key] = b[key]; + } } +} - function parse() { - var snapdragon = utils.instantiate(null, options); - parsers(snapdragon, options); +function isString(val) { + return (val && typeof val === 'string'); +} - var ast = snapdragon.parse(pattern, options); - utils.define(ast, 'snapdragon', snapdragon); - ast.input = pattern; - return ast; +function toObject(str) { + var obj = {}; + for (var i in str) { + obj[i] = str[i]; } + return obj; +} - return memoize('parse', pattern, options, parse); -}; +function isObject(val) { + return (val && typeof val === 'object') || isExtendable(val); +} /** - * Compile the given `ast` or string with the given `options`. - * - * ```js - * var mm = require('micromatch'); - * mm.compile(ast[, options]); - * - * var ast = mm.parse('a/{b,c}/d'); - * console.log(mm.compile(ast)); - * // { options: { source: 'string' }, - * // state: {}, - * // compilers: - * // { eos: [Function], - * // noop: [Function], - * // bos: [Function], - * // brace: [Function], - * // 'brace.open': [Function], - * // text: [Function], - * // 'brace.close': [Function] }, - * // output: [ 'a/(b|c)/d' ], - * // ast: - * // { ... }, - * // parsingErrors: [] } - * ``` - * @param {Object|String} `ast` - * @param {Object} `options` - * @return {Object} Returns an object that has an `output` property with the compiled string. - * @api public + * Returns true if the given `key` is an own property of `obj`. */ -micromatch.compile = function(ast, options) { - if (typeof ast === 'string') { - ast = micromatch.parse(ast, options); - } +function hasOwn(obj, key) { + return Object.prototype.hasOwnProperty.call(obj, key); +} - return memoize('compile', ast.input, options, function() { - var snapdragon = utils.instantiate(ast, options); - compilers(snapdragon, options); - return snapdragon.compile(ast, options); - }); -}; +function isEnum(obj, key) { + return Object.prototype.propertyIsEnumerable.call(obj, key); +} -/** - * Clear the regex cache. + +/***/ }), +/* 598 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +/*! + * is-extendable * - * ```js - * mm.clearCache(); - * ``` - * @api public + * Copyright (c) 2015-2017, Jon Schlinkert. + * Released under the MIT License. */ -micromatch.clearCache = function() { - micromatch.cache.caches = {}; + + +var isPlainObject = __webpack_require__(594); + +module.exports = function isExtendable(val) { + return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); }; -/** - * Returns true if the given value is effectively an empty string - */ -function isEmptyString(val) { - return String(val) === '' || String(val) === './'; -} +/***/ }), +/* 599 */ +/***/ (function(module, exports, __webpack_require__) { -/** - * Compose a matcher function with the given patterns. - * This allows matcher functions to be compiled once and - * called multiple times. +"use strict"; +/*! + * array-unique + * + * Copyright (c) 2014-2015, Jon Schlinkert. + * Licensed under the MIT License. */ -function compose(patterns, options, matcher) { - var matchers; - return memoize('compose', String(patterns), options, function() { - return function(file) { - // delay composition until it's invoked the first time, - // after that it won't be called again - if (!matchers) { - matchers = []; - for (var i = 0; i < patterns.length; i++) { - matchers.push(matcher(patterns[i], options)); - } - } - var len = matchers.length; - while (len--) { - if (matchers[len](file) === true) { - return true; - } - } - return false; - }; - }); -} +module.exports = function unique(arr) { + if (!Array.isArray(arr)) { + throw new TypeError('array-unique expects an array.'); + } -/** - * Memoize a generated regex or function. A unique key is generated - * from the `type` (usually method name), the `pattern`, and - * user-defined options. - */ + var len = arr.length; + var i = -1; -function memoize(type, pattern, options, fn) { - var key = utils.createKey(type + '=' + pattern, options); + while (i++ < len) { + var j = i + 1; - if (options && options.cache === false) { - return fn(pattern, options); + for (; j < arr.length; ++j) { + if (arr[i] === arr[j]) { + arr.splice(j--, 1); + } + } } + return arr; +}; - if (cache.has(type, key)) { - return cache.get(type, key); +module.exports.immutable = function uniqueImmutable(arr) { + if (!Array.isArray(arr)) { + throw new TypeError('array-unique expects an array.'); } - var val = fn(pattern, options); - cache.set(type, key, val); - return val; -} - -/** - * Expose compiler, parser and cache on `micromatch` - */ - -micromatch.compilers = compilers; -micromatch.parsers = parsers; -micromatch.caches = cache.caches; + var arrLen = arr.length; + var newArr = new Array(arrLen); -/** - * Expose `micromatch` - * @type {Function} - */ + for (var i = 0; i < arrLen; i++) { + newArr[i] = arr[i]; + } -module.exports = micromatch; + return module.exports(newArr); +}; /***/ }), -/* 572 */ +/* 600 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -/** - * Module dependencies - */ - -var toRegex = __webpack_require__(573); -var unique = __webpack_require__(593); -var extend = __webpack_require__(594); - -/** - * Local dependencies - */ - -var compilers = __webpack_require__(596); -var parsers = __webpack_require__(611); -var Braces = __webpack_require__(616); -var utils = __webpack_require__(597); -var MAX_LENGTH = 1024 * 64; -var cache = {}; - -/** - * Convert the given `braces` pattern into a regex-compatible string. By default, only one string is generated for every input string. Set `options.expand` to true to return an array of patterns (similar to Bash or minimatch. Before using `options.expand`, it's recommended that you read the [performance notes](#performance)). - * - * ```js - * var braces = require('braces'); - * console.log(braces('{a,b,c}')); - * //=> ['(a|b|c)'] - * - * console.log(braces('{a,b,c}', {expand: true})); - * //=> ['a', 'b', 'c'] - * ``` - * @param {String} `str` - * @param {Object} `options` - * @return {String} - * @api public - */ +var isObject = __webpack_require__(601); -function braces(pattern, options) { - var key = utils.createKey(String(pattern), options); - var arr = []; +module.exports = function extend(o/*, objects*/) { + if (!isObject(o)) { o = {}; } - var disabled = options && options.cache === false; - if (!disabled && cache.hasOwnProperty(key)) { - return cache[key]; - } + var len = arguments.length; + for (var i = 1; i < len; i++) { + var obj = arguments[i]; - if (Array.isArray(pattern)) { - for (var i = 0; i < pattern.length; i++) { - arr.push.apply(arr, braces.create(pattern[i], options)); + if (isObject(obj)) { + assign(o, obj); } - } else { - arr = braces.create(pattern, options); - } - - if (options && options.nodupes === true) { - arr = unique(arr); } + return o; +}; - if (!disabled) { - cache[key] = arr; +function assign(a, b) { + for (var key in b) { + if (hasOwn(b, key)) { + a[key] = b[key]; + } } - return arr; } /** - * Expands a brace pattern into an array. This method is called by the main [braces](#braces) function when `options.expand` is true. Before using this method it's recommended that you read the [performance notes](#performance)) and advantages of using [.optimize](#optimize) instead. - * - * ```js - * var braces = require('braces'); - * console.log(braces.expand('a/{b,c}/d')); - * //=> ['a/b/d', 'a/c/d']; - * ``` - * @param {String} `pattern` Brace pattern - * @param {Object} `options` - * @return {Array} Returns an array of expanded values. - * @api public + * Returns true if the given `key` is an own property of `obj`. */ -braces.expand = function(pattern, options) { - return braces.create(pattern, extend({}, options, {expand: true})); -}; +function hasOwn(obj, key) { + return Object.prototype.hasOwnProperty.call(obj, key); +} -/** - * Expands a brace pattern into a regex-compatible, optimized string. This method is called by the main [braces](#braces) function by default. - * - * ```js - * var braces = require('braces'); - * console.log(braces.expand('a/{b,c}/d')); - * //=> ['a/(b|c)/d'] - * ``` - * @param {String} `pattern` Brace pattern - * @param {Object} `options` - * @return {Array} Returns an array of expanded values. - * @api public - */ -braces.optimize = function(pattern, options) { - return braces.create(pattern, options); -}; +/***/ }), +/* 601 */ +/***/ (function(module, exports, __webpack_require__) { -/** - * Processes a brace pattern and returns either an expanded array (if `options.expand` is true), a highly optimized regex-compatible string. This method is called by the main [braces](#braces) function. +"use strict"; +/*! + * is-extendable * - * ```js - * var braces = require('braces'); - * console.log(braces.create('user-{200..300}/project-{a,b,c}-{1..10}')) - * //=> 'user-(20[0-9]|2[1-9][0-9]|300)/project-(a|b|c)-([1-9]|10)' - * ``` - * @param {String} `pattern` Brace pattern - * @param {Object} `options` - * @return {Array} Returns an array of expanded values. - * @api public + * Copyright (c) 2015, Jon Schlinkert. + * Licensed under the MIT License. */ -braces.create = function(pattern, options) { - if (typeof pattern !== 'string') { - throw new TypeError('expected a string'); - } - - var maxLength = (options && options.maxLength) || MAX_LENGTH; - if (pattern.length >= maxLength) { - throw new Error('expected pattern to be less than ' + maxLength + ' characters'); - } - - function create() { - if (pattern === '' || pattern.length < 3) { - return [pattern]; - } - if (utils.isEmptySets(pattern)) { - return []; - } - if (utils.isQuotedString(pattern)) { - return [pattern.slice(1, -1)]; - } +module.exports = function isExtendable(val) { + return typeof val !== 'undefined' && val !== null + && (typeof val === 'object' || typeof val === 'function'); +}; - var proto = new Braces(options); - var result = !options || options.expand !== true - ? proto.optimize(pattern, options) - : proto.expand(pattern, options); - // get the generated pattern(s) - var arr = result.output; +/***/ }), +/* 602 */ +/***/ (function(module, exports, __webpack_require__) { - // filter out empty strings if specified - if (options && options.noempty === true) { - arr = arr.filter(Boolean); - } +"use strict"; - // filter out duplicates if specified - if (options && options.nodupes === true) { - arr = unique(arr); - } - Object.defineProperty(arr, 'result', { - enumerable: false, - value: result - }); +var utils = __webpack_require__(603); - return arr; - } +module.exports = function(braces, options) { + braces.compiler - return memoize('create', pattern, options, create); -}; + /** + * bos + */ -/** - * Create a regular expression from the given string `pattern`. - * - * ```js - * var braces = require('braces'); - * - * console.log(braces.makeRe('id-{200..300}')); - * //=> /^(?:id-(20[0-9]|2[1-9][0-9]|300))$/ - * ``` - * @param {String} `pattern` The pattern to convert to regex. - * @param {Object} `options` - * @return {RegExp} - * @api public - */ + .set('bos', function() { + if (this.output) return; + this.ast.queue = isEscaped(this.ast) ? [this.ast.val] : []; + this.ast.count = 1; + }) -braces.makeRe = function(pattern, options) { - if (typeof pattern !== 'string') { - throw new TypeError('expected a string'); - } + /** + * Square brackets + */ - var maxLength = (options && options.maxLength) || MAX_LENGTH; - if (pattern.length >= maxLength) { - throw new Error('expected pattern to be less than ' + maxLength + ' characters'); - } + .set('bracket', function(node) { + var close = node.close; + var open = !node.escaped ? '[' : '\\['; + var negated = node.negated; + var inner = node.inner; - function makeRe() { - var arr = braces(pattern, options); - var opts = extend({strictErrors: false}, options); - return toRegex(arr, opts); - } + inner = inner.replace(/\\(?=[\\\w]|$)/g, '\\\\'); + if (inner === ']-') { + inner = '\\]\\-'; + } - return memoize('makeRe', pattern, options, makeRe); -}; + if (negated && inner.indexOf('.') === -1) { + inner += '.'; + } + if (negated && inner.indexOf('/') === -1) { + inner += '/'; + } -/** - * Parse the given `str` with the given `options`. - * - * ```js - * var braces = require('braces'); - * var ast = braces.parse('a/{b,c}/d'); - * console.log(ast); - * // { type: 'root', - * // errors: [], - * // input: 'a/{b,c}/d', - * // nodes: - * // [ { type: 'bos', val: '' }, - * // { type: 'text', val: 'a/' }, - * // { type: 'brace', - * // nodes: - * // [ { type: 'brace.open', val: '{' }, - * // { type: 'text', val: 'b,c' }, - * // { type: 'brace.close', val: '}' } ] }, - * // { type: 'text', val: '/d' }, - * // { type: 'eos', val: '' } ] } - * ``` - * @param {String} `pattern` Brace pattern to parse - * @param {Object} `options` - * @return {Object} Returns an AST - * @api public - */ + var val = open + negated + inner + close; + var queue = node.parent.queue; + var last = utils.arrayify(queue.pop()); -braces.parse = function(pattern, options) { - var proto = new Braces(options); - return proto.parse(pattern, options); -}; + queue.push(utils.join(last, val)); + queue.push.apply(queue, []); + }) -/** - * Compile the given `ast` or string with the given `options`. - * - * ```js - * var braces = require('braces'); - * var ast = braces.parse('a/{b,c}/d'); - * console.log(braces.compile(ast)); - * // { options: { source: 'string' }, - * // state: {}, - * // compilers: - * // { eos: [Function], - * // noop: [Function], - * // bos: [Function], - * // brace: [Function], - * // 'brace.open': [Function], - * // text: [Function], - * // 'brace.close': [Function] }, - * // output: [ 'a/(b|c)/d' ], - * // ast: - * // { ... }, - * // parsingErrors: [] } - * ``` - * @param {Object|String} `ast` AST from [.parse](#parse). If a string is passed it will be parsed first. - * @param {Object} `options` - * @return {Object} Returns an object that has an `output` property with the compiled string. - * @api public - */ + /** + * Brace + */ -braces.compile = function(ast, options) { - var proto = new Braces(options); - return proto.compile(ast, options); -}; + .set('brace', function(node) { + node.queue = isEscaped(node) ? [node.val] : []; + node.count = 1; + return this.mapVisit(node.nodes); + }) -/** - * Clear the regex cache. - * - * ```js - * braces.clearCache(); - * ``` - * @api public - */ + /** + * Open + */ -braces.clearCache = function() { - cache = braces.cache = {}; -}; + .set('brace.open', function(node) { + node.parent.open = node.val; + }) -/** - * Memoize a generated regex or function. A unique key is generated - * from the method name, pattern, and user-defined options. Set - * options.memoize to false to disable. - */ + /** + * Inner + */ -function memoize(type, pattern, options, fn) { - var key = utils.createKey(type + ':' + pattern, options); - var disabled = options && options.cache === false; - if (disabled) { - braces.clearCache(); - return fn(pattern, options); - } + .set('text', function(node) { + var queue = node.parent.queue; + var escaped = node.escaped; + var segs = [node.val]; - if (cache.hasOwnProperty(key)) { - return cache[key]; - } + if (node.optimize === false) { + options = utils.extend({}, options, {optimize: false}); + } - var res = fn(pattern, options); - cache[key] = res; - return res; -} + if (node.multiplier > 1) { + node.parent.count *= node.multiplier; + } -/** - * Expose `Braces` constructor and methods - * @type {Function} - */ + if (options.quantifiers === true && utils.isQuantifier(node.val)) { + escaped = true; -braces.Braces = Braces; -braces.compilers = compilers; -braces.parsers = parsers; -braces.cache = cache; + } else if (node.val.length > 1) { + if (isType(node.parent, 'brace') && !isEscaped(node)) { + var expanded = utils.expand(node.val, options); + segs = expanded.segs; -/** - * Expose `braces` - * @type {Function} - */ + if (expanded.isOptimized) { + node.parent.isOptimized = true; + } -module.exports = braces; + // if nothing was expanded, we probably have a literal brace + if (!segs.length) { + var val = (expanded.val || node.val); + if (options.unescape !== false) { + // unescape unexpanded brace sequence/set separators + val = val.replace(/\\([,.])/g, '$1'); + // strip quotes + val = val.replace(/["'`]/g, ''); + } + segs = [val]; + escaped = true; + } + } -/***/ }), -/* 573 */ -/***/ (function(module, exports, __webpack_require__) { + } else if (node.val === ',') { + if (options.expand) { + node.parent.queue.push(['']); + segs = ['']; + } else { + segs = ['|']; + } + } else { + escaped = true; + } -"use strict"; + if (escaped && isType(node.parent, 'brace')) { + if (node.parent.nodes.length <= 4 && node.parent.count === 1) { + node.parent.escaped = true; + } else if (node.parent.length <= 3) { + node.parent.escaped = true; + } + } + if (!hasQueue(node.parent)) { + node.parent.queue = segs; + return; + } -var safe = __webpack_require__(574); -var define = __webpack_require__(580); -var extend = __webpack_require__(586); -var not = __webpack_require__(590); -var MAX_LENGTH = 1024 * 64; + var last = utils.arrayify(queue.pop()); + if (node.parent.count > 1 && options.expand) { + last = multiply(last, node.parent.count); + node.parent.count = 1; + } -/** - * Session cache - */ + queue.push(utils.join(utils.flatten(last), segs.shift())); + queue.push.apply(queue, segs); + }) -var cache = {}; + /** + * Close + */ -/** - * Create a regular expression from the given `pattern` string. - * - * @param {String|RegExp} `pattern` Pattern can be a string or regular expression. - * @param {Object} `options` - * @return {RegExp} - * @api public - */ + .set('brace.close', function(node) { + var queue = node.parent.queue; + var prev = node.parent.parent; + var last = prev.queue.pop(); + var open = node.parent.open; + var close = node.val; -module.exports = function(patterns, options) { - if (!Array.isArray(patterns)) { - return makeRe(patterns, options); - } - return makeRe(patterns.join('|'), options); -}; + if (open && close && isOptimized(node, options)) { + open = '('; + close = ')'; + } -/** - * Create a regular expression from the given `pattern` string. - * - * @param {String|RegExp} `pattern` Pattern can be a string or regular expression. - * @param {Object} `options` - * @return {RegExp} - * @api public - */ + // if a close brace exists, and the previous segment is one character + // don't wrap the result in braces or parens + var ele = utils.last(queue); + if (node.parent.count > 1 && options.expand) { + ele = multiply(queue.pop(), node.parent.count); + node.parent.count = 1; + queue.push(ele); + } -function makeRe(pattern, options) { - if (pattern instanceof RegExp) { - return pattern; - } + if (close && typeof ele === 'string' && ele.length === 1) { + open = ''; + close = ''; + } - if (typeof pattern !== 'string') { - throw new TypeError('expected a string'); - } + if ((isLiteralBrace(node, options) || noInner(node)) && !node.parent.hasEmpty) { + queue.push(utils.join(open, queue.pop() || '')); + queue = utils.flatten(utils.join(queue, close)); + } - if (pattern.length > MAX_LENGTH) { - throw new Error('expected pattern to be less than ' + MAX_LENGTH + ' characters'); - } + if (typeof last === 'undefined') { + prev.queue = [queue]; + } else { + prev.queue.push(utils.flatten(utils.join(last, queue))); + } + }) - var key = pattern; - // do this before shallow cloning options, it's a lot faster - if (!options || (options && options.cache !== false)) { - key = createKey(pattern, options); + /** + * eos + */ - if (cache.hasOwnProperty(key)) { - return cache[key]; - } - } + .set('eos', function(node) { + if (this.input) return; - var opts = extend({}, options); - if (opts.contains === true) { - if (opts.negate === true) { - opts.strictNegate = false; - } else { - opts.strict = false; - } - } + if (options.optimize !== false) { + this.output = utils.last(utils.flatten(this.ast.queue)); + } else if (Array.isArray(utils.last(this.ast.queue))) { + this.output = utils.flatten(this.ast.queue.pop()); + } else { + this.output = utils.flatten(this.ast.queue); + } - if (opts.strict === false) { - opts.strictOpen = false; - opts.strictClose = false; - } + if (node.parent.count > 1 && options.expand) { + this.output = multiply(this.output, node.parent.count); + } - var open = opts.strictOpen !== false ? '^' : ''; - var close = opts.strictClose !== false ? '$' : ''; - var flags = opts.flags || ''; - var regex; + this.output = utils.arrayify(this.output); + this.ast.queue = []; + }); - if (opts.nocase === true && !/i/.test(flags)) { - flags += 'i'; - } +}; - try { - if (opts.negate || typeof opts.strictNegate === 'boolean') { - pattern = not.create(pattern, opts); - } +/** + * Multiply the segments in the current brace level + */ - var str = open + '(?:' + pattern + ')' + close; - regex = new RegExp(str, flags); +function multiply(queue, n, options) { + return utils.flatten(utils.repeat(utils.arrayify(queue), n)); +} - if (opts.safe === true && safe(regex) === false) { - throw new Error('potentially unsafe regular expression: ' + regex.source); - } +/** + * Return true if `node` is escaped + */ - } catch (err) { - if (opts.strictErrors === true || opts.safe === true) { - err.key = key; - err.pattern = pattern; - err.originalOptions = options; - err.createdOptions = opts; - throw err; - } +function isEscaped(node) { + return node.escaped === true; +} - try { - regex = new RegExp('^' + pattern.replace(/(\W)/g, '\\$1') + '$'); - } catch (err) { - regex = /.^/; //<= match nothing - } - } +/** + * Returns true if regex parens should be used for sets. If the parent `type` + * is not `brace`, then we're on a root node, which means we should never + * expand segments and open/close braces should be `{}` (since this indicates + * a brace is missing from the set) + */ - if (opts.cache !== false) { - memoize(regex, key, pattern, opts); - } - return regex; +function isOptimized(node, options) { + if (node.parent.isOptimized) return true; + return isType(node.parent, 'brace') + && !isEscaped(node.parent) + && options.expand !== true; } /** - * Memoize generated regex. This can result in dramatic speed improvements - * and simplify debugging by adding options and pattern to the regex. It can be - * disabled by passing setting `options.cache` to false. + * Returns true if the value in `node` should be wrapped in a literal brace. + * @return {Boolean} */ -function memoize(regex, key, pattern, options) { - define(regex, 'cached', true); - define(regex, 'pattern', pattern); - define(regex, 'options', options); - define(regex, 'key', key); - cache[key] = regex; +function isLiteralBrace(node, options) { + return isEscaped(node.parent) || options.optimize !== false; } /** - * Create the key to use for memoization. The key is generated - * by iterating over the options and concatenating key-value pairs - * to the pattern string. + * Returns true if the given `node` does not have an inner value. + * @return {Boolean} */ -function createKey(pattern, options) { - if (!options) return pattern; - var key = pattern; - for (var prop in options) { - if (options.hasOwnProperty(prop)) { - key += ';' + prop + '=' + String(options[prop]); - } +function noInner(node, type) { + if (node.parent.queue.length === 1) { + return true; } - return key; + var nodes = node.parent.nodes; + return nodes.length === 3 + && isType(nodes[0], 'brace.open') + && !isType(nodes[1], 'text') + && isType(nodes[2], 'brace.close'); } /** - * Expose `makeRe` + * Returns true if the given `node` is the given `type` + * @return {Boolean} */ -module.exports.makeRe = makeRe; - - -/***/ }), -/* 574 */ -/***/ (function(module, exports, __webpack_require__) { - -var parse = __webpack_require__(575); -var types = parse.types; +function isType(node, type) { + return typeof node !== 'undefined' && node.type === type; +} -module.exports = function (re, opts) { - if (!opts) opts = {}; - var replimit = opts.limit === undefined ? 25 : opts.limit; - - if (isRegExp(re)) re = re.source; - else if (typeof re !== 'string') re = String(re); - - try { re = parse(re) } - catch (err) { return false } - - var reps = 0; - return (function walk (node, starHeight) { - if (node.type === types.REPETITION) { - starHeight ++; - reps ++; - if (starHeight > 1) return false; - if (reps > replimit) return false; - } - - if (node.options) { - for (var i = 0, len = node.options.length; i < len; i++) { - var ok = walk({ stack: node.options[i] }, starHeight); - if (!ok) return false; - } - } - var stack = node.stack || (node.value && node.value.stack); - if (!stack) return true; - - for (var i = 0; i < stack.length; i++) { - var ok = walk(stack[i], starHeight); - if (!ok) return false; - } - - return true; - })(re, 0); -}; +/** + * Returns true if the given `node` has a non-empty queue. + * @return {Boolean} + */ -function isRegExp (x) { - return {}.toString.call(x) === '[object RegExp]'; +function hasQueue(node) { + return Array.isArray(node.queue) && node.queue.length; } /***/ }), -/* 575 */ +/* 603 */ /***/ (function(module, exports, __webpack_require__) { -var util = __webpack_require__(576); -var types = __webpack_require__(577); -var sets = __webpack_require__(578); -var positions = __webpack_require__(579); - - -module.exports = function(regexpStr) { - var i = 0, l, c, - start = { type: types.ROOT, stack: []}, - - // Keep track of last clause/group and stack. - lastGroup = start, - last = start.stack, - groupStack = []; - +"use strict"; - var repeatErr = function(i) { - util.error(regexpStr, 'Nothing to repeat at column ' + (i - 1)); - }; - // Decode a few escaped characters. - var str = util.strToChars(regexpStr); - l = str.length; +var splitString = __webpack_require__(604); +var utils = module.exports; - // Iterate through each character in string. - while (i < l) { - c = str[i++]; +/** + * Module dependencies + */ - switch (c) { - // Handle escaped characters, inclues a few sets. - case '\\': - c = str[i++]; +utils.extend = __webpack_require__(600); +utils.flatten = __webpack_require__(607); +utils.isObject = __webpack_require__(587); +utils.fillRange = __webpack_require__(608); +utils.repeat = __webpack_require__(616); +utils.unique = __webpack_require__(599); - switch (c) { - case 'b': - last.push(positions.wordBoundary()); - break; +utils.define = function(obj, key, val) { + Object.defineProperty(obj, key, { + writable: true, + configurable: true, + enumerable: false, + value: val + }); +}; - case 'B': - last.push(positions.nonWordBoundary()); - break; +/** + * Returns true if the given string contains only empty brace sets. + */ - case 'w': - last.push(sets.words()); - break; +utils.isEmptySets = function(str) { + return /^(?:\{,\})+$/.test(str); +}; - case 'W': - last.push(sets.notWords()); - break; +/** + * Returns true if the given string contains only empty brace sets. + */ - case 'd': - last.push(sets.ints()); - break; +utils.isQuotedString = function(str) { + var open = str.charAt(0); + if (open === '\'' || open === '"' || open === '`') { + return str.slice(-1) === open; + } + return false; +}; - case 'D': - last.push(sets.notInts()); - break; +/** + * Create the key to use for memoization. The unique key is generated + * by iterating over the options and concatenating key-value pairs + * to the pattern string. + */ - case 's': - last.push(sets.whitespace()); - break; +utils.createKey = function(pattern, options) { + var id = pattern; + if (typeof options === 'undefined') { + return id; + } + var keys = Object.keys(options); + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + id += ';' + key + '=' + String(options[key]); + } + return id; +}; - case 'S': - last.push(sets.notWhitespace()); - break; +/** + * Normalize options + */ - default: - // Check if c is integer. - // In which case it's a reference. - if (/\d/.test(c)) { - last.push({ type: types.REFERENCE, value: parseInt(c, 10) }); +utils.createOptions = function(options) { + var opts = utils.extend.apply(null, arguments); + if (typeof opts.expand === 'boolean') { + opts.optimize = !opts.expand; + } + if (typeof opts.optimize === 'boolean') { + opts.expand = !opts.optimize; + } + if (opts.optimize === true) { + opts.makeRe = true; + } + return opts; +}; - // Escaped character. - } else { - last.push({ type: types.CHAR, value: c.charCodeAt(0) }); - } - } +/** + * Join patterns in `a` to patterns in `b` + */ - break; +utils.join = function(a, b, options) { + options = options || {}; + a = utils.arrayify(a); + b = utils.arrayify(b); + if (!a.length) return b; + if (!b.length) return a; - // Positionals. - case '^': - last.push(positions.begin()); - break; + var len = a.length; + var idx = -1; + var arr = []; - case '$': - last.push(positions.end()); - break; + while (++idx < len) { + var val = a[idx]; + if (Array.isArray(val)) { + for (var i = 0; i < val.length; i++) { + val[i] = utils.join(val[i], b, options); + } + arr.push(val); + continue; + } + for (var j = 0; j < b.length; j++) { + var bval = b[j]; - // Handle custom sets. - case '[': - // Check if this class is 'anti' i.e. [^abc]. - var not; - if (str[i] === '^') { - not = true; - i++; - } else { - not = false; - } + if (Array.isArray(bval)) { + arr.push(utils.join(val, bval, options)); + } else { + arr.push(val + bval); + } + } + } + return arr; +}; - // Get all the characters in class. - var classTokens = util.tokenizeClass(str.slice(i), regexpStr); +/** + * Split the given string on `,` if not escaped. + */ - // Increase index by length of class. - i += classTokens[1]; - last.push({ - type: types.SET, - set: classTokens[0], - not: not, - }); +utils.split = function(str, options) { + var opts = utils.extend({sep: ','}, options); + if (typeof opts.keepQuotes !== 'boolean') { + opts.keepQuotes = true; + } + if (opts.unescape === false) { + opts.keepEscaping = true; + } + return splitString(str, opts, utils.escapeBrackets(opts)); +}; - break; +/** + * Expand ranges or sets in the given `pattern`. + * + * @param {String} `str` + * @param {Object} `options` + * @return {Object} + */ +utils.expand = function(str, options) { + var opts = utils.extend({rangeLimit: 10000}, options); + var segs = utils.split(str, opts); + var tok = { segs: segs }; - // Class of any character except \n. - case '.': - last.push(sets.anyChar()); - break; + if (utils.isQuotedString(str)) { + return tok; + } + if (opts.rangeLimit === true) { + opts.rangeLimit = 10000; + } - // Push group onto stack. - case '(': - // Create group. - var group = { - type: types.GROUP, - stack: [], - remember: true, - }; + if (segs.length > 1) { + if (opts.optimize === false) { + tok.val = segs[0]; + return tok; + } - c = str[i]; + tok.segs = utils.stringifyArray(tok.segs); + } else if (segs.length === 1) { + var arr = str.split('..'); - // If if this is a special kind of group. - if (c === '?') { - c = str[i + 1]; - i += 2; + if (arr.length === 1) { + tok.val = tok.segs[tok.segs.length - 1] || tok.val || str; + tok.segs = []; + return tok; + } - // Match if followed by. - if (c === '=') { - group.followedBy = true; + if (arr.length === 2 && arr[0] === arr[1]) { + tok.escaped = true; + tok.val = arr[0]; + tok.segs = []; + return tok; + } - // Match if not followed by. - } else if (c === '!') { - group.notFollowedBy = true; + if (arr.length > 1) { + if (opts.optimize !== false) { + opts.optimize = true; + delete opts.expand; + } - } else if (c !== ':') { - util.error(regexpStr, - 'Invalid group, character \'' + c + - '\' after \'?\' at column ' + (i - 1)); - } + if (opts.optimize !== true) { + var min = Math.min(arr[0], arr[1]); + var max = Math.max(arr[0], arr[1]); + var step = arr[2] || 1; - group.remember = false; + if (opts.rangeLimit !== false && ((max - min) / step >= opts.rangeLimit)) { + throw new RangeError('expanded array length exceeds range limit. Use options.rangeLimit to increase or disable the limit.'); } + } - // Insert subgroup into current group stack. - last.push(group); - - // Remember the current group for when the group closes. - groupStack.push(lastGroup); - - // Make this new group the current group. - lastGroup = group; - last = group.stack; - break; + arr.push(opts); + tok.segs = utils.fillRange.apply(null, arr); + if (!tok.segs.length) { + tok.escaped = true; + tok.val = str; + return tok; + } - // Pop group out of stack. - case ')': - if (groupStack.length === 0) { - util.error(regexpStr, 'Unmatched ) at column ' + (i - 1)); - } - lastGroup = groupStack.pop(); + if (opts.optimize === true) { + tok.segs = utils.stringifyArray(tok.segs); + } - // Check if this group has a PIPE. - // To get back the correct last stack. - last = lastGroup.options ? - lastGroup.options[lastGroup.options.length - 1] : lastGroup.stack; - break; + if (tok.segs === '') { + tok.val = str; + } else { + tok.val = tok.segs[0]; + } + return tok; + } + } else { + tok.val = str; + } + return tok; +}; +/** + * Ensure commas inside brackets and parens are not split. + * @param {Object} `tok` Token from the `split-string` module + * @return {undefined} + */ - // Use pipe character to give more choices. - case '|': - // Create array where options are if this is the first PIPE - // in this clause. - if (!lastGroup.options) { - lastGroup.options = [lastGroup.stack]; - delete lastGroup.stack; - } +utils.escapeBrackets = function(options) { + return function(tok) { + if (tok.escaped && tok.val === 'b') { + tok.val = '\\b'; + return; + } - // Create a new stack and add to options for rest of clause. - var stack = []; - lastGroup.options.push(stack); - last = stack; - break; + if (tok.val !== '(' && tok.val !== '[') return; + var opts = utils.extend({}, options); + var brackets = []; + var parens = []; + var stack = []; + var val = tok.val; + var str = tok.str; + var i = tok.idx - 1; + while (++i < str.length) { + var ch = str[i]; - // Repetition. - // For every repetition, remove last element from last stack - // then insert back a RANGE object. - // This design is chosen because there could be more than - // one repetition symbols in a regex i.e. `a?+{2,3}`. - case '{': - var rs = /^(\d+)(,(\d+)?)?\}/.exec(str.slice(i)), min, max; - if (rs !== null) { - if (last.length === 0) { - repeatErr(i); - } - min = parseInt(rs[1], 10); - max = rs[2] ? rs[3] ? parseInt(rs[3], 10) : Infinity : min; - i += rs[0].length; + if (ch === '\\') { + val += (opts.keepEscaping === false ? '' : ch) + str[++i]; + continue; + } - last.push({ - type: types.REPETITION, - min: min, - max: max, - value: last.pop(), - }); - } else { - last.push({ - type: types.CHAR, - value: 123, - }); - } - break; + if (ch === '(') { + parens.push(ch); + stack.push(ch); + } - case '?': - if (last.length === 0) { - repeatErr(i); - } - last.push({ - type: types.REPETITION, - min: 0, - max: 1, - value: last.pop(), - }); - break; + if (ch === '[') { + brackets.push(ch); + stack.push(ch); + } - case '+': - if (last.length === 0) { - repeatErr(i); + if (ch === ')') { + parens.pop(); + stack.pop(); + if (!stack.length) { + val += ch; + break; } - last.push({ - type: types.REPETITION, - min: 1, - max: Infinity, - value: last.pop(), - }); - break; + } - case '*': - if (last.length === 0) { - repeatErr(i); + if (ch === ']') { + brackets.pop(); + stack.pop(); + if (!stack.length) { + val += ch; + break; } - last.push({ - type: types.REPETITION, - min: 0, - max: Infinity, - value: last.pop(), - }); - break; - - - // Default is a character that is not `\[](){}?+*^$`. - default: - last.push({ - type: types.CHAR, - value: c.charCodeAt(0), - }); + } + val += ch; } - } - - // Check if any groups have not been closed. - if (groupStack.length !== 0) { - util.error(regexpStr, 'Unterminated group'); - } - - return start; + tok.split = false; + tok.val = val.slice(1); + tok.idx = i; + }; }; -module.exports.types = types; - +/** + * Returns true if the given string looks like a regex quantifier + * @return {Boolean} + */ -/***/ }), -/* 576 */ -/***/ (function(module, exports, __webpack_require__) { +utils.isQuantifier = function(str) { + return /^(?:[0-9]?,[0-9]|[0-9],)$/.test(str); +}; -var types = __webpack_require__(577); -var sets = __webpack_require__(578); +/** + * Cast `val` to an array. + * @param {*} `val` + */ +utils.stringifyArray = function(arr) { + return [utils.arrayify(arr).join('|')]; +}; -// All of these are private and only used by randexp. -// It's assumed that they will always be called with the correct input. +/** + * Cast `val` to an array. + * @param {*} `val` + */ -var CTRL = '@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^ ?'; -var SLSH = { '0': 0, 't': 9, 'n': 10, 'v': 11, 'f': 12, 'r': 13 }; +utils.arrayify = function(arr) { + if (typeof arr === 'undefined') { + return []; + } + if (typeof arr === 'string') { + return [arr]; + } + return arr; +}; /** - * Finds character representations in str and convert all to - * their respective characters - * - * @param {String} str - * @return {String} + * Returns true if the given `str` is a non-empty string + * @return {Boolean} */ -exports.strToChars = function(str) { - /* jshint maxlen: false */ - var chars_regex = /(\[\\b\])|(\\)?\\(?:u([A-F0-9]{4})|x([A-F0-9]{2})|(0?[0-7]{2})|c([@A-Z\[\\\]\^?])|([0tnvfr]))/g; - str = str.replace(chars_regex, function(s, b, lbs, a16, b16, c8, dctrl, eslsh) { - if (lbs) { - return s; - } - - var code = b ? 8 : - a16 ? parseInt(a16, 16) : - b16 ? parseInt(b16, 16) : - c8 ? parseInt(c8, 8) : - dctrl ? CTRL.indexOf(dctrl) : - SLSH[eslsh]; - var c = String.fromCharCode(code); +utils.isString = function(str) { + return str != null && typeof str === 'string'; +}; - // Escape special regex characters. - if (/[\[\]{}\^$.|?*+()]/.test(c)) { - c = '\\' + c; - } +/** + * Get the last element from `array` + * @param {Array} `array` + * @return {*} + */ - return c; - }); +utils.last = function(arr, n) { + return arr[arr.length - (n || 1)]; +}; - return str; +utils.escapeRegex = function(str) { + return str.replace(/\\?([!^*?()[\]{}+?/])/g, '\\$1'); }; -/** - * turns class into tokens - * reads str until it encounters a ] not preceeded by a \ +/***/ }), +/* 604 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +/*! + * split-string * - * @param {String} str - * @param {String} regexpStr - * @return {Array., Number>} + * Copyright (c) 2015-2017, Jon Schlinkert. + * Released under the MIT License. */ -exports.tokenizeClass = function(str, regexpStr) { - /* jshint maxlen: false */ - var tokens = []; - var regexp = /\\(?:(w)|(d)|(s)|(W)|(D)|(S))|((?:(?:\\)(.)|([^\]\\]))-(?:\\)?([^\]]))|(\])|(?:\\)?(.)/g; - var rs, c; - while ((rs = regexp.exec(str)) != null) { - if (rs[1]) { - tokens.push(sets.words()); - } else if (rs[2]) { - tokens.push(sets.ints()); +var extend = __webpack_require__(605); - } else if (rs[3]) { - tokens.push(sets.whitespace()); +module.exports = function(str, options, fn) { + if (typeof str !== 'string') { + throw new TypeError('expected a string'); + } - } else if (rs[4]) { - tokens.push(sets.notWords()); + if (typeof options === 'function') { + fn = options; + options = null; + } - } else if (rs[5]) { - tokens.push(sets.notInts()); + // allow separator to be defined as a string + if (typeof options === 'string') { + options = { sep: options }; + } - } else if (rs[6]) { - tokens.push(sets.notWhitespace()); + var opts = extend({sep: '.'}, options); + var quotes = opts.quotes || ['"', "'", '`']; + var brackets; - } else if (rs[7]) { - tokens.push({ - type: types.RANGE, - from: (rs[8] || rs[9]).charCodeAt(0), - to: rs[10].charCodeAt(0), - }); + if (opts.brackets === true) { + brackets = { + '<': '>', + '(': ')', + '[': ']', + '{': '}' + }; + } else if (opts.brackets) { + brackets = opts.brackets; + } - } else if (c = rs[12]) { - tokens.push({ - type: types.CHAR, - value: c.charCodeAt(0), - }); + var tokens = []; + var stack = []; + var arr = ['']; + var sep = opts.sep; + var len = str.length; + var idx = -1; + var closeIdx; - } else { - return [tokens, regexp.lastIndex]; + function expected() { + if (brackets && stack.length) { + return brackets[stack[stack.length - 1]]; } } - exports.error(regexpStr, 'Unterminated character class'); -}; + while (++idx < len) { + var ch = str[idx]; + var next = str[idx + 1]; + var tok = { val: ch, idx: idx, arr: arr, str: str }; + tokens.push(tok); + if (ch === '\\') { + tok.val = keepEscaping(opts, str, idx) === true ? (ch + next) : next; + tok.escaped = true; + if (typeof fn === 'function') { + fn(tok); + } + arr[arr.length - 1] += tok.val; + idx++; + continue; + } -/** - * Shortcut to throw errors. - * - * @param {String} regexp - * @param {String} msg - */ -exports.error = function(regexp, msg) { - throw new SyntaxError('Invalid regular expression: /' + regexp + '/: ' + msg); -}; + if (brackets && brackets[ch]) { + stack.push(ch); + var e = expected(); + var i = idx + 1; + if (str.indexOf(e, i + 1) !== -1) { + while (stack.length && i < len) { + var s = str[++i]; + if (s === '\\') { + s++; + continue; + } -/***/ }), -/* 577 */ -/***/ (function(module, exports) { + if (quotes.indexOf(s) !== -1) { + i = getClosingQuote(str, s, i + 1); + continue; + } -module.exports = { - ROOT : 0, - GROUP : 1, - POSITION : 2, - SET : 3, - RANGE : 4, - REPETITION : 5, - REFERENCE : 6, - CHAR : 7, -}; + e = expected(); + if (stack.length && str.indexOf(e, i + 1) === -1) { + break; + } + if (brackets[s]) { + stack.push(s); + continue; + } -/***/ }), -/* 578 */ -/***/ (function(module, exports, __webpack_require__) { + if (e === s) { + stack.pop(); + } + } + } -var types = __webpack_require__(577); + closeIdx = i; + if (closeIdx === -1) { + arr[arr.length - 1] += ch; + continue; + } -var INTS = function() { - return [{ type: types.RANGE , from: 48, to: 57 }]; -}; + ch = str.slice(idx, closeIdx + 1); + tok.val = ch; + tok.idx = idx = closeIdx; + } -var WORDS = function() { - return [ - { type: types.CHAR, value: 95 }, - { type: types.RANGE, from: 97, to: 122 }, - { type: types.RANGE, from: 65, to: 90 } - ].concat(INTS()); -}; + if (quotes.indexOf(ch) !== -1) { + closeIdx = getClosingQuote(str, ch, idx + 1); + if (closeIdx === -1) { + arr[arr.length - 1] += ch; + continue; + } -var WHITESPACE = function() { - return [ - { type: types.CHAR, value: 9 }, - { type: types.CHAR, value: 10 }, - { type: types.CHAR, value: 11 }, - { type: types.CHAR, value: 12 }, - { type: types.CHAR, value: 13 }, - { type: types.CHAR, value: 32 }, - { type: types.CHAR, value: 160 }, - { type: types.CHAR, value: 5760 }, - { type: types.CHAR, value: 6158 }, - { type: types.CHAR, value: 8192 }, - { type: types.CHAR, value: 8193 }, - { type: types.CHAR, value: 8194 }, - { type: types.CHAR, value: 8195 }, - { type: types.CHAR, value: 8196 }, - { type: types.CHAR, value: 8197 }, - { type: types.CHAR, value: 8198 }, - { type: types.CHAR, value: 8199 }, - { type: types.CHAR, value: 8200 }, - { type: types.CHAR, value: 8201 }, - { type: types.CHAR, value: 8202 }, - { type: types.CHAR, value: 8232 }, - { type: types.CHAR, value: 8233 }, - { type: types.CHAR, value: 8239 }, - { type: types.CHAR, value: 8287 }, - { type: types.CHAR, value: 12288 }, - { type: types.CHAR, value: 65279 } - ]; -}; + if (keepQuotes(ch, opts) === true) { + ch = str.slice(idx, closeIdx + 1); + } else { + ch = str.slice(idx + 1, closeIdx); + } -var NOTANYCHAR = function() { - return [ - { type: types.CHAR, value: 10 }, - { type: types.CHAR, value: 13 }, - { type: types.CHAR, value: 8232 }, - { type: types.CHAR, value: 8233 }, - ]; -}; + tok.val = ch; + tok.idx = idx = closeIdx; + } -// Predefined class objects. -exports.words = function() { - return { type: types.SET, set: WORDS(), not: false }; -}; + if (typeof fn === 'function') { + fn(tok, tokens); + ch = tok.val; + idx = tok.idx; + } -exports.notWords = function() { - return { type: types.SET, set: WORDS(), not: true }; -}; + if (tok.val === sep && tok.split !== false) { + arr.push(''); + continue; + } -exports.ints = function() { - return { type: types.SET, set: INTS(), not: false }; -}; + arr[arr.length - 1] += tok.val; + } -exports.notInts = function() { - return { type: types.SET, set: INTS(), not: true }; + return arr; }; -exports.whitespace = function() { - return { type: types.SET, set: WHITESPACE(), not: false }; -}; +function getClosingQuote(str, ch, i, brackets) { + var idx = str.indexOf(ch, i); + if (str.charAt(idx - 1) === '\\') { + return getClosingQuote(str, ch, idx + 1); + } + return idx; +} -exports.notWhitespace = function() { - return { type: types.SET, set: WHITESPACE(), not: true }; -}; +function keepQuotes(ch, opts) { + if (opts.keepDoubleQuotes === true && ch === '"') return true; + if (opts.keepSingleQuotes === true && ch === "'") return true; + return opts.keepQuotes; +} -exports.anyChar = function() { - return { type: types.SET, set: NOTANYCHAR(), not: true }; +function keepEscaping(opts, str, idx) { + if (typeof opts.keepEscaping === 'function') { + return opts.keepEscaping(str, idx); + } + return opts.keepEscaping === true || str[idx + 1] === '\\'; +} + + +/***/ }), +/* 605 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var isExtendable = __webpack_require__(606); +var assignSymbols = __webpack_require__(595); + +module.exports = Object.assign || function(obj/*, objects*/) { + if (obj === null || typeof obj === 'undefined') { + throw new TypeError('Cannot convert undefined or null to object'); + } + if (!isObject(obj)) { + obj = {}; + } + for (var i = 1; i < arguments.length; i++) { + var val = arguments[i]; + if (isString(val)) { + val = toObject(val); + } + if (isObject(val)) { + assign(obj, val); + assignSymbols(obj, val); + } + } + return obj; }; +function assign(a, b) { + for (var key in b) { + if (hasOwn(b, key)) { + a[key] = b[key]; + } + } +} -/***/ }), -/* 579 */ -/***/ (function(module, exports, __webpack_require__) { +function isString(val) { + return (val && typeof val === 'string'); +} -var types = __webpack_require__(577); +function toObject(str) { + var obj = {}; + for (var i in str) { + obj[i] = str[i]; + } + return obj; +} -exports.wordBoundary = function() { - return { type: types.POSITION, value: 'b' }; -}; +function isObject(val) { + return (val && typeof val === 'object') || isExtendable(val); +} -exports.nonWordBoundary = function() { - return { type: types.POSITION, value: 'B' }; -}; +/** + * Returns true if the given `key` is an own property of `obj`. + */ -exports.begin = function() { - return { type: types.POSITION, value: '^' }; -}; +function hasOwn(obj, key) { + return Object.prototype.hasOwnProperty.call(obj, key); +} -exports.end = function() { - return { type: types.POSITION, value: '$' }; -}; +function isEnum(obj, key) { + return Object.prototype.propertyIsEnumerable.call(obj, key); +} /***/ }), -/* 580 */ +/* 606 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /*! - * define-property + * is-extendable * - * Copyright (c) 2015-2018, Jon Schlinkert. + * Copyright (c) 2015-2017, Jon Schlinkert. * Released under the MIT License. */ -var isobject = __webpack_require__(581); -var isDescriptor = __webpack_require__(582); -var define = (typeof Reflect !== 'undefined' && Reflect.defineProperty) - ? Reflect.defineProperty - : Object.defineProperty; - -module.exports = function defineProperty(obj, key, val) { - if (!isobject(obj) && typeof obj !== 'function' && !Array.isArray(obj)) { - throw new TypeError('expected an object, function, or array'); - } - - if (typeof key !== 'string') { - throw new TypeError('expected "key" to be a string'); - } - - if (isDescriptor(val)) { - define(obj, key, val); - return obj; - } - - define(obj, key, { - configurable: true, - enumerable: false, - writable: true, - value: val - }); +var isPlainObject = __webpack_require__(594); - return obj; +module.exports = function isExtendable(val) { + return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); }; /***/ }), -/* 581 */ +/* 607 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /*! - * isobject + * arr-flatten * * Copyright (c) 2014-2017, Jon Schlinkert. * Released under the MIT License. @@ -64509,335 +68290,435 @@ module.exports = function defineProperty(obj, key, val) { -module.exports = function isObject(val) { - return val != null && typeof val === 'object' && Array.isArray(val) === false; +module.exports = function (arr) { + return flat(arr, []); }; +function flat(arr, res) { + var i = 0, cur; + var len = arr.length; + for (; i < len; i++) { + cur = arr[i]; + Array.isArray(cur) ? flat(cur, res) : res.push(cur); + } + return res; +} + /***/ }), -/* 582 */ +/* 608 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /*! - * is-descriptor + * fill-range * - * Copyright (c) 2015-2017, Jon Schlinkert. + * Copyright (c) 2014-2015, 2017, Jon Schlinkert. * Released under the MIT License. */ -var typeOf = __webpack_require__(583); -var isAccessor = __webpack_require__(584); -var isData = __webpack_require__(585); +var util = __webpack_require__(113); +var isNumber = __webpack_require__(609); +var extend = __webpack_require__(612); +var repeat = __webpack_require__(614); +var toRegex = __webpack_require__(615); -module.exports = function isDescriptor(obj, key) { - if (typeOf(obj) !== 'object') { - return false; +/** + * Return a range of numbers or letters. + * + * @param {String} `start` Start of the range + * @param {String} `stop` End of the range + * @param {String} `step` Increment or decrement to use. + * @param {Function} `fn` Custom function to modify each element in the range. + * @return {Array} + */ + +function fillRange(start, stop, step, options) { + if (typeof start === 'undefined') { + return []; } - if ('get' in obj) { - return isAccessor(obj, key); + + if (typeof stop === 'undefined' || start === stop) { + // special case, for handling negative zero + var isString = typeof start === 'string'; + if (isNumber(start) && !toNumber(start)) { + return [isString ? '0' : 0]; + } + return [start]; } - return isData(obj, key); -}; + if (typeof step !== 'number' && typeof step !== 'string') { + options = step; + step = undefined; + } -/***/ }), -/* 583 */ -/***/ (function(module, exports) { + if (typeof options === 'function') { + options = { transform: options }; + } -var toString = Object.prototype.toString; + var opts = extend({step: step}, options); + if (opts.step && !isValidNumber(opts.step)) { + if (opts.strictRanges === true) { + throw new TypeError('expected options.step to be a number'); + } + return []; + } -module.exports = function kindOf(val) { - if (val === void 0) return 'undefined'; - if (val === null) return 'null'; + opts.isNumber = isValidNumber(start) && isValidNumber(stop); + if (!opts.isNumber && !isValid(start, stop)) { + if (opts.strictRanges === true) { + throw new RangeError('invalid range arguments: ' + util.inspect([start, stop])); + } + return []; + } - var type = typeof val; - if (type === 'boolean') return 'boolean'; - if (type === 'string') return 'string'; - if (type === 'number') return 'number'; - if (type === 'symbol') return 'symbol'; - if (type === 'function') { - return isGeneratorFn(val) ? 'generatorfunction' : 'function'; + opts.isPadded = isPadded(start) || isPadded(stop); + opts.toString = opts.stringify + || typeof opts.step === 'string' + || typeof start === 'string' + || typeof stop === 'string' + || !opts.isNumber; + + if (opts.isPadded) { + opts.maxLength = Math.max(String(start).length, String(stop).length); } - if (isArray(val)) return 'array'; - if (isBuffer(val)) return 'buffer'; - if (isArguments(val)) return 'arguments'; - if (isDate(val)) return 'date'; - if (isError(val)) return 'error'; - if (isRegexp(val)) return 'regexp'; + // support legacy minimatch/fill-range options + if (typeof opts.optimize === 'boolean') opts.toRegex = opts.optimize; + if (typeof opts.makeRe === 'boolean') opts.toRegex = opts.makeRe; + return expand(start, stop, opts); +} - switch (ctorName(val)) { - case 'Symbol': return 'symbol'; - case 'Promise': return 'promise'; +function expand(start, stop, options) { + var a = options.isNumber ? toNumber(start) : start.charCodeAt(0); + var b = options.isNumber ? toNumber(stop) : stop.charCodeAt(0); - // Set, Map, WeakSet, WeakMap - case 'WeakMap': return 'weakmap'; - case 'WeakSet': return 'weakset'; - case 'Map': return 'map'; - case 'Set': return 'set'; + var step = Math.abs(toNumber(options.step)) || 1; + if (options.toRegex && step === 1) { + return toRange(a, b, start, stop, options); + } - // 8-bit typed arrays - case 'Int8Array': return 'int8array'; - case 'Uint8Array': return 'uint8array'; - case 'Uint8ClampedArray': return 'uint8clampedarray'; + var zero = {greater: [], lesser: []}; + var asc = a < b; + var arr = new Array(Math.round((asc ? b - a : a - b) / step)); + var idx = 0; - // 16-bit typed arrays - case 'Int16Array': return 'int16array'; - case 'Uint16Array': return 'uint16array'; + while (asc ? a <= b : a >= b) { + var val = options.isNumber ? a : String.fromCharCode(a); + if (options.toRegex && (val >= 0 || !options.isNumber)) { + zero.greater.push(val); + } else { + zero.lesser.push(Math.abs(val)); + } - // 32-bit typed arrays - case 'Int32Array': return 'int32array'; - case 'Uint32Array': return 'uint32array'; - case 'Float32Array': return 'float32array'; - case 'Float64Array': return 'float64array'; + if (options.isPadded) { + val = zeros(val, options); + } + + if (options.toString) { + val = String(val); + } + + if (typeof options.transform === 'function') { + arr[idx++] = options.transform(val, a, b, step, idx, arr, options); + } else { + arr[idx++] = val; + } + + if (asc) { + a += step; + } else { + a -= step; + } } - if (isGeneratorObj(val)) { - return 'generator'; + if (options.toRegex === true) { + return toSequence(arr, zero, options); } + return arr; +} - // Non-plain objects - type = toString.call(val); - switch (type) { - case '[object Object]': return 'object'; - // iterators - case '[object Map Iterator]': return 'mapiterator'; - case '[object Set Iterator]': return 'setiterator'; - case '[object String Iterator]': return 'stringiterator'; - case '[object Array Iterator]': return 'arrayiterator'; +function toRange(a, b, start, stop, options) { + if (options.isPadded) { + return toRegex(start, stop, options); } - // other - return type.slice(8, -1).toLowerCase().replace(/\s/g, ''); -}; + if (options.isNumber) { + return toRegex(Math.min(a, b), Math.max(a, b), options); + } -function ctorName(val) { - return typeof val.constructor === 'function' ? val.constructor.name : null; + var start = String.fromCharCode(Math.min(a, b)); + var stop = String.fromCharCode(Math.max(a, b)); + return '[' + start + '-' + stop + ']'; } -function isArray(val) { - if (Array.isArray) return Array.isArray(val); - return val instanceof Array; +function toSequence(arr, zeros, options) { + var greater = '', lesser = ''; + if (zeros.greater.length) { + greater = zeros.greater.join('|'); + } + if (zeros.lesser.length) { + lesser = '-(' + zeros.lesser.join('|') + ')'; + } + var res = greater && lesser + ? greater + '|' + lesser + : greater || lesser; + + if (options.capture) { + return '(' + res + ')'; + } + return res; } -function isError(val) { - return val instanceof Error || (typeof val.message === 'string' && val.constructor && typeof val.constructor.stackTraceLimit === 'number'); +function zeros(val, options) { + if (options.isPadded) { + var str = String(val); + var len = str.length; + var dash = ''; + if (str.charAt(0) === '-') { + dash = '-'; + str = str.slice(1); + } + var diff = options.maxLength - len; + var pad = repeat('0', diff); + val = (dash + pad + str); + } + if (options.stringify) { + return String(val); + } + return val; } -function isDate(val) { - if (val instanceof Date) return true; - return typeof val.toDateString === 'function' - && typeof val.getDate === 'function' - && typeof val.setDate === 'function'; +function toNumber(val) { + return Number(val) || 0; } -function isRegexp(val) { - if (val instanceof RegExp) return true; - return typeof val.flags === 'string' - && typeof val.ignoreCase === 'boolean' - && typeof val.multiline === 'boolean' - && typeof val.global === 'boolean'; +function isPadded(str) { + return /^-?0\d/.test(str); } -function isGeneratorFn(name, val) { - return ctorName(name) === 'GeneratorFunction'; +function isValid(min, max) { + return (isValidNumber(min) || isValidLetter(min)) + && (isValidNumber(max) || isValidLetter(max)); } -function isGeneratorObj(val) { - return typeof val.throw === 'function' - && typeof val.return === 'function' - && typeof val.next === 'function'; +function isValidLetter(ch) { + return typeof ch === 'string' && ch.length === 1 && /^\w+$/.test(ch); } -function isArguments(val) { - try { - if (typeof val.length === 'number' && typeof val.callee === 'function') { - return true; - } - } catch (err) { - if (err.message.indexOf('callee') !== -1) { - return true; - } - } - return false; +function isValidNumber(n) { + return isNumber(n) && !/\./.test(n); } /** - * If you need to support Safari 5-7 (8-10 yr-old browser), - * take a look at https://github.com/feross/is-buffer + * Expose `fillRange` + * @type {Function} */ -function isBuffer(val) { - if (val.constructor && typeof val.constructor.isBuffer === 'function') { - return val.constructor.isBuffer(val); - } - return false; -} +module.exports = fillRange; /***/ }), -/* 584 */ +/* 609 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /*! - * is-accessor-descriptor + * is-number * - * Copyright (c) 2015-2017, Jon Schlinkert. - * Released under the MIT License. + * Copyright (c) 2014-2015, Jon Schlinkert. + * Licensed under the MIT License. */ -var typeOf = __webpack_require__(583); +var typeOf = __webpack_require__(610); -// accessor descriptor properties -var accessor = { - get: 'function', - set: 'function', - configurable: 'boolean', - enumerable: 'boolean' +module.exports = function isNumber(num) { + var type = typeOf(num); + + if (type === 'string') { + if (!num.trim()) return false; + } else if (type !== 'number') { + return false; + } + + return (num - num + 1) >= 0; }; -function isAccessorDescriptor(obj, prop) { - if (typeof prop === 'string') { - var val = Object.getOwnPropertyDescriptor(obj, prop); - return typeof val !== 'undefined'; + +/***/ }), +/* 610 */ +/***/ (function(module, exports, __webpack_require__) { + +var isBuffer = __webpack_require__(611); +var toString = Object.prototype.toString; + +/** + * Get the native `typeof` a value. + * + * @param {*} `val` + * @return {*} Native javascript type + */ + +module.exports = function kindOf(val) { + // primitivies + if (typeof val === 'undefined') { + return 'undefined'; + } + if (val === null) { + return 'null'; + } + if (val === true || val === false || val instanceof Boolean) { + return 'boolean'; + } + if (typeof val === 'string' || val instanceof String) { + return 'string'; + } + if (typeof val === 'number' || val instanceof Number) { + return 'number'; } - if (typeOf(obj) !== 'object') { - return false; + // functions + if (typeof val === 'function' || val instanceof Function) { + return 'function'; } - if (has(obj, 'value') || has(obj, 'writable')) { - return false; + // array + if (typeof Array.isArray !== 'undefined' && Array.isArray(val)) { + return 'array'; } - if (!has(obj, 'get') || typeof obj.get !== 'function') { - return false; + // check for instances of RegExp and Date before calling `toString` + if (val instanceof RegExp) { + return 'regexp'; + } + if (val instanceof Date) { + return 'date'; } - // tldr: it's valid to have "set" be undefined - // "set" might be undefined if `Object.getOwnPropertyDescriptor` - // was used to get the value, and only `get` was defined by the user - if (has(obj, 'set') && typeof obj[key] !== 'function' && typeof obj[key] !== 'undefined') { - return false; + // other objects + var type = toString.call(val); + + if (type === '[object RegExp]') { + return 'regexp'; + } + if (type === '[object Date]') { + return 'date'; + } + if (type === '[object Arguments]') { + return 'arguments'; + } + if (type === '[object Error]') { + return 'error'; } - for (var key in obj) { - if (!accessor.hasOwnProperty(key)) { - continue; - } + // buffer + if (isBuffer(val)) { + return 'buffer'; + } - if (typeOf(obj[key]) === accessor[key]) { - continue; - } + // es6: Map, WeakMap, Set, WeakSet + if (type === '[object Set]') { + return 'set'; + } + if (type === '[object WeakSet]') { + return 'weakset'; + } + if (type === '[object Map]') { + return 'map'; + } + if (type === '[object WeakMap]') { + return 'weakmap'; + } + if (type === '[object Symbol]') { + return 'symbol'; + } - if (typeof obj[key] !== 'undefined') { - return false; - } + // typed arrays + if (type === '[object Int8Array]') { + return 'int8array'; + } + if (type === '[object Uint8Array]') { + return 'uint8array'; + } + if (type === '[object Uint8ClampedArray]') { + return 'uint8clampedarray'; + } + if (type === '[object Int16Array]') { + return 'int16array'; + } + if (type === '[object Uint16Array]') { + return 'uint16array'; + } + if (type === '[object Int32Array]') { + return 'int32array'; + } + if (type === '[object Uint32Array]') { + return 'uint32array'; + } + if (type === '[object Float32Array]') { + return 'float32array'; + } + if (type === '[object Float64Array]') { + return 'float64array'; } - return true; -} - -function has(obj, key) { - return {}.hasOwnProperty.call(obj, key); -} - -/** - * Expose `isAccessorDescriptor` - */ -module.exports = isAccessorDescriptor; + // must be a plain object + return 'object'; +}; /***/ }), -/* 585 */ -/***/ (function(module, exports, __webpack_require__) { +/* 611 */ +/***/ (function(module, exports) { -"use strict"; /*! - * is-data-descriptor + * Determine if an object is a Buffer * - * Copyright (c) 2015-2017, Jon Schlinkert. - * Released under the MIT License. + * @author Feross Aboukhadijeh + * @license MIT */ +// The _isBuffer check is for Safari 5-7 support, because it's missing +// Object.prototype.constructor. Remove this eventually +module.exports = function (obj) { + return obj != null && (isBuffer(obj) || isSlowBuffer(obj) || !!obj._isBuffer) +} +function isBuffer (obj) { + return !!obj.constructor && typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj) +} -var typeOf = __webpack_require__(583); - -module.exports = function isDataDescriptor(obj, prop) { - // data descriptor properties - var data = { - configurable: 'boolean', - enumerable: 'boolean', - writable: 'boolean' - }; - - if (typeOf(obj) !== 'object') { - return false; - } - - if (typeof prop === 'string') { - var val = Object.getOwnPropertyDescriptor(obj, prop); - return typeof val !== 'undefined'; - } - - if (!('value' in obj) && !('writable' in obj)) { - return false; - } - - for (var key in obj) { - if (key === 'value') continue; - - if (!data.hasOwnProperty(key)) { - continue; - } - - if (typeOf(obj[key]) === data[key]) { - continue; - } - - if (typeof obj[key] !== 'undefined') { - return false; - } - } - return true; -}; +// For Node v0.10 support. Remove this eventually. +function isSlowBuffer (obj) { + return typeof obj.readFloatLE === 'function' && typeof obj.slice === 'function' && isBuffer(obj.slice(0, 0)) +} /***/ }), -/* 586 */ +/* 612 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(587); -var assignSymbols = __webpack_require__(589); +var isObject = __webpack_require__(613); -module.exports = Object.assign || function(obj/*, objects*/) { - if (obj === null || typeof obj === 'undefined') { - throw new TypeError('Cannot convert undefined or null to object'); - } - if (!isObject(obj)) { - obj = {}; - } - for (var i = 1; i < arguments.length; i++) { - var val = arguments[i]; - if (isString(val)) { - val = toObject(val); - } - if (isObject(val)) { - assign(obj, val); - assignSymbols(obj, val); +module.exports = function extend(o/*, objects*/) { + if (!isObject(o)) { o = {}; } + + var len = arguments.length; + for (var i = 1; i < len; i++) { + var obj = arguments[i]; + + if (isObject(obj)) { + assign(o, obj); } } - return obj; + return o; }; function assign(a, b) { @@ -64848,22 +68729,6 @@ function assign(a, b) { } } -function isString(val) { - return (val && typeof val === 'string'); -} - -function toObject(str) { - var obj = {}; - for (var i in str) { - obj[i] = str[i]; - } - return obj; -} - -function isObject(val) { - return (val && typeof val === 'object') || isExtendable(val); -} - /** * Returns true if the given `key` is an own property of `obj`. */ @@ -64872,1583 +68737,2365 @@ function hasOwn(obj, key) { return Object.prototype.hasOwnProperty.call(obj, key); } -function isEnum(obj, key) { - return Object.prototype.propertyIsEnumerable.call(obj, key); -} - /***/ }), -/* 587 */ +/* 613 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /*! * is-extendable * - * Copyright (c) 2015-2017, Jon Schlinkert. - * Released under the MIT License. + * Copyright (c) 2015, Jon Schlinkert. + * Licensed under the MIT License. */ -var isPlainObject = __webpack_require__(588); - module.exports = function isExtendable(val) { - return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); + return typeof val !== 'undefined' && val !== null + && (typeof val === 'object' || typeof val === 'function'); }; /***/ }), -/* 588 */ +/* 614 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /*! - * is-plain-object + * repeat-string * - * Copyright (c) 2014-2017, Jon Schlinkert. - * Released under the MIT License. + * Copyright (c) 2014-2015, Jon Schlinkert. + * Licensed under the MIT License. */ -var isObject = __webpack_require__(581); +/** + * Results cache + */ -function isObjectObject(o) { - return isObject(o) === true - && Object.prototype.toString.call(o) === '[object Object]'; -} +var res = ''; +var cache; -module.exports = function isPlainObject(o) { - var ctor,prot; +/** + * Expose `repeat` + */ - if (isObjectObject(o) === false) return false; +module.exports = repeat; - // If has modified constructor - ctor = o.constructor; - if (typeof ctor !== 'function') return false; +/** + * Repeat the given `string` the specified `number` + * of times. + * + * **Example:** + * + * ```js + * var repeat = require('repeat-string'); + * repeat('A', 5); + * //=> AAAAA + * ``` + * + * @param {String} `string` The string to repeat + * @param {Number} `number` The number of times to repeat the string + * @return {String} Repeated string + * @api public + */ - // If has modified prototype - prot = ctor.prototype; - if (isObjectObject(prot) === false) return false; +function repeat(str, num) { + if (typeof str !== 'string') { + throw new TypeError('expected a string'); + } - // If constructor does not have an Object-specific method - if (prot.hasOwnProperty('isPrototypeOf') === false) { - return false; + // cover common, quick use cases + if (num === 1) return str; + if (num === 2) return str + str; + + var max = str.length * num; + if (cache !== str || typeof cache === 'undefined') { + cache = str; + res = ''; + } else if (res.length >= max) { + return res.substr(0, max); } - // Most likely a plain Object - return true; -}; + while (max > res.length && num > 1) { + if (num & 1) { + res += str; + } + + num >>= 1; + str += str; + } + + res += str; + res = res.substr(0, max); + return res; +} /***/ }), -/* 589 */ +/* 615 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /*! - * assign-symbols + * to-regex-range * - * Copyright (c) 2015, Jon Schlinkert. - * Licensed under the MIT License. + * Copyright (c) 2015, 2017, Jon Schlinkert. + * Released under the MIT License. */ -module.exports = function(receiver, objects) { - if (receiver === null || typeof receiver === 'undefined') { - throw new TypeError('expected first argument to be an object.'); - } +var repeat = __webpack_require__(614); +var isNumber = __webpack_require__(609); +var cache = {}; - if (typeof objects === 'undefined' || typeof Symbol === 'undefined') { - return receiver; +function toRegexRange(min, max, options) { + if (isNumber(min) === false) { + throw new RangeError('toRegexRange: first argument is invalid.'); } - if (typeof Object.getOwnPropertySymbols !== 'function') { - return receiver; + if (typeof max === 'undefined' || min === max) { + return String(min); } - var isEnumerable = Object.prototype.propertyIsEnumerable; - var target = Object(receiver); - var len = arguments.length, i = 0; + if (isNumber(max) === false) { + throw new RangeError('toRegexRange: second argument is invalid.'); + } - while (++i < len) { - var provider = Object(arguments[i]); - var names = Object.getOwnPropertySymbols(provider); + options = options || {}; + var relax = String(options.relaxZeros); + var shorthand = String(options.shorthand); + var capture = String(options.capture); + var key = min + ':' + max + '=' + relax + shorthand + capture; + if (cache.hasOwnProperty(key)) { + return cache[key].result; + } - for (var j = 0; j < names.length; j++) { - var key = names[j]; + var a = Math.min(min, max); + var b = Math.max(min, max); - if (isEnumerable.call(provider, key)) { - target[key] = provider[key]; - } + if (Math.abs(a - b) === 1) { + var result = min + '|' + max; + if (options.capture) { + return '(' + result + ')'; } + return result; } - return target; -}; + var isPadded = padding(min) || padding(max); + var positives = []; + var negatives = []; -/***/ }), -/* 590 */ -/***/ (function(module, exports, __webpack_require__) { + var tok = {min: min, max: max, a: a, b: b}; + if (isPadded) { + tok.isPadded = isPadded; + tok.maxLen = String(tok.max).length; + } -"use strict"; + if (a < 0) { + var newMin = b < 0 ? Math.abs(b) : 1; + var newMax = Math.abs(a); + negatives = splitToPatterns(newMin, newMax, tok, options); + a = tok.a = 0; + } + if (b >= 0) { + positives = splitToPatterns(a, b, tok, options); + } -var extend = __webpack_require__(591); -var safe = __webpack_require__(574); + tok.negatives = negatives; + tok.positives = positives; + tok.result = siftPatterns(negatives, positives, options); -/** - * The main export is a function that takes a `pattern` string and an `options` object. - * - * ```js - & var not = require('regex-not'); - & console.log(not('foo')); - & //=> /^(?:(?!^(?:foo)$).)*$/ - * ``` - * - * @param {String} `pattern` - * @param {Object} `options` - * @return {RegExp} Converts the given `pattern` to a regex using the specified `options`. - * @api public - */ + if (options.capture && (positives.length + negatives.length) > 1) { + tok.result = '(' + tok.result + ')'; + } -function toRegex(pattern, options) { - return new RegExp(toRegex.create(pattern, options)); + cache[key] = tok; + return tok.result; +} + +function siftPatterns(neg, pos, options) { + var onlyNegative = filterPatterns(neg, pos, '-', false, options) || []; + var onlyPositive = filterPatterns(pos, neg, '', false, options) || []; + var intersected = filterPatterns(neg, pos, '-?', true, options) || []; + var subpatterns = onlyNegative.concat(intersected).concat(onlyPositive); + return subpatterns.join('|'); +} + +function splitToRanges(min, max) { + min = Number(min); + max = Number(max); + + var nines = 1; + var stops = [max]; + var stop = +countNines(min, nines); + + while (min <= stop && stop <= max) { + stops = push(stops, stop); + nines += 1; + stop = +countNines(min, nines); + } + + var zeros = 1; + stop = countZeros(max + 1, zeros) - 1; + + while (min < stop && stop <= max) { + stops = push(stops, stop); + zeros += 1; + stop = countZeros(max + 1, zeros) - 1; + } + + stops.sort(compare); + return stops; } /** - * Create a regex-compatible string from the given `pattern` and `options`. - * - * ```js - & var not = require('regex-not'); - & console.log(not.create('foo')); - & //=> '^(?:(?!^(?:foo)$).)*$' - * ``` - * @param {String} `pattern` - * @param {Object} `options` + * Convert a range to a regex pattern + * @param {Number} `start` + * @param {Number} `stop` * @return {String} - * @api public */ -toRegex.create = function(pattern, options) { - if (typeof pattern !== 'string') { - throw new TypeError('expected a string'); +function rangeToPattern(start, stop, options) { + if (start === stop) { + return {pattern: String(start), digits: []}; } - var opts = extend({}, options); - if (opts.contains === true) { - opts.strictNegate = false; - } + var zipped = zip(String(start), String(stop)); + var len = zipped.length, i = -1; - var open = opts.strictOpen !== false ? '^' : ''; - var close = opts.strictClose !== false ? '$' : ''; - var endChar = opts.endChar ? opts.endChar : '+'; - var str = pattern; + var pattern = ''; + var digits = 0; - if (opts.strictNegate === false) { - str = '(?:(?!(?:' + pattern + ')).)' + endChar; - } else { - str = '(?:(?!^(?:' + pattern + ')$).)' + endChar; + while (++i < len) { + var numbers = zipped[i]; + var startDigit = numbers[0]; + var stopDigit = numbers[1]; + + if (startDigit === stopDigit) { + pattern += startDigit; + + } else if (startDigit !== '0' || stopDigit !== '9') { + pattern += toCharacterClass(startDigit, stopDigit); + + } else { + digits += 1; + } } - var res = open + str + close; - if (opts.safe === true && safe(res) === false) { - throw new Error('potentially unsafe regular expression: ' + res); + if (digits) { + pattern += options.shorthand ? '\\d' : '[0-9]'; } - return res; -}; + return { pattern: pattern, digits: [digits] }; +} -/** - * Expose `toRegex` - */ +function splitToPatterns(min, max, tok, options) { + var ranges = splitToRanges(min, max); + var len = ranges.length; + var idx = -1; -module.exports = toRegex; + var tokens = []; + var start = min; + var prev; + while (++idx < len) { + var range = ranges[idx]; + var obj = rangeToPattern(start, range, options); + var zeros = ''; -/***/ }), -/* 591 */ -/***/ (function(module, exports, __webpack_require__) { + if (!tok.isPadded && prev && prev.pattern === obj.pattern) { + if (prev.digits.length > 1) { + prev.digits.pop(); + } + prev.digits.push(obj.digits[0]); + prev.string = prev.pattern + toQuantifier(prev.digits); + start = range + 1; + continue; + } -"use strict"; + if (tok.isPadded) { + zeros = padZeros(range, tok); + } + + obj.string = zeros + obj.pattern + toQuantifier(obj.digits); + tokens.push(obj); + start = range + 1; + prev = obj; + } + + return tokens; +} +function filterPatterns(arr, comparison, prefix, intersection, options) { + var res = []; -var isExtendable = __webpack_require__(592); -var assignSymbols = __webpack_require__(589); + for (var i = 0; i < arr.length; i++) { + var tok = arr[i]; + var ele = tok.string; -module.exports = Object.assign || function(obj/*, objects*/) { - if (obj === null || typeof obj === 'undefined') { - throw new TypeError('Cannot convert undefined or null to object'); - } - if (!isObject(obj)) { - obj = {}; - } - for (var i = 1; i < arguments.length; i++) { - var val = arguments[i]; - if (isString(val)) { - val = toObject(val); + if (options.relaxZeros !== false) { + if (prefix === '-' && ele.charAt(0) === '0') { + if (ele.charAt(1) === '{') { + ele = '0*' + ele.replace(/^0\{\d+\}/, ''); + } else { + ele = '0*' + ele.slice(1); + } + } } - if (isObject(val)) { - assign(obj, val); - assignSymbols(obj, val); + + if (!intersection && !contains(comparison, 'string', ele)) { + res.push(prefix + ele); + } + + if (intersection && contains(comparison, 'string', ele)) { + res.push(prefix + ele); + } + } + return res; +} + +/** + * Zip strings (`for in` can be used on string characters) + */ + +function zip(a, b) { + var arr = []; + for (var ch in a) arr.push([a[ch], b[ch]]); + return arr; +} + +function compare(a, b) { + return a > b ? 1 : b > a ? -1 : 0; +} + +function push(arr, ele) { + if (arr.indexOf(ele) === -1) arr.push(ele); + return arr; +} + +function contains(arr, key, val) { + for (var i = 0; i < arr.length; i++) { + if (arr[i][key] === val) { + return true; } } - return obj; -}; + return false; +} -function assign(a, b) { - for (var key in b) { - if (hasOwn(b, key)) { - a[key] = b[key]; - } - } +function countNines(min, len) { + return String(min).slice(0, -len) + repeat('9', len); } -function isString(val) { - return (val && typeof val === 'string'); +function countZeros(integer, zeros) { + return integer - (integer % Math.pow(10, zeros)); } -function toObject(str) { - var obj = {}; - for (var i in str) { - obj[i] = str[i]; +function toQuantifier(digits) { + var start = digits[0]; + var stop = digits[1] ? (',' + digits[1]) : ''; + if (!stop && (!start || start === 1)) { + return ''; } - return obj; + return '{' + start + stop + '}'; } -function isObject(val) { - return (val && typeof val === 'object') || isExtendable(val); +function toCharacterClass(a, b) { + return '[' + a + ((b - a === 1) ? '' : '-') + b + ']'; } -/** - * Returns true if the given `key` is an own property of `obj`. - */ - -function hasOwn(obj, key) { - return Object.prototype.hasOwnProperty.call(obj, key); +function padding(str) { + return /^-?(0+)\d/.exec(str); } -function isEnum(obj, key) { - return Object.prototype.propertyIsEnumerable.call(obj, key); +function padZeros(val, tok) { + if (tok.isPadded) { + var diff = Math.abs(tok.maxLen - String(val).length); + switch (diff) { + case 0: + return ''; + case 1: + return '0'; + default: { + return '0{' + diff + '}'; + } + } + } + return val; } +/** + * Expose `toRegexRange` + */ + +module.exports = toRegexRange; + /***/ }), -/* 592 */ +/* 616 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /*! - * is-extendable + * repeat-element * - * Copyright (c) 2015-2017, Jon Schlinkert. - * Released under the MIT License. + * Copyright (c) 2015 Jon Schlinkert. + * Licensed under the MIT license. */ -var isPlainObject = __webpack_require__(588); +module.exports = function repeat(ele, num) { + var arr = new Array(num); -module.exports = function isExtendable(val) { - return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); + for (var i = 0; i < num; i++) { + arr[i] = ele; + } + + return arr; }; /***/ }), -/* 593 */ +/* 617 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -/*! - * array-unique - * - * Copyright (c) 2014-2015, Jon Schlinkert. - * Licensed under the MIT License. + + +var Node = __webpack_require__(618); +var utils = __webpack_require__(603); + +/** + * Braces parsers */ +module.exports = function(braces, options) { + braces.parser + .set('bos', function() { + if (!this.parsed) { + this.ast = this.nodes[0] = new Node(this.ast); + } + }) + /** + * Character parsers + */ -module.exports = function unique(arr) { - if (!Array.isArray(arr)) { - throw new TypeError('array-unique expects an array.'); - } + .set('escape', function() { + var pos = this.position(); + var m = this.match(/^(?:\\(.)|\$\{)/); + if (!m) return; - var len = arr.length; - var i = -1; + var prev = this.prev(); + var last = utils.last(prev.nodes); - while (i++ < len) { - var j = i + 1; + var node = pos(new Node({ + type: 'text', + multiplier: 1, + val: m[0] + })); - for (; j < arr.length; ++j) { - if (arr[i] === arr[j]) { - arr.splice(j--, 1); + if (node.val === '\\\\') { + return node; } - } - } - return arr; -}; -module.exports.immutable = function uniqueImmutable(arr) { - if (!Array.isArray(arr)) { - throw new TypeError('array-unique expects an array.'); - } + if (node.val === '${') { + var str = this.input; + var idx = -1; + var ch; - var arrLen = arr.length; - var newArr = new Array(arrLen); + while ((ch = str[++idx])) { + this.consume(1); + node.val += ch; + if (ch === '\\') { + node.val += str[++idx]; + continue; + } + if (ch === '}') { + break; + } + } + } - for (var i = 0; i < arrLen; i++) { - newArr[i] = arr[i]; - } + if (this.options.unescape !== false) { + node.val = node.val.replace(/\\([{}])/g, '$1'); + } - return module.exports(newArr); -}; + if (last.val === '"' && this.input.charAt(0) === '"') { + last.val = node.val; + this.consume(1); + return; + } + return concatNodes.call(this, pos, node, prev, options); + }) -/***/ }), -/* 594 */ -/***/ (function(module, exports, __webpack_require__) { + /** + * Brackets: "[...]" (basic, this is overridden by + * other parsers in more advanced implementations) + */ -"use strict"; + .set('bracket', function() { + var isInside = this.isInside('brace'); + var pos = this.position(); + var m = this.match(/^(?:\[([!^]?)([^\]]{2,}|\]-)(\]|[^*+?]+)|\[)/); + if (!m) return; + var prev = this.prev(); + var val = m[0]; + var negated = m[1] ? '^' : ''; + var inner = m[2] || ''; + var close = m[3] || ''; -var isObject = __webpack_require__(595); + if (isInside && prev.type === 'brace') { + prev.text = prev.text || ''; + prev.text += val; + } -module.exports = function extend(o/*, objects*/) { - if (!isObject(o)) { o = {}; } + var esc = this.input.slice(0, 2); + if (inner === '' && esc === '\\]') { + inner += esc; + this.consume(2); - var len = arguments.length; - for (var i = 1; i < len; i++) { - var obj = arguments[i]; + var str = this.input; + var idx = -1; + var ch; - if (isObject(obj)) { - assign(o, obj); - } - } - return o; -}; + while ((ch = str[++idx])) { + this.consume(1); + if (ch === ']') { + close = ch; + break; + } + inner += ch; + } + } -function assign(a, b) { - for (var key in b) { - if (hasOwn(b, key)) { - a[key] = b[key]; - } - } -} + return pos(new Node({ + type: 'bracket', + val: val, + escaped: close !== ']', + negated: negated, + inner: inner, + close: close + })); + }) -/** - * Returns true if the given `key` is an own property of `obj`. - */ + /** + * Empty braces (we capture these early to + * speed up processing in the compiler) + */ -function hasOwn(obj, key) { - return Object.prototype.hasOwnProperty.call(obj, key); -} + .set('multiplier', function() { + var isInside = this.isInside('brace'); + var pos = this.position(); + var m = this.match(/^\{((?:,|\{,+\})+)\}/); + if (!m) return; + this.multiplier = true; + var prev = this.prev(); + var val = m[0]; -/***/ }), -/* 595 */ -/***/ (function(module, exports, __webpack_require__) { + if (isInside && prev.type === 'brace') { + prev.text = prev.text || ''; + prev.text += val; + } -"use strict"; -/*! - * is-extendable - * - * Copyright (c) 2015, Jon Schlinkert. - * Licensed under the MIT License. - */ + var node = pos(new Node({ + type: 'text', + multiplier: 1, + match: m, + val: val + })); + return concatNodes.call(this, pos, node, prev, options); + }) + /** + * Open + */ -module.exports = function isExtendable(val) { - return typeof val !== 'undefined' && val !== null - && (typeof val === 'object' || typeof val === 'function'); -}; + .set('brace.open', function() { + var pos = this.position(); + var m = this.match(/^\{(?!(?:[^\\}]?|,+)\})/); + if (!m) return; + var prev = this.prev(); + var last = utils.last(prev.nodes); -/***/ }), -/* 596 */ -/***/ (function(module, exports, __webpack_require__) { + // if the last parsed character was an extglob character + // we need to _not optimize_ the brace pattern because + // it might be mistaken for an extglob by a downstream parser + if (last && last.val && isExtglobChar(last.val.slice(-1))) { + last.optimize = false; + } -"use strict"; + var open = pos(new Node({ + type: 'brace.open', + val: m[0] + })); + + var node = pos(new Node({ + type: 'brace', + nodes: [] + })); + + node.push(open); + prev.push(node); + this.push('brace', node); + }) + /** + * Close + */ -var utils = __webpack_require__(597); + .set('brace.close', function() { + var pos = this.position(); + var m = this.match(/^\}/); + if (!m || !m[0]) return; -module.exports = function(braces, options) { - braces.compiler + var brace = this.pop('brace'); + var node = pos(new Node({ + type: 'brace.close', + val: m[0] + })); + + if (!this.isType(brace, 'brace')) { + if (this.options.strict) { + throw new Error('missing opening "{"'); + } + node.type = 'text'; + node.multiplier = 0; + node.escaped = true; + return node; + } + + var prev = this.prev(); + var last = utils.last(prev.nodes); + if (last.text) { + var lastNode = utils.last(last.nodes); + if (lastNode.val === ')' && /[!@*?+]\(/.test(last.text)) { + var open = last.nodes[0]; + var text = last.nodes[1]; + if (open.type === 'brace.open' && text && text.type === 'text') { + text.optimize = false; + } + } + } + + if (brace.nodes.length > 2) { + var first = brace.nodes[1]; + if (first.type === 'text' && first.val === ',') { + brace.nodes.splice(1, 1); + brace.nodes.push(first); + } + } + + brace.push(node); + }) /** - * bos + * Capture boundary characters */ - .set('bos', function() { - if (this.output) return; - this.ast.queue = isEscaped(this.ast) ? [this.ast.val] : []; - this.ast.count = 1; + .set('boundary', function() { + var pos = this.position(); + var m = this.match(/^[$^](?!\{)/); + if (!m) return; + return pos(new Node({ + type: 'text', + val: m[0] + })); }) /** - * Square brackets + * One or zero, non-comma characters wrapped in braces */ - .set('bracket', function(node) { - var close = node.close; - var open = !node.escaped ? '[' : '\\['; - var negated = node.negated; - var inner = node.inner; + .set('nobrace', function() { + var isInside = this.isInside('brace'); + var pos = this.position(); + var m = this.match(/^\{[^,]?\}/); + if (!m) return; - inner = inner.replace(/\\(?=[\\\w]|$)/g, '\\\\'); - if (inner === ']-') { - inner = '\\]\\-'; - } + var prev = this.prev(); + var val = m[0]; - if (negated && inner.indexOf('.') === -1) { - inner += '.'; + if (isInside && prev.type === 'brace') { + prev.text = prev.text || ''; + prev.text += val; } - if (negated && inner.indexOf('/') === -1) { - inner += '/'; + + return pos(new Node({ + type: 'text', + multiplier: 0, + val: val + })); + }) + + /** + * Text + */ + + .set('text', function() { + var isInside = this.isInside('brace'); + var pos = this.position(); + var m = this.match(/^((?!\\)[^${}[\]])+/); + if (!m) return; + + var prev = this.prev(); + var val = m[0]; + + if (isInside && prev.type === 'brace') { + prev.text = prev.text || ''; + prev.text += val; } - var val = open + negated + inner + close; - var queue = node.parent.queue; - var last = utils.arrayify(queue.pop()); + var node = pos(new Node({ + type: 'text', + multiplier: 1, + val: val + })); + + return concatNodes.call(this, pos, node, prev, options); + }); +}; + +/** + * Returns true if the character is an extglob character. + */ + +function isExtglobChar(ch) { + return ch === '!' || ch === '@' || ch === '*' || ch === '?' || ch === '+'; +} + +/** + * Combine text nodes, and calculate empty sets (`{,,}`) + * @param {Function} `pos` Function to calculate node position + * @param {Object} `node` AST node + * @return {Object} + */ + +function concatNodes(pos, node, parent, options) { + node.orig = node.val; + var prev = this.prev(); + var last = utils.last(prev.nodes); + var isEscaped = false; + + if (node.val.length > 1) { + var a = node.val.charAt(0); + var b = node.val.slice(-1); + + isEscaped = (a === '"' && b === '"') + || (a === "'" && b === "'") + || (a === '`' && b === '`'); + } + + if (isEscaped && options.unescape !== false) { + node.val = node.val.slice(1, node.val.length - 1); + node.escaped = true; + } + + if (node.match) { + var match = node.match[1]; + if (!match || match.indexOf('}') === -1) { + match = node.match[0]; + } + + // replace each set with a single "," + var val = match.replace(/\{/g, ',').replace(/\}/g, ''); + node.multiplier *= val.length; + node.val = ''; + } + + var simpleText = last.type === 'text' + && last.multiplier === 1 + && node.multiplier === 1 + && node.val; + + if (simpleText) { + last.val += node.val; + return; + } + + prev.push(node); +} + + +/***/ }), +/* 618 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var isObject = __webpack_require__(587); +var define = __webpack_require__(619); +var utils = __webpack_require__(620); +var ownNames; + +/** + * Create a new AST `Node` with the given `val` and `type`. + * + * ```js + * var node = new Node('*', 'Star'); + * var node = new Node({type: 'star', val: '*'}); + * ``` + * @name Node + * @param {String|Object} `val` Pass a matched substring, or an object to merge onto the node. + * @param {String} `type` The node type to use when `val` is a string. + * @return {Object} node instance + * @api public + */ + +function Node(val, type, parent) { + if (typeof type !== 'string') { + parent = type; + type = null; + } - queue.push(utils.join(last, val)); - queue.push.apply(queue, []); - }) + define(this, 'parent', parent); + define(this, 'isNode', true); + define(this, 'expect', null); - /** - * Brace - */ + if (typeof type !== 'string' && isObject(val)) { + lazyKeys(); + var keys = Object.keys(val); + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + if (ownNames.indexOf(key) === -1) { + this[key] = val[key]; + } + } + } else { + this.type = type; + this.val = val; + } +} - .set('brace', function(node) { - node.queue = isEscaped(node) ? [node.val] : []; - node.count = 1; - return this.mapVisit(node.nodes); - }) +/** + * Returns true if the given value is a node. + * + * ```js + * var Node = require('snapdragon-node'); + * var node = new Node({type: 'foo'}); + * console.log(Node.isNode(node)); //=> true + * console.log(Node.isNode({})); //=> false + * ``` + * @param {Object} `node` + * @returns {Boolean} + * @api public + */ - /** - * Open - */ +Node.isNode = function(node) { + return utils.isNode(node); +}; - .set('brace.open', function(node) { - node.parent.open = node.val; - }) +/** + * Define a non-enumberable property on the node instance. + * Useful for adding properties that shouldn't be extended + * or visible during debugging. + * + * ```js + * var node = new Node(); + * node.define('foo', 'something non-enumerable'); + * ``` + * @param {String} `name` + * @param {any} `val` + * @return {Object} returns the node instance + * @api public + */ - /** - * Inner - */ +Node.prototype.define = function(name, val) { + define(this, name, val); + return this; +}; - .set('text', function(node) { - var queue = node.parent.queue; - var escaped = node.escaped; - var segs = [node.val]; +/** + * Returns true if `node.val` is an empty string, or `node.nodes` does + * not contain any non-empty text nodes. + * + * ```js + * var node = new Node({type: 'text'}); + * node.isEmpty(); //=> true + * node.val = 'foo'; + * node.isEmpty(); //=> false + * ``` + * @param {Function} `fn` (optional) Filter function that is called on `node` and/or child nodes. `isEmpty` will return false immediately when the filter function returns false on any nodes. + * @return {Boolean} + * @api public + */ - if (node.optimize === false) { - options = utils.extend({}, options, {optimize: false}); - } +Node.prototype.isEmpty = function(fn) { + return utils.isEmpty(this, fn); +}; - if (node.multiplier > 1) { - node.parent.count *= node.multiplier; - } +/** + * Given node `foo` and node `bar`, push node `bar` onto `foo.nodes`, and + * set `foo` as `bar.parent`. + * + * ```js + * var foo = new Node({type: 'foo'}); + * var bar = new Node({type: 'bar'}); + * foo.push(bar); + * ``` + * @param {Object} `node` + * @return {Number} Returns the length of `node.nodes` + * @api public + */ - if (options.quantifiers === true && utils.isQuantifier(node.val)) { - escaped = true; +Node.prototype.push = function(node) { + assert(Node.isNode(node), 'expected node to be an instance of Node'); + define(node, 'parent', this); - } else if (node.val.length > 1) { - if (isType(node.parent, 'brace') && !isEscaped(node)) { - var expanded = utils.expand(node.val, options); - segs = expanded.segs; + this.nodes = this.nodes || []; + return this.nodes.push(node); +}; - if (expanded.isOptimized) { - node.parent.isOptimized = true; - } +/** + * Given node `foo` and node `bar`, unshift node `bar` onto `foo.nodes`, and + * set `foo` as `bar.parent`. + * + * ```js + * var foo = new Node({type: 'foo'}); + * var bar = new Node({type: 'bar'}); + * foo.unshift(bar); + * ``` + * @param {Object} `node` + * @return {Number} Returns the length of `node.nodes` + * @api public + */ - // if nothing was expanded, we probably have a literal brace - if (!segs.length) { - var val = (expanded.val || node.val); - if (options.unescape !== false) { - // unescape unexpanded brace sequence/set separators - val = val.replace(/\\([,.])/g, '$1'); - // strip quotes - val = val.replace(/["'`]/g, ''); - } +Node.prototype.unshift = function(node) { + assert(Node.isNode(node), 'expected node to be an instance of Node'); + define(node, 'parent', this); - segs = [val]; - escaped = true; - } - } + this.nodes = this.nodes || []; + return this.nodes.unshift(node); +}; - } else if (node.val === ',') { - if (options.expand) { - node.parent.queue.push(['']); - segs = ['']; - } else { - segs = ['|']; - } - } else { - escaped = true; - } +/** + * Pop a node from `node.nodes`. + * + * ```js + * var node = new Node({type: 'foo'}); + * node.push(new Node({type: 'a'})); + * node.push(new Node({type: 'b'})); + * node.push(new Node({type: 'c'})); + * node.push(new Node({type: 'd'})); + * console.log(node.nodes.length); + * //=> 4 + * node.pop(); + * console.log(node.nodes.length); + * //=> 3 + * ``` + * @return {Number} Returns the popped `node` + * @api public + */ - if (escaped && isType(node.parent, 'brace')) { - if (node.parent.nodes.length <= 4 && node.parent.count === 1) { - node.parent.escaped = true; - } else if (node.parent.length <= 3) { - node.parent.escaped = true; - } - } +Node.prototype.pop = function() { + return this.nodes && this.nodes.pop(); +}; - if (!hasQueue(node.parent)) { - node.parent.queue = segs; - return; - } +/** + * Shift a node from `node.nodes`. + * + * ```js + * var node = new Node({type: 'foo'}); + * node.push(new Node({type: 'a'})); + * node.push(new Node({type: 'b'})); + * node.push(new Node({type: 'c'})); + * node.push(new Node({type: 'd'})); + * console.log(node.nodes.length); + * //=> 4 + * node.shift(); + * console.log(node.nodes.length); + * //=> 3 + * ``` + * @return {Object} Returns the shifted `node` + * @api public + */ - var last = utils.arrayify(queue.pop()); - if (node.parent.count > 1 && options.expand) { - last = multiply(last, node.parent.count); - node.parent.count = 1; - } +Node.prototype.shift = function() { + return this.nodes && this.nodes.shift(); +}; - queue.push(utils.join(utils.flatten(last), segs.shift())); - queue.push.apply(queue, segs); - }) +/** + * Remove `node` from `node.nodes`. + * + * ```js + * node.remove(childNode); + * ``` + * @param {Object} `node` + * @return {Object} Returns the removed node. + * @api public + */ - /** - * Close - */ +Node.prototype.remove = function(node) { + assert(Node.isNode(node), 'expected node to be an instance of Node'); + this.nodes = this.nodes || []; + var idx = node.index; + if (idx !== -1) { + node.index = -1; + return this.nodes.splice(idx, 1); + } + return null; +}; - .set('brace.close', function(node) { - var queue = node.parent.queue; - var prev = node.parent.parent; - var last = prev.queue.pop(); - var open = node.parent.open; - var close = node.val; +/** + * Get the first child node from `node.nodes` that matches the given `type`. + * If `type` is a number, the child node at that index is returned. + * + * ```js + * var child = node.find(1); //<= index of the node to get + * var child = node.find('foo'); //<= node.type of a child node + * var child = node.find(/^(foo|bar)$/); //<= regex to match node.type + * var child = node.find(['foo', 'bar']); //<= array of node.type(s) + * ``` + * @param {String} `type` + * @return {Object} Returns a child node or undefined. + * @api public + */ - if (open && close && isOptimized(node, options)) { - open = '('; - close = ')'; - } +Node.prototype.find = function(type) { + return utils.findNode(this.nodes, type); +}; - // if a close brace exists, and the previous segment is one character - // don't wrap the result in braces or parens - var ele = utils.last(queue); - if (node.parent.count > 1 && options.expand) { - ele = multiply(queue.pop(), node.parent.count); - node.parent.count = 1; - queue.push(ele); - } +/** + * Return true if the node is the given `type`. + * + * ```js + * var node = new Node({type: 'bar'}); + * cosole.log(node.isType('foo')); // false + * cosole.log(node.isType(/^(foo|bar)$/)); // true + * cosole.log(node.isType(['foo', 'bar'])); // true + * ``` + * @param {String} `type` + * @return {Boolean} + * @api public + */ - if (close && typeof ele === 'string' && ele.length === 1) { - open = ''; - close = ''; - } +Node.prototype.isType = function(type) { + return utils.isType(this, type); +}; - if ((isLiteralBrace(node, options) || noInner(node)) && !node.parent.hasEmpty) { - queue.push(utils.join(open, queue.pop() || '')); - queue = utils.flatten(utils.join(queue, close)); - } +/** + * Return true if the `node.nodes` has the given `type`. + * + * ```js + * var foo = new Node({type: 'foo'}); + * var bar = new Node({type: 'bar'}); + * foo.push(bar); + * + * cosole.log(foo.hasType('qux')); // false + * cosole.log(foo.hasType(/^(qux|bar)$/)); // true + * cosole.log(foo.hasType(['qux', 'bar'])); // true + * ``` + * @param {String} `type` + * @return {Boolean} + * @api public + */ - if (typeof last === 'undefined') { - prev.queue = [queue]; - } else { - prev.queue.push(utils.flatten(utils.join(last, queue))); - } - }) +Node.prototype.hasType = function(type) { + return utils.hasType(this, type); +}; - /** - * eos - */ +/** + * Get the siblings array, or `null` if it doesn't exist. + * + * ```js + * var foo = new Node({type: 'foo'}); + * var bar = new Node({type: 'bar'}); + * var baz = new Node({type: 'baz'}); + * foo.push(bar); + * foo.push(baz); + * + * console.log(bar.siblings.length) // 2 + * console.log(baz.siblings.length) // 2 + * ``` + * @return {Array} + * @api public + */ - .set('eos', function(node) { - if (this.input) return; +Object.defineProperty(Node.prototype, 'siblings', { + set: function() { + throw new Error('node.siblings is a getter and cannot be defined'); + }, + get: function() { + return this.parent ? this.parent.nodes : null; + } +}); - if (options.optimize !== false) { - this.output = utils.last(utils.flatten(this.ast.queue)); - } else if (Array.isArray(utils.last(this.ast.queue))) { - this.output = utils.flatten(this.ast.queue.pop()); - } else { - this.output = utils.flatten(this.ast.queue); - } +/** + * Get the node's current index from `node.parent.nodes`. + * This should always be correct, even when the parent adds nodes. + * + * ```js + * var foo = new Node({type: 'foo'}); + * var bar = new Node({type: 'bar'}); + * var baz = new Node({type: 'baz'}); + * var qux = new Node({type: 'qux'}); + * foo.push(bar); + * foo.push(baz); + * foo.unshift(qux); + * + * console.log(bar.index) // 1 + * console.log(baz.index) // 2 + * console.log(qux.index) // 0 + * ``` + * @return {Number} + * @api public + */ - if (node.parent.count > 1 && options.expand) { - this.output = multiply(this.output, node.parent.count); - } +Object.defineProperty(Node.prototype, 'index', { + set: function(index) { + define(this, 'idx', index); + }, + get: function() { + if (!Array.isArray(this.siblings)) { + return -1; + } + var tok = this.idx !== -1 ? this.siblings[this.idx] : null; + if (tok !== this) { + this.idx = this.siblings.indexOf(this); + } + return this.idx; + } +}); - this.output = utils.arrayify(this.output); - this.ast.queue = []; - }); +/** + * Get the previous node from the siblings array or `null`. + * + * ```js + * var foo = new Node({type: 'foo'}); + * var bar = new Node({type: 'bar'}); + * var baz = new Node({type: 'baz'}); + * foo.push(bar); + * foo.push(baz); + * + * console.log(baz.prev.type) // 'bar' + * ``` + * @return {Object} + * @api public + */ -}; +Object.defineProperty(Node.prototype, 'prev', { + set: function() { + throw new Error('node.prev is a getter and cannot be defined'); + }, + get: function() { + if (Array.isArray(this.siblings)) { + return this.siblings[this.index - 1] || this.parent.prev; + } + return null; + } +}); /** - * Multiply the segments in the current brace level + * Get the siblings array, or `null` if it doesn't exist. + * + * ```js + * var foo = new Node({type: 'foo'}); + * var bar = new Node({type: 'bar'}); + * var baz = new Node({type: 'baz'}); + * foo.push(bar); + * foo.push(baz); + * + * console.log(bar.siblings.length) // 2 + * console.log(baz.siblings.length) // 2 + * ``` + * @return {Object} + * @api public */ -function multiply(queue, n, options) { - return utils.flatten(utils.repeat(utils.arrayify(queue), n)); -} +Object.defineProperty(Node.prototype, 'next', { + set: function() { + throw new Error('node.next is a getter and cannot be defined'); + }, + get: function() { + if (Array.isArray(this.siblings)) { + return this.siblings[this.index + 1] || this.parent.next; + } + return null; + } +}); /** - * Return true if `node` is escaped + * Get the first node from `node.nodes`. + * + * ```js + * var foo = new Node({type: 'foo'}); + * var bar = new Node({type: 'bar'}); + * var baz = new Node({type: 'baz'}); + * var qux = new Node({type: 'qux'}); + * foo.push(bar); + * foo.push(baz); + * foo.push(qux); + * + * console.log(foo.first.type) // 'bar' + * ``` + * @return {Object} The first node, or undefiend + * @api public */ -function isEscaped(node) { - return node.escaped === true; -} +Object.defineProperty(Node.prototype, 'first', { + get: function() { + return this.nodes ? this.nodes[0] : null; + } +}); /** - * Returns true if regex parens should be used for sets. If the parent `type` - * is not `brace`, then we're on a root node, which means we should never - * expand segments and open/close braces should be `{}` (since this indicates - * a brace is missing from the set) + * Get the last node from `node.nodes`. + * + * ```js + * var foo = new Node({type: 'foo'}); + * var bar = new Node({type: 'bar'}); + * var baz = new Node({type: 'baz'}); + * var qux = new Node({type: 'qux'}); + * foo.push(bar); + * foo.push(baz); + * foo.push(qux); + * + * console.log(foo.last.type) // 'qux' + * ``` + * @return {Object} The last node, or undefiend + * @api public */ -function isOptimized(node, options) { - if (node.parent.isOptimized) return true; - return isType(node.parent, 'brace') - && !isEscaped(node.parent) - && options.expand !== true; -} +Object.defineProperty(Node.prototype, 'last', { + get: function() { + return this.nodes ? utils.last(this.nodes) : null; + } +}); /** - * Returns true if the value in `node` should be wrapped in a literal brace. - * @return {Boolean} + * Get the last node from `node.nodes`. + * + * ```js + * var foo = new Node({type: 'foo'}); + * var bar = new Node({type: 'bar'}); + * var baz = new Node({type: 'baz'}); + * var qux = new Node({type: 'qux'}); + * foo.push(bar); + * foo.push(baz); + * foo.push(qux); + * + * console.log(foo.last.type) // 'qux' + * ``` + * @return {Object} The last node, or undefiend + * @api public */ -function isLiteralBrace(node, options) { - return isEscaped(node.parent) || options.optimize !== false; -} +Object.defineProperty(Node.prototype, 'scope', { + get: function() { + if (this.isScope !== true) { + return this.parent ? this.parent.scope : this; + } + return this; + } +}); /** - * Returns true if the given `node` does not have an inner value. - * @return {Boolean} + * Get own property names from Node prototype, but only the + * first time `Node` is instantiated */ -function noInner(node, type) { - if (node.parent.queue.length === 1) { - return true; +function lazyKeys() { + if (!ownNames) { + ownNames = Object.getOwnPropertyNames(Node.prototype); } - var nodes = node.parent.nodes; - return nodes.length === 3 - && isType(nodes[0], 'brace.open') - && !isType(nodes[1], 'text') - && isType(nodes[2], 'brace.close'); } /** - * Returns true if the given `node` is the given `type` - * @return {Boolean} + * Simplified assertion. Throws an error is `val` is falsey. */ -function isType(node, type) { - return typeof node !== 'undefined' && node.type === type; +function assert(val, message) { + if (!val) throw new Error(message); } /** - * Returns true if the given `node` has a non-empty queue. - * @return {Boolean} + * Expose `Node` */ -function hasQueue(node) { - return Array.isArray(node.queue) && node.queue.length; -} +exports = module.exports = Node; /***/ }), -/* 597 */ +/* 619 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; +/*! + * define-property + * + * Copyright (c) 2015, 2017, Jon Schlinkert. + * Released under the MIT License. + */ -var splitString = __webpack_require__(598); -var utils = module.exports; -/** - * Module dependencies - */ +var isDescriptor = __webpack_require__(588); -utils.extend = __webpack_require__(594); -utils.flatten = __webpack_require__(601); -utils.isObject = __webpack_require__(581); -utils.fillRange = __webpack_require__(602); -utils.repeat = __webpack_require__(610); -utils.unique = __webpack_require__(593); +module.exports = function defineProperty(obj, prop, val) { + if (typeof obj !== 'object' && typeof obj !== 'function') { + throw new TypeError('expected an object or function.'); + } -utils.define = function(obj, key, val) { - Object.defineProperty(obj, key, { - writable: true, + if (typeof prop !== 'string') { + throw new TypeError('expected `prop` to be a string.'); + } + + if (isDescriptor(val) && ('set' in val || 'get' in val)) { + return Object.defineProperty(obj, prop, val); + } + + return Object.defineProperty(obj, prop, { configurable: true, enumerable: false, + writable: true, value: val }); }; + +/***/ }), +/* 620 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var typeOf = __webpack_require__(621); +var utils = module.exports; + /** - * Returns true if the given string contains only empty brace sets. + * Returns true if the given value is a node. + * + * ```js + * var Node = require('snapdragon-node'); + * var node = new Node({type: 'foo'}); + * console.log(utils.isNode(node)); //=> true + * console.log(utils.isNode({})); //=> false + * ``` + * @param {Object} `node` Instance of [snapdragon-node][] + * @returns {Boolean} + * @api public */ -utils.isEmptySets = function(str) { - return /^(?:\{,\})+$/.test(str); +utils.isNode = function(node) { + return typeOf(node) === 'object' && node.isNode === true; }; /** - * Returns true if the given string contains only empty brace sets. + * Emit an empty string for the given `node`. + * + * ```js + * // do nothing for beginning-of-string + * snapdragon.compiler.set('bos', utils.noop); + * ``` + * @param {Object} `node` Instance of [snapdragon-node][] + * @returns {undefined} + * @api public */ -utils.isQuotedString = function(str) { - var open = str.charAt(0); - if (open === '\'' || open === '"' || open === '`') { - return str.slice(-1) === open; - } - return false; +utils.noop = function(node) { + append(this, '', node); }; /** - * Create the key to use for memoization. The unique key is generated - * by iterating over the options and concatenating key-value pairs - * to the pattern string. + * Appdend `node.val` to `compiler.output`, exactly as it was created + * by the parser. + * + * ```js + * snapdragon.compiler.set('text', utils.identity); + * ``` + * @param {Object} `node` Instance of [snapdragon-node][] + * @returns {undefined} + * @api public */ -utils.createKey = function(pattern, options) { - var id = pattern; - if (typeof options === 'undefined') { - return id; - } - var keys = Object.keys(options); - for (var i = 0; i < keys.length; i++) { - var key = keys[i]; - id += ';' + key + '=' + String(options[key]); - } - return id; +utils.identity = function(node) { + append(this, node.val, node); }; /** - * Normalize options + * Previously named `.emit`, this method appends the given `val` + * to `compiler.output` for the given node. Useful when you know + * what value should be appended advance, regardless of the actual + * value of `node.val`. + * + * ```js + * snapdragon.compiler + * .set('i', function(node) { + * this.mapVisit(node); + * }) + * .set('i.open', utils.append('')) + * .set('i.close', utils.append('')) + * ``` + * @param {Object} `node` Instance of [snapdragon-node][] + * @returns {Function} Returns a compiler middleware function. + * @api public */ -utils.createOptions = function(options) { - var opts = utils.extend.apply(null, arguments); - if (typeof opts.expand === 'boolean') { - opts.optimize = !opts.expand; - } - if (typeof opts.optimize === 'boolean') { - opts.expand = !opts.optimize; - } - if (opts.optimize === true) { - opts.makeRe = true; - } - return opts; +utils.append = function(val) { + return function(node) { + append(this, val, node); + }; }; /** - * Join patterns in `a` to patterns in `b` + * Used in compiler middleware, this onverts an AST node into + * an empty `text` node and deletes `node.nodes` if it exists. + * The advantage of this method is that, as opposed to completely + * removing the node, indices will not need to be re-calculated + * in sibling nodes, and nothing is appended to the output. + * + * ```js + * utils.toNoop(node); + * // convert `node.nodes` to the given value instead of deleting it + * utils.toNoop(node, []); + * ``` + * @param {Object} `node` Instance of [snapdragon-node][] + * @param {Array} `nodes` Optionally pass a new `nodes` value, to replace the existing `node.nodes` array. + * @api public */ -utils.join = function(a, b, options) { - options = options || {}; - a = utils.arrayify(a); - b = utils.arrayify(b); - - if (!a.length) return b; - if (!b.length) return a; - - var len = a.length; - var idx = -1; - var arr = []; - - while (++idx < len) { - var val = a[idx]; - if (Array.isArray(val)) { - for (var i = 0; i < val.length; i++) { - val[i] = utils.join(val[i], b, options); - } - arr.push(val); - continue; - } - - for (var j = 0; j < b.length; j++) { - var bval = b[j]; - - if (Array.isArray(bval)) { - arr.push(utils.join(val, bval, options)); - } else { - arr.push(val + bval); - } - } +utils.toNoop = function(node, nodes) { + if (nodes) { + node.nodes = nodes; + } else { + delete node.nodes; + node.type = 'text'; + node.val = ''; } - return arr; }; /** - * Split the given string on `,` if not escaped. + * Visit `node` with the given `fn`. The built-in `.visit` method in snapdragon + * automatically calls registered compilers, this allows you to pass a visitor + * function. + * + * ```js + * snapdragon.compiler.set('i', function(node) { + * utils.visit(node, function(childNode) { + * // do stuff with "childNode" + * return childNode; + * }); + * }); + * ``` + * @param {Object} `node` Instance of [snapdragon-node][] + * @param {Function} `fn` + * @return {Object} returns the node after recursively visiting all child nodes. + * @api public */ -utils.split = function(str, options) { - var opts = utils.extend({sep: ','}, options); - if (typeof opts.keepQuotes !== 'boolean') { - opts.keepQuotes = true; - } - if (opts.unescape === false) { - opts.keepEscaping = true; - } - return splitString(str, opts, utils.escapeBrackets(opts)); +utils.visit = function(node, fn) { + assert(utils.isNode(node), 'expected node to be an instance of Node'); + assert(isFunction(fn), 'expected a visitor function'); + fn(node); + return node.nodes ? utils.mapVisit(node, fn) : node; }; /** - * Expand ranges or sets in the given `pattern`. + * Map [visit](#visit) the given `fn` over `node.nodes`. This is called by + * [visit](#visit), use this method if you do not want `fn` to be called on + * the first node. * - * @param {String} `str` + * ```js + * snapdragon.compiler.set('i', function(node) { + * utils.mapVisit(node, function(childNode) { + * // do stuff with "childNode" + * return childNode; + * }); + * }); + * ``` + * @param {Object} `node` Instance of [snapdragon-node][] * @param {Object} `options` - * @return {Object} + * @param {Function} `fn` + * @return {Object} returns the node + * @api public */ -utils.expand = function(str, options) { - var opts = utils.extend({rangeLimit: 10000}, options); - var segs = utils.split(str, opts); - var tok = { segs: segs }; - - if (utils.isQuotedString(str)) { - return tok; - } +utils.mapVisit = function(node, fn) { + assert(utils.isNode(node), 'expected node to be an instance of Node'); + assert(isArray(node.nodes), 'expected node.nodes to be an array'); + assert(isFunction(fn), 'expected a visitor function'); - if (opts.rangeLimit === true) { - opts.rangeLimit = 10000; + for (var i = 0; i < node.nodes.length; i++) { + utils.visit(node.nodes[i], fn); } + return node; +}; - if (segs.length > 1) { - if (opts.optimize === false) { - tok.val = segs[0]; - return tok; - } - - tok.segs = utils.stringifyArray(tok.segs); - } else if (segs.length === 1) { - var arr = str.split('..'); - - if (arr.length === 1) { - tok.val = tok.segs[tok.segs.length - 1] || tok.val || str; - tok.segs = []; - return tok; - } - - if (arr.length === 2 && arr[0] === arr[1]) { - tok.escaped = true; - tok.val = arr[0]; - tok.segs = []; - return tok; - } - - if (arr.length > 1) { - if (opts.optimize !== false) { - opts.optimize = true; - delete opts.expand; - } - - if (opts.optimize !== true) { - var min = Math.min(arr[0], arr[1]); - var max = Math.max(arr[0], arr[1]); - var step = arr[2] || 1; - - if (opts.rangeLimit !== false && ((max - min) / step >= opts.rangeLimit)) { - throw new RangeError('expanded array length exceeds range limit. Use options.rangeLimit to increase or disable the limit.'); - } - } - - arr.push(opts); - tok.segs = utils.fillRange.apply(null, arr); +/** + * Unshift an `*.open` node onto `node.nodes`. + * + * ```js + * var Node = require('snapdragon-node'); + * snapdragon.parser.set('brace', function(node) { + * var match = this.match(/^{/); + * if (match) { + * var parent = new Node({type: 'brace'}); + * utils.addOpen(parent, Node); + * console.log(parent.nodes[0]): + * // { type: 'brace.open', val: '' }; + * + * // push the parent "brace" node onto the stack + * this.push(parent); + * + * // return the parent node, so it's also added to the AST + * return brace; + * } + * }); + * ``` + * @param {Object} `node` Instance of [snapdragon-node][] + * @param {Function} `Node` (required) Node constructor function from [snapdragon-node][]. + * @param {Function} `filter` Optionaly specify a filter function to exclude the node. + * @return {Object} Returns the created opening node. + * @api public + */ - if (!tok.segs.length) { - tok.escaped = true; - tok.val = str; - return tok; - } +utils.addOpen = function(node, Node, val, filter) { + assert(utils.isNode(node), 'expected node to be an instance of Node'); + assert(isFunction(Node), 'expected Node to be a constructor function'); - if (opts.optimize === true) { - tok.segs = utils.stringifyArray(tok.segs); - } + if (typeof val === 'function') { + filter = val; + val = ''; + } - if (tok.segs === '') { - tok.val = str; - } else { - tok.val = tok.segs[0]; - } - return tok; - } + if (typeof filter === 'function' && !filter(node)) return; + var open = new Node({ type: node.type + '.open', val: val}); + var unshift = node.unshift || node.unshiftNode; + if (typeof unshift === 'function') { + unshift.call(node, open); } else { - tok.val = str; + utils.unshiftNode(node, open); } - return tok; + return open; }; /** - * Ensure commas inside brackets and parens are not split. - * @param {Object} `tok` Token from the `split-string` module - * @return {undefined} + * Push a `*.close` node onto `node.nodes`. + * + * ```js + * var Node = require('snapdragon-node'); + * snapdragon.parser.set('brace', function(node) { + * var match = this.match(/^}/); + * if (match) { + * var parent = this.parent(); + * if (parent.type !== 'brace') { + * throw new Error('missing opening: ' + '}'); + * } + * + * utils.addClose(parent, Node); + * console.log(parent.nodes[parent.nodes.length - 1]): + * // { type: 'brace.close', val: '' }; + * + * // no need to return a node, since the parent + * // was already added to the AST + * return; + * } + * }); + * ``` + * @param {Object} `node` Instance of [snapdragon-node][] + * @param {Function} `Node` (required) Node constructor function from [snapdragon-node][]. + * @param {Function} `filter` Optionaly specify a filter function to exclude the node. + * @return {Object} Returns the created closing node. + * @api public */ -utils.escapeBrackets = function(options) { - return function(tok) { - if (tok.escaped && tok.val === 'b') { - tok.val = '\\b'; - return; - } - - if (tok.val !== '(' && tok.val !== '[') return; - var opts = utils.extend({}, options); - var brackets = []; - var parens = []; - var stack = []; - var val = tok.val; - var str = tok.str; - var i = tok.idx - 1; - - while (++i < str.length) { - var ch = str[i]; - - if (ch === '\\') { - val += (opts.keepEscaping === false ? '' : ch) + str[++i]; - continue; - } - - if (ch === '(') { - parens.push(ch); - stack.push(ch); - } - - if (ch === '[') { - brackets.push(ch); - stack.push(ch); - } - - if (ch === ')') { - parens.pop(); - stack.pop(); - if (!stack.length) { - val += ch; - break; - } - } - - if (ch === ']') { - brackets.pop(); - stack.pop(); - if (!stack.length) { - val += ch; - break; - } - } - val += ch; - } - - tok.split = false; - tok.val = val.slice(1); - tok.idx = i; - }; +utils.addClose = function(node, Node, val, filter) { + assert(utils.isNode(node), 'expected node to be an instance of Node'); + assert(isFunction(Node), 'expected Node to be a constructor function'); + + if (typeof val === 'function') { + filter = val; + val = ''; + } + + if (typeof filter === 'function' && !filter(node)) return; + var close = new Node({ type: node.type + '.close', val: val}); + var push = node.push || node.pushNode; + if (typeof push === 'function') { + push.call(node, close); + } else { + utils.pushNode(node, close); + } + return close; }; /** - * Returns true if the given string looks like a regex quantifier - * @return {Boolean} + * Wraps the given `node` with `*.open` and `*.close` nodes. + * + * @param {Object} `node` Instance of [snapdragon-node][] + * @param {Function} `Node` (required) Node constructor function from [snapdragon-node][]. + * @param {Function} `filter` Optionaly specify a filter function to exclude the node. + * @return {Object} Returns the node + * @api public */ -utils.isQuantifier = function(str) { - return /^(?:[0-9]?,[0-9]|[0-9],)$/.test(str); +utils.wrapNodes = function(node, Node, filter) { + assert(utils.isNode(node), 'expected node to be an instance of Node'); + assert(isFunction(Node), 'expected Node to be a constructor function'); + + utils.addOpen(node, Node, filter); + utils.addClose(node, Node, filter); + return node; }; /** - * Cast `val` to an array. - * @param {*} `val` + * Push the given `node` onto `parent.nodes`, and set `parent` as `node.parent. + * + * ```js + * var parent = new Node({type: 'foo'}); + * var node = new Node({type: 'bar'}); + * utils.pushNode(parent, node); + * console.log(parent.nodes[0].type) // 'bar' + * console.log(node.parent.type) // 'foo' + * ``` + * @param {Object} `parent` + * @param {Object} `node` Instance of [snapdragon-node][] + * @return {Object} Returns the child node + * @api public */ -utils.stringifyArray = function(arr) { - return [utils.arrayify(arr).join('|')]; +utils.pushNode = function(parent, node) { + assert(utils.isNode(parent), 'expected parent node to be an instance of Node'); + assert(utils.isNode(node), 'expected node to be an instance of Node'); + + node.define('parent', parent); + parent.nodes = parent.nodes || []; + parent.nodes.push(node); + return node; }; /** - * Cast `val` to an array. - * @param {*} `val` + * Unshift `node` onto `parent.nodes`, and set `parent` as `node.parent. + * + * ```js + * var parent = new Node({type: 'foo'}); + * var node = new Node({type: 'bar'}); + * utils.unshiftNode(parent, node); + * console.log(parent.nodes[0].type) // 'bar' + * console.log(node.parent.type) // 'foo' + * ``` + * @param {Object} `parent` + * @param {Object} `node` Instance of [snapdragon-node][] + * @return {undefined} + * @api public */ -utils.arrayify = function(arr) { - if (typeof arr === 'undefined') { - return []; - } - if (typeof arr === 'string') { - return [arr]; - } - return arr; +utils.unshiftNode = function(parent, node) { + assert(utils.isNode(parent), 'expected parent node to be an instance of Node'); + assert(utils.isNode(node), 'expected node to be an instance of Node'); + + node.define('parent', parent); + parent.nodes = parent.nodes || []; + parent.nodes.unshift(node); }; /** - * Returns true if the given `str` is a non-empty string - * @return {Boolean} + * Pop the last `node` off of `parent.nodes`. The advantage of + * using this method is that it checks for `node.nodes` and works + * with any version of `snapdragon-node`. + * + * ```js + * var parent = new Node({type: 'foo'}); + * utils.pushNode(parent, new Node({type: 'foo'})); + * utils.pushNode(parent, new Node({type: 'bar'})); + * utils.pushNode(parent, new Node({type: 'baz'})); + * console.log(parent.nodes.length); //=> 3 + * utils.popNode(parent); + * console.log(parent.nodes.length); //=> 2 + * ``` + * @param {Object} `parent` + * @param {Object} `node` Instance of [snapdragon-node][] + * @return {Number|Undefined} Returns the length of `node.nodes` or undefined. + * @api public */ -utils.isString = function(str) { - return str != null && typeof str === 'string'; +utils.popNode = function(node) { + assert(utils.isNode(node), 'expected node to be an instance of Node'); + if (typeof node.pop === 'function') { + return node.pop(); + } + return node.nodes && node.nodes.pop(); }; /** - * Get the last element from `array` - * @param {Array} `array` - * @return {*} + * Shift the first `node` off of `parent.nodes`. The advantage of + * using this method is that it checks for `node.nodes` and works + * with any version of `snapdragon-node`. + * + * ```js + * var parent = new Node({type: 'foo'}); + * utils.pushNode(parent, new Node({type: 'foo'})); + * utils.pushNode(parent, new Node({type: 'bar'})); + * utils.pushNode(parent, new Node({type: 'baz'})); + * console.log(parent.nodes.length); //=> 3 + * utils.shiftNode(parent); + * console.log(parent.nodes.length); //=> 2 + * ``` + * @param {Object} `parent` + * @param {Object} `node` Instance of [snapdragon-node][] + * @return {Number|Undefined} Returns the length of `node.nodes` or undefined. + * @api public */ -utils.last = function(arr, n) { - return arr[arr.length - (n || 1)]; -}; - -utils.escapeRegex = function(str) { - return str.replace(/\\?([!^*?()[\]{}+?/])/g, '\\$1'); +utils.shiftNode = function(node) { + assert(utils.isNode(node), 'expected node to be an instance of Node'); + if (typeof node.shift === 'function') { + return node.shift(); + } + return node.nodes && node.nodes.shift(); }; - -/***/ }), -/* 598 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/*! - * split-string +/** + * Remove the specified `node` from `parent.nodes`. * - * Copyright (c) 2015-2017, Jon Schlinkert. - * Released under the MIT License. + * ```js + * var parent = new Node({type: 'abc'}); + * var foo = new Node({type: 'foo'}); + * utils.pushNode(parent, foo); + * utils.pushNode(parent, new Node({type: 'bar'})); + * utils.pushNode(parent, new Node({type: 'baz'})); + * console.log(parent.nodes.length); //=> 3 + * utils.removeNode(parent, foo); + * console.log(parent.nodes.length); //=> 2 + * ``` + * @param {Object} `parent` + * @param {Object} `node` Instance of [snapdragon-node][] + * @return {Object|undefined} Returns the removed node, if successful, or undefined if it does not exist on `parent.nodes`. + * @api public */ +utils.removeNode = function(parent, node) { + assert(utils.isNode(parent), 'expected parent.node to be an instance of Node'); + assert(utils.isNode(node), 'expected node to be an instance of Node'); - -var extend = __webpack_require__(599); - -module.exports = function(str, options, fn) { - if (typeof str !== 'string') { - throw new TypeError('expected a string'); - } - - if (typeof options === 'function') { - fn = options; - options = null; - } - - // allow separator to be defined as a string - if (typeof options === 'string') { - options = { sep: options }; + if (!parent.nodes) { + return null; } - var opts = extend({sep: '.'}, options); - var quotes = opts.quotes || ['"', "'", '`']; - var brackets; - - if (opts.brackets === true) { - brackets = { - '<': '>', - '(': ')', - '[': ']', - '{': '}' - }; - } else if (opts.brackets) { - brackets = opts.brackets; + if (typeof parent.remove === 'function') { + return parent.remove(node); } - var tokens = []; - var stack = []; - var arr = ['']; - var sep = opts.sep; - var len = str.length; - var idx = -1; - var closeIdx; - - function expected() { - if (brackets && stack.length) { - return brackets[stack[stack.length - 1]]; - } + var idx = parent.nodes.indexOf(node); + if (idx !== -1) { + return parent.nodes.splice(idx, 1); } +}; - while (++idx < len) { - var ch = str[idx]; - var next = str[idx + 1]; - var tok = { val: ch, idx: idx, arr: arr, str: str }; - tokens.push(tok); - - if (ch === '\\') { - tok.val = keepEscaping(opts, str, idx) === true ? (ch + next) : next; - tok.escaped = true; - if (typeof fn === 'function') { - fn(tok); - } - arr[arr.length - 1] += tok.val; - idx++; - continue; - } - - if (brackets && brackets[ch]) { - stack.push(ch); - var e = expected(); - var i = idx + 1; - - if (str.indexOf(e, i + 1) !== -1) { - while (stack.length && i < len) { - var s = str[++i]; - if (s === '\\') { - s++; - continue; - } - - if (quotes.indexOf(s) !== -1) { - i = getClosingQuote(str, s, i + 1); - continue; - } - - e = expected(); - if (stack.length && str.indexOf(e, i + 1) === -1) { - break; - } - - if (brackets[s]) { - stack.push(s); - continue; - } +/** + * Returns true if `node.type` matches the given `type`. Throws a + * `TypeError` if `node` is not an instance of `Node`. + * + * ```js + * var Node = require('snapdragon-node'); + * var node = new Node({type: 'foo'}); + * console.log(utils.isType(node, 'foo')); // false + * console.log(utils.isType(node, 'bar')); // true + * ``` + * @param {Object} `node` Instance of [snapdragon-node][] + * @param {String} `type` + * @return {Boolean} + * @api public + */ - if (e === s) { - stack.pop(); - } +utils.isType = function(node, type) { + assert(utils.isNode(node), 'expected node to be an instance of Node'); + switch (typeOf(type)) { + case 'array': + var types = type.slice(); + for (var i = 0; i < types.length; i++) { + if (utils.isType(node, types[i])) { + return true; } } - - closeIdx = i; - if (closeIdx === -1) { - arr[arr.length - 1] += ch; - continue; - } - - ch = str.slice(idx, closeIdx + 1); - tok.val = ch; - tok.idx = idx = closeIdx; - } - - if (quotes.indexOf(ch) !== -1) { - closeIdx = getClosingQuote(str, ch, idx + 1); - if (closeIdx === -1) { - arr[arr.length - 1] += ch; - continue; - } - - if (keepQuotes(ch, opts) === true) { - ch = str.slice(idx, closeIdx + 1); - } else { - ch = str.slice(idx + 1, closeIdx); - } - - tok.val = ch; - tok.idx = idx = closeIdx; + return false; + case 'string': + return node.type === type; + case 'regexp': + return type.test(node.type); + default: { + throw new TypeError('expected "type" to be an array, string or regexp'); } + } +}; - if (typeof fn === 'function') { - fn(tok, tokens); - ch = tok.val; - idx = tok.idx; - } +/** + * Returns true if the given `node` has the given `type` in `node.nodes`. + * Throws a `TypeError` if `node` is not an instance of `Node`. + * + * ```js + * var Node = require('snapdragon-node'); + * var node = new Node({ + * type: 'foo', + * nodes: [ + * new Node({type: 'bar'}), + * new Node({type: 'baz'}) + * ] + * }); + * console.log(utils.hasType(node, 'xyz')); // false + * console.log(utils.hasType(node, 'baz')); // true + * ``` + * @param {Object} `node` Instance of [snapdragon-node][] + * @param {String} `type` + * @return {Boolean} + * @api public + */ - if (tok.val === sep && tok.split !== false) { - arr.push(''); - continue; +utils.hasType = function(node, type) { + assert(utils.isNode(node), 'expected node to be an instance of Node'); + if (!Array.isArray(node.nodes)) return false; + for (var i = 0; i < node.nodes.length; i++) { + if (utils.isType(node.nodes[i], type)) { + return true; } - - arr[arr.length - 1] += tok.val; } - - return arr; + return false; }; -function getClosingQuote(str, ch, i, brackets) { - var idx = str.indexOf(ch, i); - if (str.charAt(idx - 1) === '\\') { - return getClosingQuote(str, ch, idx + 1); - } - return idx; -} - -function keepQuotes(ch, opts) { - if (opts.keepDoubleQuotes === true && ch === '"') return true; - if (opts.keepSingleQuotes === true && ch === "'") return true; - return opts.keepQuotes; -} +/** + * Returns the first node from `node.nodes` of the given `type` + * + * ```js + * var node = new Node({ + * type: 'foo', + * nodes: [ + * new Node({type: 'text', val: 'abc'}), + * new Node({type: 'text', val: 'xyz'}) + * ] + * }); + * + * var textNode = utils.firstOfType(node.nodes, 'text'); + * console.log(textNode.val); + * //=> 'abc' + * ``` + * @param {Array} `nodes` + * @param {String} `type` + * @return {Object|undefined} Returns the first matching node or undefined. + * @api public + */ -function keepEscaping(opts, str, idx) { - if (typeof opts.keepEscaping === 'function') { - return opts.keepEscaping(str, idx); +utils.firstOfType = function(nodes, type) { + for (var i = 0; i < nodes.length; i++) { + var node = nodes[i]; + if (utils.isType(node, type)) { + return node; + } } - return opts.keepEscaping === true || str[idx + 1] === '\\'; -} - - -/***/ }), -/* 599 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - +}; -var isExtendable = __webpack_require__(600); -var assignSymbols = __webpack_require__(589); +/** + * Returns the node at the specified index, or the first node of the + * given `type` from `node.nodes`. + * + * ```js + * var node = new Node({ + * type: 'foo', + * nodes: [ + * new Node({type: 'text', val: 'abc'}), + * new Node({type: 'text', val: 'xyz'}) + * ] + * }); + * + * var nodeOne = utils.findNode(node.nodes, 'text'); + * console.log(nodeOne.val); + * //=> 'abc' + * + * var nodeTwo = utils.findNode(node.nodes, 1); + * console.log(nodeTwo.val); + * //=> 'xyz' + * ``` + * + * @param {Array} `nodes` + * @param {String|Number} `type` Node type or index. + * @return {Object} Returns a node or undefined. + * @api public + */ -module.exports = Object.assign || function(obj/*, objects*/) { - if (obj === null || typeof obj === 'undefined') { - throw new TypeError('Cannot convert undefined or null to object'); - } - if (!isObject(obj)) { - obj = {}; +utils.findNode = function(nodes, type) { + if (!Array.isArray(nodes)) { + return null; } - for (var i = 1; i < arguments.length; i++) { - var val = arguments[i]; - if (isString(val)) { - val = toObject(val); - } - if (isObject(val)) { - assign(obj, val); - assignSymbols(obj, val); - } + if (typeof type === 'number') { + return nodes[type]; } - return obj; + return utils.firstOfType(nodes, type); }; -function assign(a, b) { - for (var key in b) { - if (hasOwn(b, key)) { - a[key] = b[key]; - } - } -} - -function isString(val) { - return (val && typeof val === 'string'); -} - -function toObject(str) { - var obj = {}; - for (var i in str) { - obj[i] = str[i]; - } - return obj; -} +/** + * Returns true if the given node is an "*.open" node. + * + * ```js + * var Node = require('snapdragon-node'); + * var brace = new Node({type: 'brace'}); + * var open = new Node({type: 'brace.open'}); + * var close = new Node({type: 'brace.close'}); + * + * console.log(utils.isOpen(brace)); // false + * console.log(utils.isOpen(open)); // true + * console.log(utils.isOpen(close)); // false + * ``` + * @param {Object} `node` Instance of [snapdragon-node][] + * @return {Boolean} + * @api public + */ -function isObject(val) { - return (val && typeof val === 'object') || isExtendable(val); -} +utils.isOpen = function(node) { + assert(utils.isNode(node), 'expected node to be an instance of Node'); + return node.type.slice(-5) === '.open'; +}; /** - * Returns true if the given `key` is an own property of `obj`. + * Returns true if the given node is a "*.close" node. + * + * ```js + * var Node = require('snapdragon-node'); + * var brace = new Node({type: 'brace'}); + * var open = new Node({type: 'brace.open'}); + * var close = new Node({type: 'brace.close'}); + * + * console.log(utils.isClose(brace)); // false + * console.log(utils.isClose(open)); // false + * console.log(utils.isClose(close)); // true + * ``` + * @param {Object} `node` Instance of [snapdragon-node][] + * @return {Boolean} + * @api public */ -function hasOwn(obj, key) { - return Object.prototype.hasOwnProperty.call(obj, key); -} - -function isEnum(obj, key) { - return Object.prototype.propertyIsEnumerable.call(obj, key); -} - - -/***/ }), -/* 600 */ -/***/ (function(module, exports, __webpack_require__) { +utils.isClose = function(node) { + assert(utils.isNode(node), 'expected node to be an instance of Node'); + return node.type.slice(-6) === '.close'; +}; -"use strict"; -/*! - * is-extendable +/** + * Returns true if `node.nodes` **has** an `.open` node * - * Copyright (c) 2015-2017, Jon Schlinkert. - * Released under the MIT License. + * ```js + * var Node = require('snapdragon-node'); + * var brace = new Node({ + * type: 'brace', + * nodes: [] + * }); + * + * var open = new Node({type: 'brace.open'}); + * console.log(utils.hasOpen(brace)); // false + * + * brace.pushNode(open); + * console.log(utils.hasOpen(brace)); // true + * ``` + * @param {Object} `node` Instance of [snapdragon-node][] + * @return {Boolean} + * @api public */ +utils.hasOpen = function(node) { + assert(utils.isNode(node), 'expected node to be an instance of Node'); + var first = node.first || node.nodes ? node.nodes[0] : null; + if (utils.isNode(first)) { + return first.type === node.type + '.open'; + } + return false; +}; +/** + * Returns true if `node.nodes` **has** a `.close` node + * + * ```js + * var Node = require('snapdragon-node'); + * var brace = new Node({ + * type: 'brace', + * nodes: [] + * }); + * + * var close = new Node({type: 'brace.close'}); + * console.log(utils.hasClose(brace)); // false + * + * brace.pushNode(close); + * console.log(utils.hasClose(brace)); // true + * ``` + * @param {Object} `node` Instance of [snapdragon-node][] + * @return {Boolean} + * @api public + */ -var isPlainObject = __webpack_require__(588); - -module.exports = function isExtendable(val) { - return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); +utils.hasClose = function(node) { + assert(utils.isNode(node), 'expected node to be an instance of Node'); + var last = node.last || node.nodes ? node.nodes[node.nodes.length - 1] : null; + if (utils.isNode(last)) { + return last.type === node.type + '.close'; + } + return false; }; +/** + * Returns true if `node.nodes` has both `.open` and `.close` nodes + * + * ```js + * var Node = require('snapdragon-node'); + * var brace = new Node({ + * type: 'brace', + * nodes: [] + * }); + * + * var open = new Node({type: 'brace.open'}); + * var close = new Node({type: 'brace.close'}); + * console.log(utils.hasOpen(brace)); // false + * console.log(utils.hasClose(brace)); // false + * + * brace.pushNode(open); + * brace.pushNode(close); + * console.log(utils.hasOpen(brace)); // true + * console.log(utils.hasClose(brace)); // true + * ``` + * @param {Object} `node` Instance of [snapdragon-node][] + * @return {Boolean} + * @api public + */ -/***/ }), -/* 601 */ -/***/ (function(module, exports, __webpack_require__) { +utils.hasOpenAndClose = function(node) { + return utils.hasOpen(node) && utils.hasClose(node); +}; -"use strict"; -/*! - * arr-flatten +/** + * Push the given `node` onto the `state.inside` array for the + * given type. This array is used as a specialized "stack" for + * only the given `node.type`. * - * Copyright (c) 2014-2017, Jon Schlinkert. - * Released under the MIT License. + * ```js + * var state = { inside: {}}; + * var node = new Node({type: 'brace'}); + * utils.addType(state, node); + * console.log(state.inside); + * //=> { brace: [{type: 'brace'}] } + * ``` + * @param {Object} `state` The `compiler.state` object or custom state object. + * @param {Object} `node` Instance of [snapdragon-node][] + * @return {Array} Returns the `state.inside` stack for the given type. + * @api public */ +utils.addType = function(state, node) { + assert(utils.isNode(node), 'expected node to be an instance of Node'); + assert(isObject(state), 'expected state to be an object'); + var type = node.parent + ? node.parent.type + : node.type.replace(/\.open$/, ''); -module.exports = function (arr) { - return flat(arr, []); -}; - -function flat(arr, res) { - var i = 0, cur; - var len = arr.length; - for (; i < len; i++) { - cur = arr[i]; - Array.isArray(cur) ? flat(cur, res) : res.push(cur); + if (!state.hasOwnProperty('inside')) { + state.inside = {}; + } + if (!state.inside.hasOwnProperty(type)) { + state.inside[type] = []; } - return res; -} - -/***/ }), -/* 602 */ -/***/ (function(module, exports, __webpack_require__) { + var arr = state.inside[type]; + arr.push(node); + return arr; +}; -"use strict"; -/*! - * fill-range +/** + * Remove the given `node` from the `state.inside` array for the + * given type. This array is used as a specialized "stack" for + * only the given `node.type`. * - * Copyright (c) 2014-2015, 2017, Jon Schlinkert. - * Released under the MIT License. + * ```js + * var state = { inside: {}}; + * var node = new Node({type: 'brace'}); + * utils.addType(state, node); + * console.log(state.inside); + * //=> { brace: [{type: 'brace'}] } + * utils.removeType(state, node); + * //=> { brace: [] } + * ``` + * @param {Object} `state` The `compiler.state` object or custom state object. + * @param {Object} `node` Instance of [snapdragon-node][] + * @return {Array} Returns the `state.inside` stack for the given type. + * @api public */ +utils.removeType = function(state, node) { + assert(utils.isNode(node), 'expected node to be an instance of Node'); + assert(isObject(state), 'expected state to be an object'); + var type = node.parent + ? node.parent.type + : node.type.replace(/\.close$/, ''); -var util = __webpack_require__(113); -var isNumber = __webpack_require__(603); -var extend = __webpack_require__(606); -var repeat = __webpack_require__(608); -var toRegex = __webpack_require__(609); + if (state.inside.hasOwnProperty(type)) { + return state.inside[type].pop(); + } +}; /** - * Return a range of numbers or letters. + * Returns true if `node.val` is an empty string, or `node.nodes` does + * not contain any non-empty text nodes. * - * @param {String} `start` Start of the range - * @param {String} `stop` End of the range - * @param {String} `step` Increment or decrement to use. - * @param {Function} `fn` Custom function to modify each element in the range. - * @return {Array} + * ```js + * var node = new Node({type: 'text'}); + * utils.isEmpty(node); //=> true + * node.val = 'foo'; + * utils.isEmpty(node); //=> false + * ``` + * @param {Object} `node` Instance of [snapdragon-node][] + * @param {Function} `fn` + * @return {Boolean} + * @api public */ -function fillRange(start, stop, step, options) { - if (typeof start === 'undefined') { - return []; - } +utils.isEmpty = function(node, fn) { + assert(utils.isNode(node), 'expected node to be an instance of Node'); - if (typeof stop === 'undefined' || start === stop) { - // special case, for handling negative zero - var isString = typeof start === 'string'; - if (isNumber(start) && !toNumber(start)) { - return [isString ? '0' : 0]; + if (!Array.isArray(node.nodes)) { + if (node.type !== 'text') { + return true; } - return [start]; - } - - if (typeof step !== 'number' && typeof step !== 'string') { - options = step; - step = undefined; - } - - if (typeof options === 'function') { - options = { transform: options }; - } - - var opts = extend({step: step}, options); - if (opts.step && !isValidNumber(opts.step)) { - if (opts.strictRanges === true) { - throw new TypeError('expected options.step to be a number'); + if (typeof fn === 'function') { + return fn(node, node.parent); } - return []; + return !utils.trim(node.val); } - opts.isNumber = isValidNumber(start) && isValidNumber(stop); - if (!opts.isNumber && !isValid(start, stop)) { - if (opts.strictRanges === true) { - throw new RangeError('invalid range arguments: ' + util.inspect([start, stop])); + for (var i = 0; i < node.nodes.length; i++) { + var child = node.nodes[i]; + if (utils.isOpen(child) || utils.isClose(child)) { + continue; + } + if (!utils.isEmpty(child, fn)) { + return false; } - return []; } - opts.isPadded = isPadded(start) || isPadded(stop); - opts.toString = opts.stringify - || typeof opts.step === 'string' - || typeof start === 'string' - || typeof stop === 'string' - || !opts.isNumber; - - if (opts.isPadded) { - opts.maxLength = Math.max(String(start).length, String(stop).length); - } + return true; +}; - // support legacy minimatch/fill-range options - if (typeof opts.optimize === 'boolean') opts.toRegex = opts.optimize; - if (typeof opts.makeRe === 'boolean') opts.toRegex = opts.makeRe; - return expand(start, stop, opts); -} +/** + * Returns true if the `state.inside` stack for the given type exists + * and has one or more nodes on it. + * + * ```js + * var state = { inside: {}}; + * var node = new Node({type: 'brace'}); + * console.log(utils.isInsideType(state, 'brace')); //=> false + * utils.addType(state, node); + * console.log(utils.isInsideType(state, 'brace')); //=> true + * utils.removeType(state, node); + * console.log(utils.isInsideType(state, 'brace')); //=> false + * ``` + * @param {Object} `state` + * @param {String} `type` + * @return {Boolean} + * @api public + */ -function expand(start, stop, options) { - var a = options.isNumber ? toNumber(start) : start.charCodeAt(0); - var b = options.isNumber ? toNumber(stop) : stop.charCodeAt(0); +utils.isInsideType = function(state, type) { + assert(isObject(state), 'expected state to be an object'); + assert(isString(type), 'expected type to be a string'); - var step = Math.abs(toNumber(options.step)) || 1; - if (options.toRegex && step === 1) { - return toRange(a, b, start, stop, options); + if (!state.hasOwnProperty('inside')) { + return false; } - var zero = {greater: [], lesser: []}; - var asc = a < b; - var arr = new Array(Math.round((asc ? b - a : a - b) / step)); - var idx = 0; - - while (asc ? a <= b : a >= b) { - var val = options.isNumber ? a : String.fromCharCode(a); - if (options.toRegex && (val >= 0 || !options.isNumber)) { - zero.greater.push(val); - } else { - zero.lesser.push(Math.abs(val)); - } + if (!state.inside.hasOwnProperty(type)) { + return false; + } - if (options.isPadded) { - val = zeros(val, options); - } + return state.inside[type].length > 0; +}; - if (options.toString) { - val = String(val); - } +/** + * Returns true if `node` is either a child or grand-child of the given `type`, + * or `state.inside[type]` is a non-empty array. + * + * ```js + * var state = { inside: {}}; + * var node = new Node({type: 'brace'}); + * var open = new Node({type: 'brace.open'}); + * console.log(utils.isInside(state, open, 'brace')); //=> false + * utils.pushNode(node, open); + * console.log(utils.isInside(state, open, 'brace')); //=> true + * ``` + * @param {Object} `state` Either the `compiler.state` object, if it exists, or a user-supplied state object. + * @param {Object} `node` Instance of [snapdragon-node][] + * @param {String} `type` The `node.type` to check for. + * @return {Boolean} + * @api public + */ - if (typeof options.transform === 'function') { - arr[idx++] = options.transform(val, a, b, step, idx, arr, options); - } else { - arr[idx++] = val; - } +utils.isInside = function(state, node, type) { + assert(utils.isNode(node), 'expected node to be an instance of Node'); + assert(isObject(state), 'expected state to be an object'); - if (asc) { - a += step; - } else { - a -= step; + if (Array.isArray(type)) { + for (var i = 0; i < type.length; i++) { + if (utils.isInside(state, node, type[i])) { + return true; + } } + return false; } - if (options.toRegex === true) { - return toSequence(arr, zero, options); + var parent = node.parent; + if (typeof type === 'string') { + return (parent && parent.type === type) || utils.isInsideType(state, type); } - return arr; -} -function toRange(a, b, start, stop, options) { - if (options.isPadded) { - return toRegex(start, stop, options); - } + if (typeOf(type) === 'regexp') { + if (parent && parent.type && type.test(parent.type)) { + return true; + } - if (options.isNumber) { - return toRegex(Math.min(a, b), Math.max(a, b), options); + var keys = Object.keys(state.inside); + var len = keys.length; + var idx = -1; + while (++idx < len) { + var key = keys[idx]; + var val = state.inside[key]; + + if (Array.isArray(val) && val.length !== 0 && type.test(key)) { + return true; + } + } } + return false; +}; - var start = String.fromCharCode(Math.min(a, b)); - var stop = String.fromCharCode(Math.max(a, b)); - return '[' + start + '-' + stop + ']'; -} +/** + * Get the last `n` element from the given `array`. Used for getting + * a node from `node.nodes.` + * + * @param {Array} `array` + * @param {Number} `n` + * @return {undefined} + * @api public + */ -function toSequence(arr, zeros, options) { - var greater = '', lesser = ''; - if (zeros.greater.length) { - greater = zeros.greater.join('|'); - } - if (zeros.lesser.length) { - lesser = '-(' + zeros.lesser.join('|') + ')'; - } - var res = greater && lesser - ? greater + '|' + lesser - : greater || lesser; +utils.last = function(arr, n) { + return arr[arr.length - (n || 1)]; +}; - if (options.capture) { - return '(' + res + ')'; - } - return res; -} +/** + * Cast the given `val` to an array. + * + * ```js + * console.log(utils.arrayify('')); + * //=> [] + * console.log(utils.arrayify('foo')); + * //=> ['foo'] + * console.log(utils.arrayify(['foo'])); + * //=> ['foo'] + * ``` + * @param {any} `val` + * @return {Array} + * @api public + */ -function zeros(val, options) { - if (options.isPadded) { - var str = String(val); - var len = str.length; - var dash = ''; - if (str.charAt(0) === '-') { - dash = '-'; - str = str.slice(1); - } - var diff = options.maxLength - len; - var pad = repeat('0', diff); - val = (dash + pad + str); +utils.arrayify = function(val) { + if (typeof val === 'string' && val !== '') { + return [val]; } - if (options.stringify) { - return String(val); + if (!Array.isArray(val)) { + return []; } return val; -} - -function toNumber(val) { - return Number(val) || 0; -} +}; -function isPadded(str) { - return /^-?0\d/.test(str); -} +/** + * Convert the given `val` to a string by joining with `,`. Useful + * for creating a cheerio/CSS/DOM-style selector from a list of strings. + * + * @param {any} `val` + * @return {Array} + * @api public + */ -function isValid(min, max) { - return (isValidNumber(min) || isValidLetter(min)) - && (isValidNumber(max) || isValidLetter(max)); -} +utils.stringify = function(val) { + return utils.arrayify(val).join(','); +}; -function isValidLetter(ch) { - return typeof ch === 'string' && ch.length === 1 && /^\w+$/.test(ch); -} +/** + * Ensure that the given value is a string and call `.trim()` on it, + * or return an empty string. + * + * @param {String} `str` + * @return {String} + * @api public + */ -function isValidNumber(n) { - return isNumber(n) && !/\./.test(n); -} +utils.trim = function(str) { + return typeof str === 'string' ? str.trim() : ''; +}; /** - * Expose `fillRange` - * @type {Function} + * Return true if val is an object */ -module.exports = fillRange; +function isObject(val) { + return typeOf(val) === 'object'; +} +/** + * Return true if val is a string + */ -/***/ }), -/* 603 */ -/***/ (function(module, exports, __webpack_require__) { +function isString(val) { + return typeof val === 'string'; +} -"use strict"; -/*! - * is-number - * - * Copyright (c) 2014-2015, Jon Schlinkert. - * Licensed under the MIT License. +/** + * Return true if val is a function */ +function isFunction(val) { + return typeof val === 'function'; +} +/** + * Return true if val is an array + */ -var typeOf = __webpack_require__(604); +function isArray(val) { + return Array.isArray(val); +} -module.exports = function isNumber(num) { - var type = typeOf(num); +/** + * Shim to ensure the `.append` methods work with any version of snapdragon + */ - if (type === 'string') { - if (!num.trim()) return false; - } else if (type !== 'number') { - return false; +function append(compiler, val, node) { + if (typeof compiler.append !== 'function') { + return compiler.emit(val, node); } + return compiler.append(val, node); +} - return (num - num + 1) >= 0; -}; +/** + * Simplified assertion. Throws an error is `val` is falsey. + */ + +function assert(val, message) { + if (!val) throw new Error(message); +} /***/ }), -/* 604 */ +/* 621 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(605); +var isBuffer = __webpack_require__(611); var toString = Object.prototype.toString; /** @@ -66567,1368 +71214,1258 @@ module.exports = function kindOf(val) { /***/ }), -/* 605 */ -/***/ (function(module, exports) { - -/*! - * Determine if an object is a Buffer - * - * @author Feross Aboukhadijeh - * @license MIT - */ - -// The _isBuffer check is for Safari 5-7 support, because it's missing -// Object.prototype.constructor. Remove this eventually -module.exports = function (obj) { - return obj != null && (isBuffer(obj) || isSlowBuffer(obj) || !!obj._isBuffer) -} - -function isBuffer (obj) { - return !!obj.constructor && typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj) -} - -// For Node v0.10 support. Remove this eventually. -function isSlowBuffer (obj) { - return typeof obj.readFloatLE === 'function' && typeof obj.slice === 'function' && isBuffer(obj.slice(0, 0)) -} - - -/***/ }), -/* 606 */ +/* 622 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(607); - -module.exports = function extend(o/*, objects*/) { - if (!isObject(o)) { o = {}; } - - var len = arguments.length; - for (var i = 1; i < len; i++) { - var obj = arguments[i]; - - if (isObject(obj)) { - assign(o, obj); - } - } - return o; -}; - -function assign(a, b) { - for (var key in b) { - if (hasOwn(b, key)) { - a[key] = b[key]; - } - } -} +var extend = __webpack_require__(600); +var Snapdragon = __webpack_require__(623); +var compilers = __webpack_require__(602); +var parsers = __webpack_require__(617); +var utils = __webpack_require__(603); /** - * Returns true if the given `key` is an own property of `obj`. + * Customize Snapdragon parser and renderer */ -function hasOwn(obj, key) { - return Object.prototype.hasOwnProperty.call(obj, key); +function Braces(options) { + this.options = extend({}, options); } - -/***/ }), -/* 607 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/*! - * is-extendable - * - * Copyright (c) 2015, Jon Schlinkert. - * Licensed under the MIT License. - */ - - - -module.exports = function isExtendable(val) { - return typeof val !== 'undefined' && val !== null - && (typeof val === 'object' || typeof val === 'function'); -}; - - -/***/ }), -/* 608 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/*! - * repeat-string - * - * Copyright (c) 2014-2015, Jon Schlinkert. - * Licensed under the MIT License. - */ - - - -/** - * Results cache - */ - -var res = ''; -var cache; - /** - * Expose `repeat` + * Initialize braces */ -module.exports = repeat; - -/** - * Repeat the given `string` the specified `number` - * of times. - * - * **Example:** - * - * ```js - * var repeat = require('repeat-string'); - * repeat('A', 5); - * //=> AAAAA - * ``` - * - * @param {String} `string` The string to repeat - * @param {Number} `number` The number of times to repeat the string - * @return {String} Repeated string - * @api public - */ +Braces.prototype.init = function(options) { + if (this.isInitialized) return; + this.isInitialized = true; + var opts = utils.createOptions({}, this.options, options); + this.snapdragon = this.options.snapdragon || new Snapdragon(opts); + this.compiler = this.snapdragon.compiler; + this.parser = this.snapdragon.parser; -function repeat(str, num) { - if (typeof str !== 'string') { - throw new TypeError('expected a string'); - } + compilers(this.snapdragon, opts); + parsers(this.snapdragon, opts); - // cover common, quick use cases - if (num === 1) return str; - if (num === 2) return str + str; + /** + * Call Snapdragon `.parse` method. When AST is returned, we check to + * see if any unclosed braces are left on the stack and, if so, we iterate + * over the stack and correct the AST so that compilers are called in the correct + * order and unbalance braces are properly escaped. + */ - var max = str.length * num; - if (cache !== str || typeof cache === 'undefined') { - cache = str; - res = ''; - } else if (res.length >= max) { - return res.substr(0, max); - } + utils.define(this.snapdragon, 'parse', function(pattern, options) { + var parsed = Snapdragon.prototype.parse.apply(this, arguments); + this.parser.ast.input = pattern; - while (max > res.length && num > 1) { - if (num & 1) { - res += str; + var stack = this.parser.stack; + while (stack.length) { + addParent({type: 'brace.close', val: ''}, stack.pop()); } - num >>= 1; - str += str; - } - - res += str; - res = res.substr(0, max); - return res; -} - - -/***/ }), -/* 609 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/*! - * to-regex-range - * - * Copyright (c) 2015, 2017, Jon Schlinkert. - * Released under the MIT License. - */ - - - -var repeat = __webpack_require__(608); -var isNumber = __webpack_require__(603); -var cache = {}; - -function toRegexRange(min, max, options) { - if (isNumber(min) === false) { - throw new RangeError('toRegexRange: first argument is invalid.'); - } - - if (typeof max === 'undefined' || min === max) { - return String(min); - } - - if (isNumber(max) === false) { - throw new RangeError('toRegexRange: second argument is invalid.'); - } - - options = options || {}; - var relax = String(options.relaxZeros); - var shorthand = String(options.shorthand); - var capture = String(options.capture); - var key = min + ':' + max + '=' + relax + shorthand + capture; - if (cache.hasOwnProperty(key)) { - return cache[key].result; - } - - var a = Math.min(min, max); - var b = Math.max(min, max); - - if (Math.abs(a - b) === 1) { - var result = min + '|' + max; - if (options.capture) { - return '(' + result + ')'; + function addParent(node, parent) { + utils.define(node, 'parent', parent); + parent.nodes.push(node); } - return result; - } - - var isPadded = padding(min) || padding(max); - var positives = []; - var negatives = []; - - var tok = {min: min, max: max, a: a, b: b}; - if (isPadded) { - tok.isPadded = isPadded; - tok.maxLen = String(tok.max).length; - } - - if (a < 0) { - var newMin = b < 0 ? Math.abs(b) : 1; - var newMax = Math.abs(a); - negatives = splitToPatterns(newMin, newMax, tok, options); - a = tok.a = 0; - } - - if (b >= 0) { - positives = splitToPatterns(a, b, tok, options); - } - - tok.negatives = negatives; - tok.positives = positives; - tok.result = siftPatterns(negatives, positives, options); - - if (options.capture && (positives.length + negatives.length) > 1) { - tok.result = '(' + tok.result + ')'; - } - - cache[key] = tok; - return tok.result; -} - -function siftPatterns(neg, pos, options) { - var onlyNegative = filterPatterns(neg, pos, '-', false, options) || []; - var onlyPositive = filterPatterns(pos, neg, '', false, options) || []; - var intersected = filterPatterns(neg, pos, '-?', true, options) || []; - var subpatterns = onlyNegative.concat(intersected).concat(onlyPositive); - return subpatterns.join('|'); -} - -function splitToRanges(min, max) { - min = Number(min); - max = Number(max); - - var nines = 1; - var stops = [max]; - var stop = +countNines(min, nines); - - while (min <= stop && stop <= max) { - stops = push(stops, stop); - nines += 1; - stop = +countNines(min, nines); - } - - var zeros = 1; - stop = countZeros(max + 1, zeros) - 1; - - while (min < stop && stop <= max) { - stops = push(stops, stop); - zeros += 1; - stop = countZeros(max + 1, zeros) - 1; - } - stops.sort(compare); - return stops; -} + // add non-enumerable parser reference + utils.define(parsed, 'parser', this.parser); + return parsed; + }); +}; /** - * Convert a range to a regex pattern - * @param {Number} `start` - * @param {Number} `stop` - * @return {String} + * Decorate `.parse` method */ -function rangeToPattern(start, stop, options) { - if (start === stop) { - return {pattern: String(start), digits: []}; - } - - var zipped = zip(String(start), String(stop)); - var len = zipped.length, i = -1; - - var pattern = ''; - var digits = 0; - - while (++i < len) { - var numbers = zipped[i]; - var startDigit = numbers[0]; - var stopDigit = numbers[1]; - - if (startDigit === stopDigit) { - pattern += startDigit; - - } else if (startDigit !== '0' || stopDigit !== '9') { - pattern += toCharacterClass(startDigit, stopDigit); - - } else { - digits += 1; - } - } - - if (digits) { - pattern += options.shorthand ? '\\d' : '[0-9]'; - } - - return { pattern: pattern, digits: [digits] }; -} - -function splitToPatterns(min, max, tok, options) { - var ranges = splitToRanges(min, max); - var len = ranges.length; - var idx = -1; - - var tokens = []; - var start = min; - var prev; - - while (++idx < len) { - var range = ranges[idx]; - var obj = rangeToPattern(start, range, options); - var zeros = ''; - - if (!tok.isPadded && prev && prev.pattern === obj.pattern) { - if (prev.digits.length > 1) { - prev.digits.pop(); - } - prev.digits.push(obj.digits[0]); - prev.string = prev.pattern + toQuantifier(prev.digits); - start = range + 1; - continue; - } - - if (tok.isPadded) { - zeros = padZeros(range, tok); - } - - obj.string = zeros + obj.pattern + toQuantifier(obj.digits); - tokens.push(obj); - start = range + 1; - prev = obj; - } - - return tokens; -} - -function filterPatterns(arr, comparison, prefix, intersection, options) { - var res = []; - - for (var i = 0; i < arr.length; i++) { - var tok = arr[i]; - var ele = tok.string; - - if (options.relaxZeros !== false) { - if (prefix === '-' && ele.charAt(0) === '0') { - if (ele.charAt(1) === '{') { - ele = '0*' + ele.replace(/^0\{\d+\}/, ''); - } else { - ele = '0*' + ele.slice(1); - } - } - } - - if (!intersection && !contains(comparison, 'string', ele)) { - res.push(prefix + ele); - } - - if (intersection && contains(comparison, 'string', ele)) { - res.push(prefix + ele); - } - } - return res; -} +Braces.prototype.parse = function(ast, options) { + if (ast && typeof ast === 'object' && ast.nodes) return ast; + this.init(options); + return this.snapdragon.parse(ast, options); +}; /** - * Zip strings (`for in` can be used on string characters) + * Decorate `.compile` method */ -function zip(a, b) { - var arr = []; - for (var ch in a) arr.push([a[ch], b[ch]]); - return arr; -} - -function compare(a, b) { - return a > b ? 1 : b > a ? -1 : 0; -} - -function push(arr, ele) { - if (arr.indexOf(ele) === -1) arr.push(ele); - return arr; -} - -function contains(arr, key, val) { - for (var i = 0; i < arr.length; i++) { - if (arr[i][key] === val) { - return true; - } - } - return false; -} - -function countNines(min, len) { - return String(min).slice(0, -len) + repeat('9', len); -} - -function countZeros(integer, zeros) { - return integer - (integer % Math.pow(10, zeros)); -} - -function toQuantifier(digits) { - var start = digits[0]; - var stop = digits[1] ? (',' + digits[1]) : ''; - if (!stop && (!start || start === 1)) { - return ''; +Braces.prototype.compile = function(ast, options) { + if (typeof ast === 'string') { + ast = this.parse(ast, options); + } else { + this.init(options); } - return '{' + start + stop + '}'; -} + return this.snapdragon.compile(ast, options); +}; -function toCharacterClass(a, b) { - return '[' + a + ((b - a === 1) ? '' : '-') + b + ']'; -} +/** + * Expand + */ -function padding(str) { - return /^-?(0+)\d/.exec(str); -} +Braces.prototype.expand = function(pattern) { + var ast = this.parse(pattern, {expand: true}); + return this.compile(ast, {expand: true}); +}; -function padZeros(val, tok) { - if (tok.isPadded) { - var diff = Math.abs(tok.maxLen - String(val).length); - switch (diff) { - case 0: - return ''; - case 1: - return '0'; - default: { - return '0{' + diff + '}'; - } - } - } - return val; -} +/** + * Optimize + */ + +Braces.prototype.optimize = function(pattern) { + var ast = this.parse(pattern, {optimize: true}); + return this.compile(ast, {optimize: true}); +}; /** - * Expose `toRegexRange` + * Expose `Braces` */ -module.exports = toRegexRange; +module.exports = Braces; /***/ }), -/* 610 */ +/* 623 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -/*! - * repeat-element + + +var Base = __webpack_require__(624); +var define = __webpack_require__(654); +var Compiler = __webpack_require__(665); +var Parser = __webpack_require__(688); +var utils = __webpack_require__(668); +var regexCache = {}; +var cache = {}; + +/** + * Create a new instance of `Snapdragon` with the given `options`. * - * Copyright (c) 2015 Jon Schlinkert. - * Licensed under the MIT license. + * ```js + * var snapdragon = new Snapdragon(); + * ``` + * + * @param {Object} `options` + * @api public */ +function Snapdragon(options) { + Base.call(this, null, options); + this.options = utils.extend({source: 'string'}, this.options); + this.compiler = new Compiler(this.options); + this.parser = new Parser(this.options); + Object.defineProperty(this, 'compilers', { + get: function() { + return this.compiler.compilers; + } + }); -module.exports = function repeat(ele, num) { - var arr = new Array(num); + Object.defineProperty(this, 'parsers', { + get: function() { + return this.parser.parsers; + } + }); - for (var i = 0; i < num; i++) { - arr[i] = ele; - } + Object.defineProperty(this, 'regex', { + get: function() { + return this.parser.regex; + } + }); +} - return arr; -}; +/** + * Inherit Base + */ +Base.extend(Snapdragon); -/***/ }), -/* 611 */ -/***/ (function(module, exports, __webpack_require__) { +/** + * Add a parser to `snapdragon.parsers` for capturing the given `type` using + * the specified regex or parser function. A function is useful if you need + * to customize how the token is created and/or have access to the parser + * instance to check options, etc. + * + * ```js + * snapdragon + * .capture('slash', /^\//) + * .capture('dot', function() { + * var pos = this.position(); + * var m = this.match(/^\./); + * if (!m) return; + * return pos({ + * type: 'dot', + * val: m[0] + * }); + * }); + * ``` + * @param {String} `type` + * @param {RegExp|Function} `regex` + * @return {Object} Returns the parser instance for chaining + * @api public + */ -"use strict"; +Snapdragon.prototype.capture = function() { + return this.parser.capture.apply(this.parser, arguments); +}; +/** + * Register a plugin `fn`. + * + * ```js + * var snapdragon = new Snapdgragon([options]); + * snapdragon.use(function() { + * console.log(this); //<= snapdragon instance + * console.log(this.parser); //<= parser instance + * console.log(this.compiler); //<= compiler instance + * }); + * ``` + * @param {Object} `fn` + * @api public + */ -var Node = __webpack_require__(612); -var utils = __webpack_require__(597); +Snapdragon.prototype.use = function(fn) { + fn.call(this, this); + return this; +}; /** - * Braces parsers + * Parse the given `str`. + * + * ```js + * var snapdragon = new Snapdgragon([options]); + * // register parsers + * snapdragon.parser.use(function() {}); + * + * // parse + * var ast = snapdragon.parse('foo/bar'); + * console.log(ast); + * ``` + * @param {String} `str` + * @param {Object} `options` Set `options.sourcemap` to true to enable source maps. + * @return {Object} Returns an AST. + * @api public */ -module.exports = function(braces, options) { - braces.parser - .set('bos', function() { - if (!this.parsed) { - this.ast = this.nodes[0] = new Node(this.ast); - } - }) +Snapdragon.prototype.parse = function(str, options) { + this.options = utils.extend({}, this.options, options); + var parsed = this.parser.parse(str, this.options); - /** - * Character parsers - */ + // add non-enumerable parser reference + define(parsed, 'parser', this.parser); + return parsed; +}; - .set('escape', function() { - var pos = this.position(); - var m = this.match(/^(?:\\(.)|\$\{)/); - if (!m) return; +/** + * Compile the given `AST`. + * + * ```js + * var snapdragon = new Snapdgragon([options]); + * // register plugins + * snapdragon.use(function() {}); + * // register parser plugins + * snapdragon.parser.use(function() {}); + * // register compiler plugins + * snapdragon.compiler.use(function() {}); + * + * // parse + * var ast = snapdragon.parse('foo/bar'); + * + * // compile + * var res = snapdragon.compile(ast); + * console.log(res.output); + * ``` + * @param {Object} `ast` + * @param {Object} `options` + * @return {Object} Returns an object with an `output` property with the rendered string. + * @api public + */ - var prev = this.prev(); - var last = utils.last(prev.nodes); +Snapdragon.prototype.compile = function(ast, options) { + this.options = utils.extend({}, this.options, options); + var compiled = this.compiler.compile(ast, this.options); - var node = pos(new Node({ - type: 'text', - multiplier: 1, - val: m[0] - })); + // add non-enumerable compiler reference + define(compiled, 'compiler', this.compiler); + return compiled; +}; - if (node.val === '\\\\') { - return node; - } +/** + * Expose `Snapdragon` + */ - if (node.val === '${') { - var str = this.input; - var idx = -1; - var ch; +module.exports = Snapdragon; - while ((ch = str[++idx])) { - this.consume(1); - node.val += ch; - if (ch === '\\') { - node.val += str[++idx]; - continue; - } - if (ch === '}') { - break; - } - } - } +/** + * Expose `Parser` and `Compiler` + */ - if (this.options.unescape !== false) { - node.val = node.val.replace(/\\([{}])/g, '$1'); - } +module.exports.Compiler = Compiler; +module.exports.Parser = Parser; - if (last.val === '"' && this.input.charAt(0) === '"') { - last.val = node.val; - this.consume(1); - return; - } - return concatNodes.call(this, pos, node, prev, options); - }) +/***/ }), +/* 624 */ +/***/ (function(module, exports, __webpack_require__) { - /** - * Brackets: "[...]" (basic, this is overridden by - * other parsers in more advanced implementations) - */ +"use strict"; - .set('bracket', function() { - var isInside = this.isInside('brace'); - var pos = this.position(); - var m = this.match(/^(?:\[([!^]?)([^\]]{2,}|\]-)(\]|[^*+?]+)|\[)/); - if (!m) return; - var prev = this.prev(); - var val = m[0]; - var negated = m[1] ? '^' : ''; - var inner = m[2] || ''; - var close = m[3] || ''; +var util = __webpack_require__(113); +var define = __webpack_require__(625); +var CacheBase = __webpack_require__(626); +var Emitter = __webpack_require__(627); +var isObject = __webpack_require__(587); +var merge = __webpack_require__(648); +var pascal = __webpack_require__(651); +var cu = __webpack_require__(652); - if (isInside && prev.type === 'brace') { - prev.text = prev.text || ''; - prev.text += val; - } +/** + * Optionally define a custom `cache` namespace to use. + */ - var esc = this.input.slice(0, 2); - if (inner === '' && esc === '\\]') { - inner += esc; - this.consume(2); +function namespace(name) { + var Cache = name ? CacheBase.namespace(name) : CacheBase; + var fns = []; - var str = this.input; - var idx = -1; - var ch; + /** + * Create an instance of `Base` with the given `config` and `options`. + * + * ```js + * // initialize with `config` and `options` + * var app = new Base({isApp: true}, {abc: true}); + * app.set('foo', 'bar'); + * + * // values defined with the given `config` object will be on the root of the instance + * console.log(app.baz); //=> undefined + * console.log(app.foo); //=> 'bar' + * // or use `.get` + * console.log(app.get('isApp')); //=> true + * console.log(app.get('foo')); //=> 'bar' + * + * // values defined with the given `options` object will be on `app.options + * console.log(app.options.abc); //=> true + * ``` + * + * @param {Object} `config` If supplied, this object is passed to [cache-base][] to merge onto the the instance upon instantiation. + * @param {Object} `options` If supplied, this object is used to initialize the `base.options` object. + * @api public + */ - while ((ch = str[++idx])) { - this.consume(1); - if (ch === ']') { - close = ch; - break; - } - inner += ch; - } - } + function Base(config, options) { + if (!(this instanceof Base)) { + return new Base(config, options); + } + Cache.call(this, config); + this.is('base'); + this.initBase(config, options); + } - return pos(new Node({ - type: 'bracket', - val: val, - escaped: close !== ']', - negated: negated, - inner: inner, - close: close - })); - }) + /** + * Inherit cache-base + */ - /** - * Empty braces (we capture these early to - * speed up processing in the compiler) - */ + util.inherits(Base, Cache); - .set('multiplier', function() { - var isInside = this.isInside('brace'); - var pos = this.position(); - var m = this.match(/^\{((?:,|\{,+\})+)\}/); - if (!m) return; + /** + * Add static emitter methods + */ - this.multiplier = true; - var prev = this.prev(); - var val = m[0]; + Emitter(Base); - if (isInside && prev.type === 'brace') { - prev.text = prev.text || ''; - prev.text += val; - } + /** + * Initialize `Base` defaults with the given `config` object + */ - var node = pos(new Node({ - type: 'text', - multiplier: 1, - match: m, - val: val - })); + Base.prototype.initBase = function(config, options) { + this.options = merge({}, this.options, options); + this.cache = this.cache || {}; + this.define('registered', {}); + if (name) this[name] = {}; - return concatNodes.call(this, pos, node, prev, options); - }) + // make `app._callbacks` non-enumerable + this.define('_callbacks', this._callbacks); + if (isObject(config)) { + this.visit('set', config); + } + Base.run(this, 'use', fns); + }; - /** - * Open - */ + /** + * Set the given `name` on `app._name` and `app.is*` properties. Used for doing + * lookups in plugins. + * + * ```js + * app.is('foo'); + * console.log(app._name); + * //=> 'foo' + * console.log(app.isFoo); + * //=> true + * app.is('bar'); + * console.log(app.isFoo); + * //=> true + * console.log(app.isBar); + * //=> true + * console.log(app._name); + * //=> 'bar' + * ``` + * @name .is + * @param {String} `name` + * @return {Boolean} + * @api public + */ - .set('brace.open', function() { - var pos = this.position(); - var m = this.match(/^\{(?!(?:[^\\}]?|,+)\})/); - if (!m) return; + Base.prototype.is = function(name) { + if (typeof name !== 'string') { + throw new TypeError('expected name to be a string'); + } + this.define('is' + pascal(name), true); + this.define('_name', name); + this.define('_appname', name); + return this; + }; - var prev = this.prev(); - var last = utils.last(prev.nodes); + /** + * Returns true if a plugin has already been registered on an instance. + * + * Plugin implementors are encouraged to use this first thing in a plugin + * to prevent the plugin from being called more than once on the same + * instance. + * + * ```js + * var base = new Base(); + * base.use(function(app) { + * if (app.isRegistered('myPlugin')) return; + * // do stuff to `app` + * }); + * + * // to also record the plugin as being registered + * base.use(function(app) { + * if (app.isRegistered('myPlugin', true)) return; + * // do stuff to `app` + * }); + * ``` + * @name .isRegistered + * @emits `plugin` Emits the name of the plugin being registered. Useful for unit tests, to ensure plugins are only registered once. + * @param {String} `name` The plugin name. + * @param {Boolean} `register` If the plugin if not already registered, to record it as being registered pass `true` as the second argument. + * @return {Boolean} Returns true if a plugin is already registered. + * @api public + */ - // if the last parsed character was an extglob character - // we need to _not optimize_ the brace pattern because - // it might be mistaken for an extglob by a downstream parser - if (last && last.val && isExtglobChar(last.val.slice(-1))) { - last.optimize = false; - } + Base.prototype.isRegistered = function(name, register) { + if (this.registered.hasOwnProperty(name)) { + return true; + } + if (register !== false) { + this.registered[name] = true; + this.emit('plugin', name); + } + return false; + }; - var open = pos(new Node({ - type: 'brace.open', - val: m[0] - })); + /** + * Define a plugin function to be called immediately upon init. Plugins are chainable + * and expose the following arguments to the plugin function: + * + * - `app`: the current instance of `Base` + * - `base`: the [first ancestor instance](#base) of `Base` + * + * ```js + * var app = new Base() + * .use(foo) + * .use(bar) + * .use(baz) + * ``` + * @name .use + * @param {Function} `fn` plugin function to call + * @return {Object} Returns the item instance for chaining. + * @api public + */ - var node = pos(new Node({ - type: 'brace', - nodes: [] - })); + Base.prototype.use = function(fn) { + fn.call(this, this); + return this; + }; + + /** + * The `.define` method is used for adding non-enumerable property on the instance. + * Dot-notation is **not supported** with `define`. + * + * ```js + * // arbitrary `render` function using lodash `template` + * app.define('render', function(str, locals) { + * return _.template(str)(locals); + * }); + * ``` + * @name .define + * @param {String} `key` The name of the property to define. + * @param {any} `value` + * @return {Object} Returns the instance for chaining. + * @api public + */ - node.push(open); - prev.push(node); - this.push('brace', node); - }) + Base.prototype.define = function(key, val) { + if (isObject(key)) { + return this.visit('define', key); + } + define(this, key, val); + return this; + }; - /** - * Close - */ + /** + * Mix property `key` onto the Base prototype. If base is inherited using + * `Base.extend` this method will be overridden by a new `mixin` method that will + * only add properties to the prototype of the inheriting application. + * + * ```js + * app.mixin('foo', function() { + * // do stuff + * }); + * ``` + * @name .mixin + * @param {String} `key` + * @param {Object|Array} `val` + * @return {Object} Returns the `base` instance for chaining. + * @api public + */ - .set('brace.close', function() { - var pos = this.position(); - var m = this.match(/^\}/); - if (!m || !m[0]) return; + Base.prototype.mixin = function(key, val) { + Base.prototype[key] = val; + return this; + }; - var brace = this.pop('brace'); - var node = pos(new Node({ - type: 'brace.close', - val: m[0] - })); + /** + * Non-enumberable mixin array, used by the static [Base.mixin]() method. + */ - if (!this.isType(brace, 'brace')) { - if (this.options.strict) { - throw new Error('missing opening "{"'); - } - node.type = 'text'; - node.multiplier = 0; - node.escaped = true; - return node; - } + Base.prototype.mixins = Base.prototype.mixins || []; - var prev = this.prev(); - var last = utils.last(prev.nodes); - if (last.text) { - var lastNode = utils.last(last.nodes); - if (lastNode.val === ')' && /[!@*?+]\(/.test(last.text)) { - var open = last.nodes[0]; - var text = last.nodes[1]; - if (open.type === 'brace.open' && text && text.type === 'text') { - text.optimize = false; - } - } - } + /** + * Getter/setter used when creating nested instances of `Base`, for storing a reference + * to the first ancestor instance. This works by setting an instance of `Base` on the `parent` + * property of a "child" instance. The `base` property defaults to the current instance if + * no `parent` property is defined. + * + * ```js + * // create an instance of `Base`, this is our first ("base") instance + * var first = new Base(); + * first.foo = 'bar'; // arbitrary property, to make it easier to see what's happening later + * + * // create another instance + * var second = new Base(); + * // create a reference to the first instance (`first`) + * second.parent = first; + * + * // create another instance + * var third = new Base(); + * // create a reference to the previous instance (`second`) + * // repeat this pattern every time a "child" instance is created + * third.parent = second; + * + * // we can always access the first instance using the `base` property + * console.log(first.base.foo); + * //=> 'bar' + * console.log(second.base.foo); + * //=> 'bar' + * console.log(third.base.foo); + * //=> 'bar' + * // and now you know how to get to third base ;) + * ``` + * @name .base + * @api public + */ - if (brace.nodes.length > 2) { - var first = brace.nodes[1]; - if (first.type === 'text' && first.val === ',') { - brace.nodes.splice(1, 1); - brace.nodes.push(first); - } - } + Object.defineProperty(Base.prototype, 'base', { + configurable: true, + get: function() { + return this.parent ? this.parent.base : this; + } + }); - brace.push(node); - }) + /** + * Static method for adding global plugin functions that will + * be added to an instance when created. + * + * ```js + * Base.use(function(app) { + * app.foo = 'bar'; + * }); + * var app = new Base(); + * console.log(app.foo); + * //=> 'bar' + * ``` + * @name #use + * @param {Function} `fn` Plugin function to use on each instance. + * @return {Object} Returns the `Base` constructor for chaining + * @api public + */ - /** - * Capture boundary characters - */ + define(Base, 'use', function(fn) { + fns.push(fn); + return Base; + }); - .set('boundary', function() { - var pos = this.position(); - var m = this.match(/^[$^](?!\{)/); - if (!m) return; - return pos(new Node({ - type: 'text', - val: m[0] - })); - }) + /** + * Run an array of functions by passing each function + * to a method on the given object specified by the given property. + * + * @param {Object} `obj` Object containing method to use. + * @param {String} `prop` Name of the method on the object to use. + * @param {Array} `arr` Array of functions to pass to the method. + */ - /** - * One or zero, non-comma characters wrapped in braces - */ + define(Base, 'run', function(obj, prop, arr) { + var len = arr.length, i = 0; + while (len--) { + obj[prop](arr[i++]); + } + return Base; + }); - .set('nobrace', function() { - var isInside = this.isInside('brace'); - var pos = this.position(); - var m = this.match(/^\{[^,]?\}/); - if (!m) return; + /** + * Static method for inheriting the prototype and static methods of the `Base` class. + * This method greatly simplifies the process of creating inheritance-based applications. + * See [static-extend][] for more details. + * + * ```js + * var extend = cu.extend(Parent); + * Parent.extend(Child); + * + * // optional methods + * Parent.extend(Child, { + * foo: function() {}, + * bar: function() {} + * }); + * ``` + * @name #extend + * @param {Function} `Ctor` constructor to extend + * @param {Object} `methods` Optional prototype properties to mix in. + * @return {Object} Returns the `Base` constructor for chaining + * @api public + */ - var prev = this.prev(); - var val = m[0]; + define(Base, 'extend', cu.extend(Base, function(Ctor, Parent) { + Ctor.prototype.mixins = Ctor.prototype.mixins || []; - if (isInside && prev.type === 'brace') { - prev.text = prev.text || ''; - prev.text += val; + define(Ctor, 'mixin', function(fn) { + var mixin = fn(Ctor.prototype, Ctor); + if (typeof mixin === 'function') { + Ctor.prototype.mixins.push(mixin); } + return Ctor; + }); - return pos(new Node({ - type: 'text', - multiplier: 0, - val: val - })); - }) + define(Ctor, 'mixins', function(Child) { + Base.run(Child, 'mixin', Ctor.prototype.mixins); + return Ctor; + }); - /** - * Text - */ + Ctor.prototype.mixin = function(key, value) { + Ctor.prototype[key] = value; + return this; + }; + return Base; + })); - .set('text', function() { - var isInside = this.isInside('brace'); - var pos = this.position(); - var m = this.match(/^((?!\\)[^${}[\]])+/); - if (!m) return; + /** + * Used for adding methods to the `Base` prototype, and/or to the prototype of child instances. + * When a mixin function returns a function, the returned function is pushed onto the `.mixins` + * array, making it available to be used on inheriting classes whenever `Base.mixins()` is + * called (e.g. `Base.mixins(Child)`). + * + * ```js + * Base.mixin(function(proto) { + * proto.foo = function(msg) { + * return 'foo ' + msg; + * }; + * }); + * ``` + * @name #mixin + * @param {Function} `fn` Function to call + * @return {Object} Returns the `Base` constructor for chaining + * @api public + */ - var prev = this.prev(); - var val = m[0]; + define(Base, 'mixin', function(fn) { + var mixin = fn(Base.prototype, Base); + if (typeof mixin === 'function') { + Base.prototype.mixins.push(mixin); + } + return Base; + }); - if (isInside && prev.type === 'brace') { - prev.text = prev.text || ''; - prev.text += val; - } + /** + * Static method for running global mixin functions against a child constructor. + * Mixins must be registered before calling this method. + * + * ```js + * Base.extend(Child); + * Base.mixins(Child); + * ``` + * @name #mixins + * @param {Function} `Child` Constructor function of a child class + * @return {Object} Returns the `Base` constructor for chaining + * @api public + */ - var node = pos(new Node({ - type: 'text', - multiplier: 1, - val: val - })); + define(Base, 'mixins', function(Child) { + Base.run(Child, 'mixin', Base.prototype.mixins); + return Base; + }); - return concatNodes.call(this, pos, node, prev, options); - }); -}; + /** + * Similar to `util.inherit`, but copies all static properties, prototype properties, and + * getters/setters from `Provider` to `Receiver`. See [class-utils][]{#inherit} for more details. + * + * ```js + * Base.inherit(Foo, Bar); + * ``` + * @name #inherit + * @param {Function} `Receiver` Receiving (child) constructor + * @param {Function} `Provider` Providing (parent) constructor + * @return {Object} Returns the `Base` constructor for chaining + * @api public + */ + + define(Base, 'inherit', cu.inherit); + define(Base, 'bubble', cu.bubble); + return Base; +} /** - * Returns true if the character is an extglob character. + * Expose `Base` with default settings */ -function isExtglobChar(ch) { - return ch === '!' || ch === '@' || ch === '*' || ch === '?' || ch === '+'; -} +module.exports = namespace(); /** - * Combine text nodes, and calculate empty sets (`{,,}`) - * @param {Function} `pos` Function to calculate node position - * @param {Object} `node` AST node - * @return {Object} + * Allow users to define a namespace */ -function concatNodes(pos, node, parent, options) { - node.orig = node.val; - var prev = this.prev(); - var last = utils.last(prev.nodes); - var isEscaped = false; +module.exports.namespace = namespace; - if (node.val.length > 1) { - var a = node.val.charAt(0); - var b = node.val.slice(-1); - isEscaped = (a === '"' && b === '"') - || (a === "'" && b === "'") - || (a === '`' && b === '`'); - } +/***/ }), +/* 625 */ +/***/ (function(module, exports, __webpack_require__) { - if (isEscaped && options.unescape !== false) { - node.val = node.val.slice(1, node.val.length - 1); - node.escaped = true; - } +"use strict"; +/*! + * define-property + * + * Copyright (c) 2015, 2017, Jon Schlinkert. + * Released under the MIT License. + */ - if (node.match) { - var match = node.match[1]; - if (!match || match.indexOf('}') === -1) { - match = node.match[0]; - } - // replace each set with a single "," - var val = match.replace(/\{/g, ',').replace(/\}/g, ''); - node.multiplier *= val.length; - node.val = ''; + +var isDescriptor = __webpack_require__(588); + +module.exports = function defineProperty(obj, prop, val) { + if (typeof obj !== 'object' && typeof obj !== 'function') { + throw new TypeError('expected an object or function.'); } - var simpleText = last.type === 'text' - && last.multiplier === 1 - && node.multiplier === 1 - && node.val; + if (typeof prop !== 'string') { + throw new TypeError('expected `prop` to be a string.'); + } - if (simpleText) { - last.val += node.val; - return; + if (isDescriptor(val) && ('set' in val || 'get' in val)) { + return Object.defineProperty(obj, prop, val); } - prev.push(node); -} + return Object.defineProperty(obj, prop, { + configurable: true, + enumerable: false, + writable: true, + value: val + }); +}; /***/ }), -/* 612 */ +/* 626 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(581); -var define = __webpack_require__(613); -var utils = __webpack_require__(614); -var ownNames; +var isObject = __webpack_require__(587); +var Emitter = __webpack_require__(627); +var visit = __webpack_require__(628); +var toPath = __webpack_require__(631); +var union = __webpack_require__(633); +var del = __webpack_require__(639); +var get = __webpack_require__(636); +var has = __webpack_require__(644); +var set = __webpack_require__(647); /** - * Create a new AST `Node` with the given `val` and `type`. + * Create a `Cache` constructor that when instantiated will + * store values on the given `prop`. * * ```js - * var node = new Node('*', 'Star'); - * var node = new Node({type: 'star', val: '*'}); + * var Cache = require('cache-base').namespace('data'); + * var cache = new Cache(); + * + * cache.set('foo', 'bar'); + * //=> {data: {foo: 'bar'}} * ``` - * @name Node - * @param {String|Object} `val` Pass a matched substring, or an object to merge onto the node. - * @param {String} `type` The node type to use when `val` is a string. - * @return {Object} node instance + * @param {String} `prop` The property name to use for storing values. + * @return {Function} Returns a custom `Cache` constructor * @api public */ -function Node(val, type, parent) { - if (typeof type !== 'string') { - parent = type; - type = null; - } +function namespace(prop) { - define(this, 'parent', parent); - define(this, 'isNode', true); - define(this, 'expect', null); + /** + * Create a new `Cache`. Internally the `Cache` constructor is created using + * the `namespace` function, with `cache` defined as the storage object. + * + * ```js + * var app = new Cache(); + * ``` + * @param {Object} `cache` Optionally pass an object to initialize with. + * @constructor + * @api public + */ - if (typeof type !== 'string' && isObject(val)) { - lazyKeys(); - var keys = Object.keys(val); - for (var i = 0; i < keys.length; i++) { - var key = keys[i]; - if (ownNames.indexOf(key) === -1) { - this[key] = val[key]; - } + function Cache(cache) { + if (prop) { + this[prop] = {}; + } + if (cache) { + this.set(cache); } - } else { - this.type = type; - this.val = val; } -} -/** - * Returns true if the given value is a node. - * - * ```js - * var Node = require('snapdragon-node'); - * var node = new Node({type: 'foo'}); - * console.log(Node.isNode(node)); //=> true - * console.log(Node.isNode({})); //=> false - * ``` - * @param {Object} `node` - * @returns {Boolean} - * @api public - */ + /** + * Inherit Emitter + */ -Node.isNode = function(node) { - return utils.isNode(node); -}; + Emitter(Cache.prototype); -/** - * Define a non-enumberable property on the node instance. - * Useful for adding properties that shouldn't be extended - * or visible during debugging. - * - * ```js - * var node = new Node(); - * node.define('foo', 'something non-enumerable'); - * ``` - * @param {String} `name` - * @param {any} `val` - * @return {Object} returns the node instance - * @api public - */ + /** + * Assign `value` to `key`. Also emits `set` with + * the key and value. + * + * ```js + * app.on('set', function(key, val) { + * // do something when `set` is emitted + * }); + * + * app.set(key, value); + * + * // also takes an object or array + * app.set({name: 'Halle'}); + * app.set([{foo: 'bar'}, {baz: 'quux'}]); + * console.log(app); + * //=> {name: 'Halle', foo: 'bar', baz: 'quux'} + * ``` + * + * @name .set + * @emits `set` with `key` and `value` as arguments. + * @param {String} `key` + * @param {any} `value` + * @return {Object} Returns the instance for chaining. + * @api public + */ + + Cache.prototype.set = function(key, val) { + if (Array.isArray(key) && arguments.length === 2) { + key = toPath(key); + } + if (isObject(key) || Array.isArray(key)) { + this.visit('set', key); + } else { + set(prop ? this[prop] : this, key, val); + this.emit('set', key, val); + } + return this; + }; -Node.prototype.define = function(name, val) { - define(this, name, val); - return this; -}; + /** + * Union `array` to `key`. Also emits `set` with + * the key and value. + * + * ```js + * app.union('a.b', ['foo']); + * app.union('a.b', ['bar']); + * console.log(app.get('a')); + * //=> {b: ['foo', 'bar']} + * ``` + * @name .union + * @param {String} `key` + * @param {any} `value` + * @return {Object} Returns the instance for chaining. + * @api public + */ -/** - * Returns true if `node.val` is an empty string, or `node.nodes` does - * not contain any non-empty text nodes. - * - * ```js - * var node = new Node({type: 'text'}); - * node.isEmpty(); //=> true - * node.val = 'foo'; - * node.isEmpty(); //=> false - * ``` - * @param {Function} `fn` (optional) Filter function that is called on `node` and/or child nodes. `isEmpty` will return false immediately when the filter function returns false on any nodes. - * @return {Boolean} - * @api public - */ + Cache.prototype.union = function(key, val) { + if (Array.isArray(key) && arguments.length === 2) { + key = toPath(key); + } + var ctx = prop ? this[prop] : this; + union(ctx, key, arrayify(val)); + this.emit('union', val); + return this; + }; -Node.prototype.isEmpty = function(fn) { - return utils.isEmpty(this, fn); -}; + /** + * Return the value of `key`. Dot notation may be used + * to get [nested property values][get-value]. + * + * ```js + * app.set('a.b.c', 'd'); + * app.get('a.b'); + * //=> {c: 'd'} + * + * app.get(['a', 'b']); + * //=> {c: 'd'} + * ``` + * + * @name .get + * @emits `get` with `key` and `value` as arguments. + * @param {String} `key` The name of the property to get. Dot-notation may be used. + * @return {any} Returns the value of `key` + * @api public + */ -/** - * Given node `foo` and node `bar`, push node `bar` onto `foo.nodes`, and - * set `foo` as `bar.parent`. - * - * ```js - * var foo = new Node({type: 'foo'}); - * var bar = new Node({type: 'bar'}); - * foo.push(bar); - * ``` - * @param {Object} `node` - * @return {Number} Returns the length of `node.nodes` - * @api public - */ + Cache.prototype.get = function(key) { + key = toPath(arguments); -Node.prototype.push = function(node) { - assert(Node.isNode(node), 'expected node to be an instance of Node'); - define(node, 'parent', this); + var ctx = prop ? this[prop] : this; + var val = get(ctx, key); - this.nodes = this.nodes || []; - return this.nodes.push(node); -}; + this.emit('get', key, val); + return val; + }; -/** - * Given node `foo` and node `bar`, unshift node `bar` onto `foo.nodes`, and - * set `foo` as `bar.parent`. - * - * ```js - * var foo = new Node({type: 'foo'}); - * var bar = new Node({type: 'bar'}); - * foo.unshift(bar); - * ``` - * @param {Object} `node` - * @return {Number} Returns the length of `node.nodes` - * @api public - */ + /** + * Return true if app has a stored value for `key`, + * false only if value is `undefined`. + * + * ```js + * app.set('foo', 'bar'); + * app.has('foo'); + * //=> true + * ``` + * + * @name .has + * @emits `has` with `key` and true or false as arguments. + * @param {String} `key` + * @return {Boolean} + * @api public + */ -Node.prototype.unshift = function(node) { - assert(Node.isNode(node), 'expected node to be an instance of Node'); - define(node, 'parent', this); + Cache.prototype.has = function(key) { + key = toPath(arguments); - this.nodes = this.nodes || []; - return this.nodes.unshift(node); -}; + var ctx = prop ? this[prop] : this; + var val = get(ctx, key); -/** - * Pop a node from `node.nodes`. - * - * ```js - * var node = new Node({type: 'foo'}); - * node.push(new Node({type: 'a'})); - * node.push(new Node({type: 'b'})); - * node.push(new Node({type: 'c'})); - * node.push(new Node({type: 'd'})); - * console.log(node.nodes.length); - * //=> 4 - * node.pop(); - * console.log(node.nodes.length); - * //=> 3 - * ``` - * @return {Number} Returns the popped `node` - * @api public - */ + var has = typeof val !== 'undefined'; + this.emit('has', key, has); + return has; + }; -Node.prototype.pop = function() { - return this.nodes && this.nodes.pop(); -}; + /** + * Delete one or more properties from the instance. + * + * ```js + * app.del(); // delete all + * // or + * app.del('foo'); + * // or + * app.del(['foo', 'bar']); + * ``` + * @name .del + * @emits `del` with the `key` as the only argument. + * @param {String|Array} `key` Property name or array of property names. + * @return {Object} Returns the instance for chaining. + * @api public + */ -/** - * Shift a node from `node.nodes`. - * - * ```js - * var node = new Node({type: 'foo'}); - * node.push(new Node({type: 'a'})); - * node.push(new Node({type: 'b'})); - * node.push(new Node({type: 'c'})); - * node.push(new Node({type: 'd'})); - * console.log(node.nodes.length); - * //=> 4 - * node.shift(); - * console.log(node.nodes.length); - * //=> 3 - * ``` - * @return {Object} Returns the shifted `node` - * @api public - */ + Cache.prototype.del = function(key) { + if (Array.isArray(key)) { + this.visit('del', key); + } else { + del(prop ? this[prop] : this, key); + this.emit('del', key); + } + return this; + }; -Node.prototype.shift = function() { - return this.nodes && this.nodes.shift(); -}; + /** + * Reset the entire cache to an empty object. + * + * ```js + * app.clear(); + * ``` + * @api public + */ -/** - * Remove `node` from `node.nodes`. - * - * ```js - * node.remove(childNode); - * ``` - * @param {Object} `node` - * @return {Object} Returns the removed node. - * @api public - */ + Cache.prototype.clear = function() { + if (prop) { + this[prop] = {}; + } + }; -Node.prototype.remove = function(node) { - assert(Node.isNode(node), 'expected node to be an instance of Node'); - this.nodes = this.nodes || []; - var idx = node.index; - if (idx !== -1) { - node.index = -1; - return this.nodes.splice(idx, 1); - } - return null; -}; + /** + * Visit `method` over the properties in the given object, or map + * visit over the object-elements in an array. + * + * @name .visit + * @param {String} `method` The name of the `base` method to call. + * @param {Object|Array} `val` The object or array to iterate over. + * @return {Object} Returns the instance for chaining. + * @api public + */ -/** - * Get the first child node from `node.nodes` that matches the given `type`. - * If `type` is a number, the child node at that index is returned. - * - * ```js - * var child = node.find(1); //<= index of the node to get - * var child = node.find('foo'); //<= node.type of a child node - * var child = node.find(/^(foo|bar)$/); //<= regex to match node.type - * var child = node.find(['foo', 'bar']); //<= array of node.type(s) - * ``` - * @param {String} `type` - * @return {Object} Returns a child node or undefined. - * @api public - */ + Cache.prototype.visit = function(method, val) { + visit(this, method, val); + return this; + }; -Node.prototype.find = function(type) { - return utils.findNode(this.nodes, type); -}; + return Cache; +} /** - * Return true if the node is the given `type`. - * - * ```js - * var node = new Node({type: 'bar'}); - * cosole.log(node.isType('foo')); // false - * cosole.log(node.isType(/^(foo|bar)$/)); // true - * cosole.log(node.isType(['foo', 'bar'])); // true - * ``` - * @param {String} `type` - * @return {Boolean} - * @api public + * Cast val to an array */ -Node.prototype.isType = function(type) { - return utils.isType(this, type); -}; +function arrayify(val) { + return val ? (Array.isArray(val) ? val : [val]) : []; +} /** - * Return true if the `node.nodes` has the given `type`. - * - * ```js - * var foo = new Node({type: 'foo'}); - * var bar = new Node({type: 'bar'}); - * foo.push(bar); - * - * cosole.log(foo.hasType('qux')); // false - * cosole.log(foo.hasType(/^(qux|bar)$/)); // true - * cosole.log(foo.hasType(['qux', 'bar'])); // true - * ``` - * @param {String} `type` - * @return {Boolean} - * @api public + * Expose `Cache` */ -Node.prototype.hasType = function(type) { - return utils.hasType(this, type); -}; +module.exports = namespace(); /** - * Get the siblings array, or `null` if it doesn't exist. - * - * ```js - * var foo = new Node({type: 'foo'}); - * var bar = new Node({type: 'bar'}); - * var baz = new Node({type: 'baz'}); - * foo.push(bar); - * foo.push(baz); - * - * console.log(bar.siblings.length) // 2 - * console.log(baz.siblings.length) // 2 - * ``` - * @return {Array} - * @api public + * Expose `Cache.namespace` */ -Object.defineProperty(Node.prototype, 'siblings', { - set: function() { - throw new Error('node.siblings is a getter and cannot be defined'); - }, - get: function() { - return this.parent ? this.parent.nodes : null; - } -}); - -/** - * Get the node's current index from `node.parent.nodes`. - * This should always be correct, even when the parent adds nodes. - * - * ```js - * var foo = new Node({type: 'foo'}); - * var bar = new Node({type: 'bar'}); - * var baz = new Node({type: 'baz'}); - * var qux = new Node({type: 'qux'}); - * foo.push(bar); - * foo.push(baz); - * foo.unshift(qux); - * - * console.log(bar.index) // 1 - * console.log(baz.index) // 2 - * console.log(qux.index) // 0 - * ``` - * @return {Number} - * @api public - */ +module.exports.namespace = namespace; -Object.defineProperty(Node.prototype, 'index', { - set: function(index) { - define(this, 'idx', index); - }, - get: function() { - if (!Array.isArray(this.siblings)) { - return -1; - } - var tok = this.idx !== -1 ? this.siblings[this.idx] : null; - if (tok !== this) { - this.idx = this.siblings.indexOf(this); - } - return this.idx; - } -}); -/** - * Get the previous node from the siblings array or `null`. - * - * ```js - * var foo = new Node({type: 'foo'}); - * var bar = new Node({type: 'bar'}); - * var baz = new Node({type: 'baz'}); - * foo.push(bar); - * foo.push(baz); - * - * console.log(baz.prev.type) // 'bar' - * ``` - * @return {Object} - * @api public - */ +/***/ }), +/* 627 */ +/***/ (function(module, exports, __webpack_require__) { -Object.defineProperty(Node.prototype, 'prev', { - set: function() { - throw new Error('node.prev is a getter and cannot be defined'); - }, - get: function() { - if (Array.isArray(this.siblings)) { - return this.siblings[this.index - 1] || this.parent.prev; - } - return null; - } -}); + +/** + * Expose `Emitter`. + */ + +if (true) { + module.exports = Emitter; +} + +/** + * Initialize a new `Emitter`. + * + * @api public + */ + +function Emitter(obj) { + if (obj) return mixin(obj); +}; + +/** + * Mixin the emitter properties. + * + * @param {Object} obj + * @return {Object} + * @api private + */ + +function mixin(obj) { + for (var key in Emitter.prototype) { + obj[key] = Emitter.prototype[key]; + } + return obj; +} + +/** + * Listen on the given `event` with `fn`. + * + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public + */ + +Emitter.prototype.on = +Emitter.prototype.addEventListener = function(event, fn){ + this._callbacks = this._callbacks || {}; + (this._callbacks['$' + event] = this._callbacks['$' + event] || []) + .push(fn); + return this; +}; + +/** + * Adds an `event` listener that will be invoked a single + * time then automatically removed. + * + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public + */ + +Emitter.prototype.once = function(event, fn){ + function on() { + this.off(event, on); + fn.apply(this, arguments); + } + + on.fn = fn; + this.on(event, on); + return this; +}; + +/** + * Remove the given callback for `event` or all + * registered callbacks. + * + * @param {String} event + * @param {Function} fn + * @return {Emitter} + * @api public + */ + +Emitter.prototype.off = +Emitter.prototype.removeListener = +Emitter.prototype.removeAllListeners = +Emitter.prototype.removeEventListener = function(event, fn){ + this._callbacks = this._callbacks || {}; + + // all + if (0 == arguments.length) { + this._callbacks = {}; + return this; + } + + // specific event + var callbacks = this._callbacks['$' + event]; + if (!callbacks) return this; + + // remove all handlers + if (1 == arguments.length) { + delete this._callbacks['$' + event]; + return this; + } + + // remove specific handler + var cb; + for (var i = 0; i < callbacks.length; i++) { + cb = callbacks[i]; + if (cb === fn || cb.fn === fn) { + callbacks.splice(i, 1); + break; + } + } + return this; +}; + +/** + * Emit `event` with the given args. + * + * @param {String} event + * @param {Mixed} ... + * @return {Emitter} + */ + +Emitter.prototype.emit = function(event){ + this._callbacks = this._callbacks || {}; + var args = [].slice.call(arguments, 1) + , callbacks = this._callbacks['$' + event]; + + if (callbacks) { + callbacks = callbacks.slice(0); + for (var i = 0, len = callbacks.length; i < len; ++i) { + callbacks[i].apply(this, args); + } + } + + return this; +}; + +/** + * Return array of callbacks for `event`. + * + * @param {String} event + * @return {Array} + * @api public + */ + +Emitter.prototype.listeners = function(event){ + this._callbacks = this._callbacks || {}; + return this._callbacks['$' + event] || []; +}; + +/** + * Check if this emitter has `event` handlers. + * + * @param {String} event + * @return {Boolean} + * @api public + */ + +Emitter.prototype.hasListeners = function(event){ + return !! this.listeners(event).length; +}; -/** - * Get the siblings array, or `null` if it doesn't exist. - * - * ```js - * var foo = new Node({type: 'foo'}); - * var bar = new Node({type: 'bar'}); - * var baz = new Node({type: 'baz'}); - * foo.push(bar); - * foo.push(baz); - * - * console.log(bar.siblings.length) // 2 - * console.log(baz.siblings.length) // 2 - * ``` - * @return {Object} - * @api public - */ -Object.defineProperty(Node.prototype, 'next', { - set: function() { - throw new Error('node.next is a getter and cannot be defined'); - }, - get: function() { - if (Array.isArray(this.siblings)) { - return this.siblings[this.index + 1] || this.parent.next; - } - return null; - } -}); +/***/ }), +/* 628 */ +/***/ (function(module, exports, __webpack_require__) { -/** - * Get the first node from `node.nodes`. - * - * ```js - * var foo = new Node({type: 'foo'}); - * var bar = new Node({type: 'bar'}); - * var baz = new Node({type: 'baz'}); - * var qux = new Node({type: 'qux'}); - * foo.push(bar); - * foo.push(baz); - * foo.push(qux); +"use strict"; +/*! + * collection-visit * - * console.log(foo.first.type) // 'bar' - * ``` - * @return {Object} The first node, or undefiend - * @api public + * Copyright (c) 2015, 2017, Jon Schlinkert. + * Released under the MIT License. */ -Object.defineProperty(Node.prototype, 'first', { - get: function() { - return this.nodes ? this.nodes[0] : null; - } -}); -/** - * Get the last node from `node.nodes`. - * - * ```js - * var foo = new Node({type: 'foo'}); - * var bar = new Node({type: 'bar'}); - * var baz = new Node({type: 'baz'}); - * var qux = new Node({type: 'qux'}); - * foo.push(bar); - * foo.push(baz); - * foo.push(qux); - * - * console.log(foo.last.type) // 'qux' - * ``` - * @return {Object} The last node, or undefiend - * @api public - */ -Object.defineProperty(Node.prototype, 'last', { - get: function() { - return this.nodes ? utils.last(this.nodes) : null; - } -}); +var visit = __webpack_require__(629); +var mapVisit = __webpack_require__(630); -/** - * Get the last node from `node.nodes`. - * - * ```js - * var foo = new Node({type: 'foo'}); - * var bar = new Node({type: 'bar'}); - * var baz = new Node({type: 'baz'}); - * var qux = new Node({type: 'qux'}); - * foo.push(bar); - * foo.push(baz); - * foo.push(qux); - * - * console.log(foo.last.type) // 'qux' - * ``` - * @return {Object} The last node, or undefiend - * @api public - */ +module.exports = function(collection, method, val) { + var result; -Object.defineProperty(Node.prototype, 'scope', { - get: function() { - if (this.isScope !== true) { - return this.parent ? this.parent.scope : this; - } - return this; + if (typeof val === 'string' && (method in collection)) { + var args = [].slice.call(arguments, 2); + result = collection[method].apply(collection, args); + } else if (Array.isArray(val)) { + result = mapVisit.apply(null, arguments); + } else { + result = visit.apply(null, arguments); } -}); - -/** - * Get own property names from Node prototype, but only the - * first time `Node` is instantiated - */ -function lazyKeys() { - if (!ownNames) { - ownNames = Object.getOwnPropertyNames(Node.prototype); + if (typeof result !== 'undefined') { + return result; } -} - -/** - * Simplified assertion. Throws an error is `val` is falsey. - */ - -function assert(val, message) { - if (!val) throw new Error(message); -} -/** - * Expose `Node` - */ - -exports = module.exports = Node; + return collection; +}; /***/ }), -/* 613 */ +/* 629 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /*! - * define-property + * object-visit * * Copyright (c) 2015, 2017, Jon Schlinkert. * Released under the MIT License. @@ -67936,1061 +72473,724 @@ exports = module.exports = Node; -var isDescriptor = __webpack_require__(582); +var isObject = __webpack_require__(587); -module.exports = function defineProperty(obj, prop, val) { - if (typeof obj !== 'object' && typeof obj !== 'function') { - throw new TypeError('expected an object or function.'); +module.exports = function visit(thisArg, method, target, val) { + if (!isObject(thisArg) && typeof thisArg !== 'function') { + throw new Error('object-visit expects `thisArg` to be an object.'); } - if (typeof prop !== 'string') { - throw new TypeError('expected `prop` to be a string.'); + if (typeof method !== 'string') { + throw new Error('object-visit expects `method` name to be a string'); } - if (isDescriptor(val) && ('set' in val || 'get' in val)) { - return Object.defineProperty(obj, prop, val); + if (typeof thisArg[method] !== 'function') { + return thisArg; } - return Object.defineProperty(obj, prop, { - configurable: true, - enumerable: false, - writable: true, - value: val - }); + var args = [].slice.call(arguments, 3); + target = target || {}; + + for (var key in target) { + var arr = [key, target[key]].concat(args); + thisArg[method].apply(thisArg, arr); + } + return thisArg; }; /***/ }), -/* 614 */ +/* 630 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var typeOf = __webpack_require__(615); -var utils = module.exports; +var util = __webpack_require__(113); +var visit = __webpack_require__(629); /** - * Returns true if the given value is a node. + * Map `visit` over an array of objects. * - * ```js - * var Node = require('snapdragon-node'); - * var node = new Node({type: 'foo'}); - * console.log(utils.isNode(node)); //=> true - * console.log(utils.isNode({})); //=> false - * ``` - * @param {Object} `node` Instance of [snapdragon-node][] - * @returns {Boolean} - * @api public + * @param {Object} `collection` The context in which to invoke `method` + * @param {String} `method` Name of the method to call on `collection` + * @param {Object} `arr` Array of objects. */ -utils.isNode = function(node) { - return typeOf(node) === 'object' && node.isNode === true; -}; +module.exports = function mapVisit(collection, method, val) { + if (isObject(val)) { + return visit.apply(null, arguments); + } -/** - * Emit an empty string for the given `node`. - * - * ```js - * // do nothing for beginning-of-string - * snapdragon.compiler.set('bos', utils.noop); - * ``` - * @param {Object} `node` Instance of [snapdragon-node][] - * @returns {undefined} - * @api public - */ + if (!Array.isArray(val)) { + throw new TypeError('expected an array: ' + util.inspect(val)); + } -utils.noop = function(node) { - append(this, '', node); + var args = [].slice.call(arguments, 3); + + for (var i = 0; i < val.length; i++) { + var ele = val[i]; + if (isObject(ele)) { + visit.apply(null, [collection, method, ele].concat(args)); + } else { + collection[method].apply(collection, [ele].concat(args)); + } + } }; -/** - * Appdend `node.val` to `compiler.output`, exactly as it was created - * by the parser. - * - * ```js - * snapdragon.compiler.set('text', utils.identity); - * ``` - * @param {Object} `node` Instance of [snapdragon-node][] - * @returns {undefined} - * @api public - */ +function isObject(val) { + return val && (typeof val === 'function' || (!Array.isArray(val) && typeof val === 'object')); +} -utils.identity = function(node) { - append(this, node.val, node); -}; -/** - * Previously named `.emit`, this method appends the given `val` - * to `compiler.output` for the given node. Useful when you know - * what value should be appended advance, regardless of the actual - * value of `node.val`. +/***/ }), +/* 631 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +/*! + * to-object-path * - * ```js - * snapdragon.compiler - * .set('i', function(node) { - * this.mapVisit(node); - * }) - * .set('i.open', utils.append('')) - * .set('i.close', utils.append('')) - * ``` - * @param {Object} `node` Instance of [snapdragon-node][] - * @returns {Function} Returns a compiler middleware function. - * @api public + * Copyright (c) 2015, Jon Schlinkert. + * Licensed under the MIT License. */ -utils.append = function(val) { - return function(node) { - append(this, val, node); - }; -}; -/** - * Used in compiler middleware, this onverts an AST node into - * an empty `text` node and deletes `node.nodes` if it exists. - * The advantage of this method is that, as opposed to completely - * removing the node, indices will not need to be re-calculated - * in sibling nodes, and nothing is appended to the output. - * - * ```js - * utils.toNoop(node); - * // convert `node.nodes` to the given value instead of deleting it - * utils.toNoop(node, []); - * ``` - * @param {Object} `node` Instance of [snapdragon-node][] - * @param {Array} `nodes` Optionally pass a new `nodes` value, to replace the existing `node.nodes` array. - * @api public - */ -utils.toNoop = function(node, nodes) { - if (nodes) { - node.nodes = nodes; - } else { - delete node.nodes; - node.type = 'text'; - node.val = ''; +var typeOf = __webpack_require__(632); + +module.exports = function toPath(args) { + if (typeOf(args) !== 'arguments') { + args = arguments; } + return filter(args).join('.'); }; -/** - * Visit `node` with the given `fn`. The built-in `.visit` method in snapdragon - * automatically calls registered compilers, this allows you to pass a visitor - * function. - * - * ```js - * snapdragon.compiler.set('i', function(node) { - * utils.visit(node, function(childNode) { - * // do stuff with "childNode" - * return childNode; - * }); - * }); - * ``` - * @param {Object} `node` Instance of [snapdragon-node][] - * @param {Function} `fn` - * @return {Object} returns the node after recursively visiting all child nodes. - * @api public - */ +function filter(arr) { + var len = arr.length; + var idx = -1; + var res = []; -utils.visit = function(node, fn) { - assert(utils.isNode(node), 'expected node to be an instance of Node'); - assert(isFunction(fn), 'expected a visitor function'); - fn(node); - return node.nodes ? utils.mapVisit(node, fn) : node; -}; + while (++idx < len) { + var ele = arr[idx]; + if (typeOf(ele) === 'arguments' || Array.isArray(ele)) { + res.push.apply(res, filter(ele)); + } else if (typeof ele === 'string') { + res.push(ele); + } + } + return res; +} + + +/***/ }), +/* 632 */ +/***/ (function(module, exports, __webpack_require__) { + +var isBuffer = __webpack_require__(611); +var toString = Object.prototype.toString; /** - * Map [visit](#visit) the given `fn` over `node.nodes`. This is called by - * [visit](#visit), use this method if you do not want `fn` to be called on - * the first node. + * Get the native `typeof` a value. * - * ```js - * snapdragon.compiler.set('i', function(node) { - * utils.mapVisit(node, function(childNode) { - * // do stuff with "childNode" - * return childNode; - * }); - * }); - * ``` - * @param {Object} `node` Instance of [snapdragon-node][] - * @param {Object} `options` - * @param {Function} `fn` - * @return {Object} returns the node - * @api public + * @param {*} `val` + * @return {*} Native javascript type */ -utils.mapVisit = function(node, fn) { - assert(utils.isNode(node), 'expected node to be an instance of Node'); - assert(isArray(node.nodes), 'expected node.nodes to be an array'); - assert(isFunction(fn), 'expected a visitor function'); +module.exports = function kindOf(val) { + // primitivies + if (typeof val === 'undefined') { + return 'undefined'; + } + if (val === null) { + return 'null'; + } + if (val === true || val === false || val instanceof Boolean) { + return 'boolean'; + } + if (typeof val === 'string' || val instanceof String) { + return 'string'; + } + if (typeof val === 'number' || val instanceof Number) { + return 'number'; + } - for (var i = 0; i < node.nodes.length; i++) { - utils.visit(node.nodes[i], fn); + // functions + if (typeof val === 'function' || val instanceof Function) { + return 'function'; } - return node; -}; -/** - * Unshift an `*.open` node onto `node.nodes`. - * - * ```js - * var Node = require('snapdragon-node'); - * snapdragon.parser.set('brace', function(node) { - * var match = this.match(/^{/); - * if (match) { - * var parent = new Node({type: 'brace'}); - * utils.addOpen(parent, Node); - * console.log(parent.nodes[0]): - * // { type: 'brace.open', val: '' }; - * - * // push the parent "brace" node onto the stack - * this.push(parent); - * - * // return the parent node, so it's also added to the AST - * return brace; - * } - * }); - * ``` - * @param {Object} `node` Instance of [snapdragon-node][] - * @param {Function} `Node` (required) Node constructor function from [snapdragon-node][]. - * @param {Function} `filter` Optionaly specify a filter function to exclude the node. - * @return {Object} Returns the created opening node. - * @api public - */ + // array + if (typeof Array.isArray !== 'undefined' && Array.isArray(val)) { + return 'array'; + } -utils.addOpen = function(node, Node, val, filter) { - assert(utils.isNode(node), 'expected node to be an instance of Node'); - assert(isFunction(Node), 'expected Node to be a constructor function'); + // check for instances of RegExp and Date before calling `toString` + if (val instanceof RegExp) { + return 'regexp'; + } + if (val instanceof Date) { + return 'date'; + } - if (typeof val === 'function') { - filter = val; - val = ''; + // other objects + var type = toString.call(val); + + if (type === '[object RegExp]') { + return 'regexp'; + } + if (type === '[object Date]') { + return 'date'; + } + if (type === '[object Arguments]') { + return 'arguments'; + } + if (type === '[object Error]') { + return 'error'; + } + + // buffer + if (isBuffer(val)) { + return 'buffer'; + } + + // es6: Map, WeakMap, Set, WeakSet + if (type === '[object Set]') { + return 'set'; + } + if (type === '[object WeakSet]') { + return 'weakset'; + } + if (type === '[object Map]') { + return 'map'; + } + if (type === '[object WeakMap]') { + return 'weakmap'; + } + if (type === '[object Symbol]') { + return 'symbol'; } - if (typeof filter === 'function' && !filter(node)) return; - var open = new Node({ type: node.type + '.open', val: val}); - var unshift = node.unshift || node.unshiftNode; - if (typeof unshift === 'function') { - unshift.call(node, open); - } else { - utils.unshiftNode(node, open); + // typed arrays + if (type === '[object Int8Array]') { + return 'int8array'; } - return open; + if (type === '[object Uint8Array]') { + return 'uint8array'; + } + if (type === '[object Uint8ClampedArray]') { + return 'uint8clampedarray'; + } + if (type === '[object Int16Array]') { + return 'int16array'; + } + if (type === '[object Uint16Array]') { + return 'uint16array'; + } + if (type === '[object Int32Array]') { + return 'int32array'; + } + if (type === '[object Uint32Array]') { + return 'uint32array'; + } + if (type === '[object Float32Array]') { + return 'float32array'; + } + if (type === '[object Float64Array]') { + return 'float64array'; + } + + // must be a plain object + return 'object'; }; -/** - * Push a `*.close` node onto `node.nodes`. - * - * ```js - * var Node = require('snapdragon-node'); - * snapdragon.parser.set('brace', function(node) { - * var match = this.match(/^}/); - * if (match) { - * var parent = this.parent(); - * if (parent.type !== 'brace') { - * throw new Error('missing opening: ' + '}'); - * } - * - * utils.addClose(parent, Node); - * console.log(parent.nodes[parent.nodes.length - 1]): - * // { type: 'brace.close', val: '' }; - * - * // no need to return a node, since the parent - * // was already added to the AST - * return; - * } - * }); - * ``` - * @param {Object} `node` Instance of [snapdragon-node][] - * @param {Function} `Node` (required) Node constructor function from [snapdragon-node][]. - * @param {Function} `filter` Optionaly specify a filter function to exclude the node. - * @return {Object} Returns the created closing node. - * @api public - */ -utils.addClose = function(node, Node, val, filter) { - assert(utils.isNode(node), 'expected node to be an instance of Node'); - assert(isFunction(Node), 'expected Node to be a constructor function'); +/***/ }), +/* 633 */ +/***/ (function(module, exports, __webpack_require__) { - if (typeof val === 'function') { - filter = val; - val = ''; +"use strict"; + + +var isObject = __webpack_require__(634); +var union = __webpack_require__(635); +var get = __webpack_require__(636); +var set = __webpack_require__(637); + +module.exports = function unionValue(obj, prop, value) { + if (!isObject(obj)) { + throw new TypeError('union-value expects the first argument to be an object.'); } - if (typeof filter === 'function' && !filter(node)) return; - var close = new Node({ type: node.type + '.close', val: val}); - var push = node.push || node.pushNode; - if (typeof push === 'function') { - push.call(node, close); - } else { - utils.pushNode(node, close); + if (typeof prop !== 'string') { + throw new TypeError('union-value expects `prop` to be a string.'); } - return close; + + var arr = arrayify(get(obj, prop)); + set(obj, prop, union(arr, arrayify(value))); + return obj; }; -/** - * Wraps the given `node` with `*.open` and `*.close` nodes. - * - * @param {Object} `node` Instance of [snapdragon-node][] - * @param {Function} `Node` (required) Node constructor function from [snapdragon-node][]. - * @param {Function} `filter` Optionaly specify a filter function to exclude the node. - * @return {Object} Returns the node - * @api public - */ +function arrayify(val) { + if (val === null || typeof val === 'undefined') { + return []; + } + if (Array.isArray(val)) { + return val; + } + return [val]; +} -utils.wrapNodes = function(node, Node, filter) { - assert(utils.isNode(node), 'expected node to be an instance of Node'); - assert(isFunction(Node), 'expected Node to be a constructor function'); - utils.addOpen(node, Node, filter); - utils.addClose(node, Node, filter); - return node; -}; +/***/ }), +/* 634 */ +/***/ (function(module, exports, __webpack_require__) { -/** - * Push the given `node` onto `parent.nodes`, and set `parent` as `node.parent. +"use strict"; +/*! + * is-extendable * - * ```js - * var parent = new Node({type: 'foo'}); - * var node = new Node({type: 'bar'}); - * utils.pushNode(parent, node); - * console.log(parent.nodes[0].type) // 'bar' - * console.log(node.parent.type) // 'foo' - * ``` - * @param {Object} `parent` - * @param {Object} `node` Instance of [snapdragon-node][] - * @return {Object} Returns the child node - * @api public + * Copyright (c) 2015, Jon Schlinkert. + * Licensed under the MIT License. */ -utils.pushNode = function(parent, node) { - assert(utils.isNode(parent), 'expected parent node to be an instance of Node'); - assert(utils.isNode(node), 'expected node to be an instance of Node'); - node.define('parent', parent); - parent.nodes = parent.nodes || []; - parent.nodes.push(node); - return node; + +module.exports = function isExtendable(val) { + return typeof val !== 'undefined' && val !== null + && (typeof val === 'object' || typeof val === 'function'); }; -/** - * Unshift `node` onto `parent.nodes`, and set `parent` as `node.parent. - * - * ```js - * var parent = new Node({type: 'foo'}); - * var node = new Node({type: 'bar'}); - * utils.unshiftNode(parent, node); - * console.log(parent.nodes[0].type) // 'bar' - * console.log(node.parent.type) // 'foo' - * ``` - * @param {Object} `parent` - * @param {Object} `node` Instance of [snapdragon-node][] - * @return {undefined} - * @api public - */ -utils.unshiftNode = function(parent, node) { - assert(utils.isNode(parent), 'expected parent node to be an instance of Node'); - assert(utils.isNode(node), 'expected node to be an instance of Node'); +/***/ }), +/* 635 */ +/***/ (function(module, exports, __webpack_require__) { - node.define('parent', parent); - parent.nodes = parent.nodes || []; - parent.nodes.unshift(node); -}; +"use strict"; -/** - * Pop the last `node` off of `parent.nodes`. The advantage of - * using this method is that it checks for `node.nodes` and works - * with any version of `snapdragon-node`. - * - * ```js - * var parent = new Node({type: 'foo'}); - * utils.pushNode(parent, new Node({type: 'foo'})); - * utils.pushNode(parent, new Node({type: 'bar'})); - * utils.pushNode(parent, new Node({type: 'baz'})); - * console.log(parent.nodes.length); //=> 3 - * utils.popNode(parent); - * console.log(parent.nodes.length); //=> 2 - * ``` - * @param {Object} `parent` - * @param {Object} `node` Instance of [snapdragon-node][] - * @return {Number|Undefined} Returns the length of `node.nodes` or undefined. - * @api public - */ -utils.popNode = function(node) { - assert(utils.isNode(node), 'expected node to be an instance of Node'); - if (typeof node.pop === 'function') { - return node.pop(); +module.exports = function union(init) { + if (!Array.isArray(init)) { + throw new TypeError('arr-union expects the first argument to be an array.'); } - return node.nodes && node.nodes.pop(); -}; -/** - * Shift the first `node` off of `parent.nodes`. The advantage of - * using this method is that it checks for `node.nodes` and works - * with any version of `snapdragon-node`. - * - * ```js - * var parent = new Node({type: 'foo'}); - * utils.pushNode(parent, new Node({type: 'foo'})); - * utils.pushNode(parent, new Node({type: 'bar'})); - * utils.pushNode(parent, new Node({type: 'baz'})); - * console.log(parent.nodes.length); //=> 3 - * utils.shiftNode(parent); - * console.log(parent.nodes.length); //=> 2 - * ``` - * @param {Object} `parent` - * @param {Object} `node` Instance of [snapdragon-node][] - * @return {Number|Undefined} Returns the length of `node.nodes` or undefined. - * @api public - */ + var len = arguments.length; + var i = 0; -utils.shiftNode = function(node) { - assert(utils.isNode(node), 'expected node to be an instance of Node'); - if (typeof node.shift === 'function') { - return node.shift(); + while (++i < len) { + var arg = arguments[i]; + if (!arg) continue; + + if (!Array.isArray(arg)) { + arg = [arg]; + } + + for (var j = 0; j < arg.length; j++) { + var ele = arg[j]; + + if (init.indexOf(ele) >= 0) { + continue; + } + init.push(ele); + } } - return node.nodes && node.nodes.shift(); + return init; }; -/** - * Remove the specified `node` from `parent.nodes`. + +/***/ }), +/* 636 */ +/***/ (function(module, exports) { + +/*! + * get-value * - * ```js - * var parent = new Node({type: 'abc'}); - * var foo = new Node({type: 'foo'}); - * utils.pushNode(parent, foo); - * utils.pushNode(parent, new Node({type: 'bar'})); - * utils.pushNode(parent, new Node({type: 'baz'})); - * console.log(parent.nodes.length); //=> 3 - * utils.removeNode(parent, foo); - * console.log(parent.nodes.length); //=> 2 - * ``` - * @param {Object} `parent` - * @param {Object} `node` Instance of [snapdragon-node][] - * @return {Object|undefined} Returns the removed node, if successful, or undefined if it does not exist on `parent.nodes`. - * @api public + * Copyright (c) 2014-2015, Jon Schlinkert. + * Licensed under the MIT License. */ -utils.removeNode = function(parent, node) { - assert(utils.isNode(parent), 'expected parent.node to be an instance of Node'); - assert(utils.isNode(node), 'expected node to be an instance of Node'); - - if (!parent.nodes) { - return null; +module.exports = function(obj, prop, a, b, c) { + if (!isObject(obj) || !prop) { + return obj; } - if (typeof parent.remove === 'function') { - return parent.remove(node); - } + prop = toString(prop); - var idx = parent.nodes.indexOf(node); - if (idx !== -1) { - return parent.nodes.splice(idx, 1); + // allowing for multiple properties to be passed as + // a string or array, but much faster (3-4x) than doing + // `[].slice.call(arguments)` + if (a) prop += '.' + toString(a); + if (b) prop += '.' + toString(b); + if (c) prop += '.' + toString(c); + + if (prop in obj) { + return obj[prop]; } -}; -/** - * Returns true if `node.type` matches the given `type`. Throws a - * `TypeError` if `node` is not an instance of `Node`. - * - * ```js - * var Node = require('snapdragon-node'); - * var node = new Node({type: 'foo'}); - * console.log(utils.isType(node, 'foo')); // false - * console.log(utils.isType(node, 'bar')); // true - * ``` - * @param {Object} `node` Instance of [snapdragon-node][] - * @param {String} `type` - * @return {Boolean} - * @api public - */ + var segs = prop.split('.'); + var len = segs.length; + var i = -1; -utils.isType = function(node, type) { - assert(utils.isNode(node), 'expected node to be an instance of Node'); - switch (typeOf(type)) { - case 'array': - var types = type.slice(); - for (var i = 0; i < types.length; i++) { - if (utils.isType(node, types[i])) { - return true; - } - } - return false; - case 'string': - return node.type === type; - case 'regexp': - return type.test(node.type); - default: { - throw new TypeError('expected "type" to be an array, string or regexp'); + while (obj && (++i < len)) { + var key = segs[i]; + while (key[key.length - 1] === '\\') { + key = key.slice(0, -1) + '.' + segs[++i]; } + obj = obj[key]; } + return obj; }; -/** - * Returns true if the given `node` has the given `type` in `node.nodes`. - * Throws a `TypeError` if `node` is not an instance of `Node`. - * - * ```js - * var Node = require('snapdragon-node'); - * var node = new Node({ - * type: 'foo', - * nodes: [ - * new Node({type: 'bar'}), - * new Node({type: 'baz'}) - * ] - * }); - * console.log(utils.hasType(node, 'xyz')); // false - * console.log(utils.hasType(node, 'baz')); // true - * ``` - * @param {Object} `node` Instance of [snapdragon-node][] - * @param {String} `type` - * @return {Boolean} - * @api public - */ +function isObject(val) { + return val !== null && (typeof val === 'object' || typeof val === 'function'); +} -utils.hasType = function(node, type) { - assert(utils.isNode(node), 'expected node to be an instance of Node'); - if (!Array.isArray(node.nodes)) return false; - for (var i = 0; i < node.nodes.length; i++) { - if (utils.isType(node.nodes[i], type)) { - return true; - } +function toString(val) { + if (!val) return ''; + if (Array.isArray(val)) { + return val.join('.'); } - return false; -}; + return val; +} -/** - * Returns the first node from `node.nodes` of the given `type` - * - * ```js - * var node = new Node({ - * type: 'foo', - * nodes: [ - * new Node({type: 'text', val: 'abc'}), - * new Node({type: 'text', val: 'xyz'}) - * ] - * }); + +/***/ }), +/* 637 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +/*! + * set-value * - * var textNode = utils.firstOfType(node.nodes, 'text'); - * console.log(textNode.val); - * //=> 'abc' - * ``` - * @param {Array} `nodes` - * @param {String} `type` - * @return {Object|undefined} Returns the first matching node or undefined. - * @api public + * Copyright (c) 2014-2015, 2017, Jon Schlinkert. + * Released under the MIT License. */ -utils.firstOfType = function(nodes, type) { - for (var i = 0; i < nodes.length; i++) { - var node = nodes[i]; - if (utils.isType(node, type)) { - return node; - } + + +var split = __webpack_require__(604); +var extend = __webpack_require__(638); +var isPlainObject = __webpack_require__(594); +var isObject = __webpack_require__(634); + +module.exports = function(obj, prop, val) { + if (!isObject(obj)) { + return obj; } -}; -/** - * Returns the node at the specified index, or the first node of the - * given `type` from `node.nodes`. - * - * ```js - * var node = new Node({ - * type: 'foo', - * nodes: [ - * new Node({type: 'text', val: 'abc'}), - * new Node({type: 'text', val: 'xyz'}) - * ] - * }); - * - * var nodeOne = utils.findNode(node.nodes, 'text'); - * console.log(nodeOne.val); - * //=> 'abc' - * - * var nodeTwo = utils.findNode(node.nodes, 1); - * console.log(nodeTwo.val); - * //=> 'xyz' - * ``` - * - * @param {Array} `nodes` - * @param {String|Number} `type` Node type or index. - * @return {Object} Returns a node or undefined. - * @api public - */ + if (Array.isArray(prop)) { + prop = [].concat.apply([], prop).join('.'); + } -utils.findNode = function(nodes, type) { - if (!Array.isArray(nodes)) { - return null; + if (typeof prop !== 'string') { + return obj; } - if (typeof type === 'number') { - return nodes[type]; + + var keys = split(prop, {sep: '.', brackets: true}).filter(isValidKey); + var len = keys.length; + var idx = -1; + var current = obj; + + while (++idx < len) { + var key = keys[idx]; + if (idx !== len - 1) { + if (!isObject(current[key])) { + current[key] = {}; + } + current = current[key]; + continue; + } + + if (isPlainObject(current[key]) && isPlainObject(val)) { + current[key] = extend({}, current[key], val); + } else { + current[key] = val; + } } - return utils.firstOfType(nodes, type); + + return obj; }; -/** - * Returns true if the given node is an "*.open" node. - * - * ```js - * var Node = require('snapdragon-node'); - * var brace = new Node({type: 'brace'}); - * var open = new Node({type: 'brace.open'}); - * var close = new Node({type: 'brace.close'}); - * - * console.log(utils.isOpen(brace)); // false - * console.log(utils.isOpen(open)); // true - * console.log(utils.isOpen(close)); // false - * ``` - * @param {Object} `node` Instance of [snapdragon-node][] - * @return {Boolean} - * @api public - */ +function isValidKey(key) { + return key !== '__proto__' && key !== 'constructor' && key !== 'prototype'; +} + + +/***/ }), +/* 638 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; -utils.isOpen = function(node) { - assert(utils.isNode(node), 'expected node to be an instance of Node'); - return node.type.slice(-5) === '.open'; -}; -/** - * Returns true if the given node is a "*.close" node. - * - * ```js - * var Node = require('snapdragon-node'); - * var brace = new Node({type: 'brace'}); - * var open = new Node({type: 'brace.open'}); - * var close = new Node({type: 'brace.close'}); - * - * console.log(utils.isClose(brace)); // false - * console.log(utils.isClose(open)); // false - * console.log(utils.isClose(close)); // true - * ``` - * @param {Object} `node` Instance of [snapdragon-node][] - * @return {Boolean} - * @api public - */ +var isObject = __webpack_require__(634); -utils.isClose = function(node) { - assert(utils.isNode(node), 'expected node to be an instance of Node'); - return node.type.slice(-6) === '.close'; -}; +module.exports = function extend(o/*, objects*/) { + if (!isObject(o)) { o = {}; } -/** - * Returns true if `node.nodes` **has** an `.open` node - * - * ```js - * var Node = require('snapdragon-node'); - * var brace = new Node({ - * type: 'brace', - * nodes: [] - * }); - * - * var open = new Node({type: 'brace.open'}); - * console.log(utils.hasOpen(brace)); // false - * - * brace.pushNode(open); - * console.log(utils.hasOpen(brace)); // true - * ``` - * @param {Object} `node` Instance of [snapdragon-node][] - * @return {Boolean} - * @api public - */ + var len = arguments.length; + for (var i = 1; i < len; i++) { + var obj = arguments[i]; -utils.hasOpen = function(node) { - assert(utils.isNode(node), 'expected node to be an instance of Node'); - var first = node.first || node.nodes ? node.nodes[0] : null; - if (utils.isNode(first)) { - return first.type === node.type + '.open'; + if (isObject(obj)) { + assign(o, obj); + } } - return false; + return o; }; -/** - * Returns true if `node.nodes` **has** a `.close` node - * - * ```js - * var Node = require('snapdragon-node'); - * var brace = new Node({ - * type: 'brace', - * nodes: [] - * }); - * - * var close = new Node({type: 'brace.close'}); - * console.log(utils.hasClose(brace)); // false - * - * brace.pushNode(close); - * console.log(utils.hasClose(brace)); // true - * ``` - * @param {Object} `node` Instance of [snapdragon-node][] - * @return {Boolean} - * @api public - */ - -utils.hasClose = function(node) { - assert(utils.isNode(node), 'expected node to be an instance of Node'); - var last = node.last || node.nodes ? node.nodes[node.nodes.length - 1] : null; - if (utils.isNode(last)) { - return last.type === node.type + '.close'; +function assign(a, b) { + for (var key in b) { + if (hasOwn(b, key)) { + a[key] = b[key]; + } } - return false; -}; +} /** - * Returns true if `node.nodes` has both `.open` and `.close` nodes - * - * ```js - * var Node = require('snapdragon-node'); - * var brace = new Node({ - * type: 'brace', - * nodes: [] - * }); - * - * var open = new Node({type: 'brace.open'}); - * var close = new Node({type: 'brace.close'}); - * console.log(utils.hasOpen(brace)); // false - * console.log(utils.hasClose(brace)); // false - * - * brace.pushNode(open); - * brace.pushNode(close); - * console.log(utils.hasOpen(brace)); // true - * console.log(utils.hasClose(brace)); // true - * ``` - * @param {Object} `node` Instance of [snapdragon-node][] - * @return {Boolean} - * @api public + * Returns true if the given `key` is an own property of `obj`. */ -utils.hasOpenAndClose = function(node) { - return utils.hasOpen(node) && utils.hasClose(node); -}; +function hasOwn(obj, key) { + return Object.prototype.hasOwnProperty.call(obj, key); +} -/** - * Push the given `node` onto the `state.inside` array for the - * given type. This array is used as a specialized "stack" for - * only the given `node.type`. + +/***/ }), +/* 639 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +/*! + * unset-value * - * ```js - * var state = { inside: {}}; - * var node = new Node({type: 'brace'}); - * utils.addType(state, node); - * console.log(state.inside); - * //=> { brace: [{type: 'brace'}] } - * ``` - * @param {Object} `state` The `compiler.state` object or custom state object. - * @param {Object} `node` Instance of [snapdragon-node][] - * @return {Array} Returns the `state.inside` stack for the given type. - * @api public + * Copyright (c) 2015, 2017, Jon Schlinkert. + * Released under the MIT License. */ -utils.addType = function(state, node) { - assert(utils.isNode(node), 'expected node to be an instance of Node'); - assert(isObject(state), 'expected state to be an object'); - var type = node.parent - ? node.parent.type - : node.type.replace(/\.open$/, ''); - if (!state.hasOwnProperty('inside')) { - state.inside = {}; +var isObject = __webpack_require__(587); +var has = __webpack_require__(640); + +module.exports = function unset(obj, prop) { + if (!isObject(obj)) { + throw new TypeError('expected an object.'); } - if (!state.inside.hasOwnProperty(type)) { - state.inside[type] = []; + if (obj.hasOwnProperty(prop)) { + delete obj[prop]; + return true; } - var arr = state.inside[type]; - arr.push(node); - return arr; + if (has(obj, prop)) { + var segs = prop.split('.'); + var last = segs.pop(); + while (segs.length && segs[segs.length - 1].slice(-1) === '\\') { + last = segs.pop().slice(0, -1) + '.' + last; + } + while (segs.length) obj = obj[prop = segs.shift()]; + return (delete obj[last]); + } + return true; }; -/** - * Remove the given `node` from the `state.inside` array for the - * given type. This array is used as a specialized "stack" for - * only the given `node.type`. + +/***/ }), +/* 640 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +/*! + * has-value * - * ```js - * var state = { inside: {}}; - * var node = new Node({type: 'brace'}); - * utils.addType(state, node); - * console.log(state.inside); - * //=> { brace: [{type: 'brace'}] } - * utils.removeType(state, node); - * //=> { brace: [] } - * ``` - * @param {Object} `state` The `compiler.state` object or custom state object. - * @param {Object} `node` Instance of [snapdragon-node][] - * @return {Array} Returns the `state.inside` stack for the given type. - * @api public + * Copyright (c) 2014-2016, Jon Schlinkert. + * Licensed under the MIT License. */ -utils.removeType = function(state, node) { - assert(utils.isNode(node), 'expected node to be an instance of Node'); - assert(isObject(state), 'expected state to be an object'); - var type = node.parent - ? node.parent.type - : node.type.replace(/\.close$/, ''); - if (state.inside.hasOwnProperty(type)) { - return state.inside[type].pop(); +var isObject = __webpack_require__(641); +var hasValues = __webpack_require__(643); +var get = __webpack_require__(636); + +module.exports = function(obj, prop, noZero) { + if (isObject(obj)) { + return hasValues(get(obj, prop), noZero); } + return hasValues(obj, prop); }; -/** - * Returns true if `node.val` is an empty string, or `node.nodes` does - * not contain any non-empty text nodes. + +/***/ }), +/* 641 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +/*! + * isobject * - * ```js - * var node = new Node({type: 'text'}); - * utils.isEmpty(node); //=> true - * node.val = 'foo'; - * utils.isEmpty(node); //=> false - * ``` - * @param {Object} `node` Instance of [snapdragon-node][] - * @param {Function} `fn` - * @return {Boolean} - * @api public + * Copyright (c) 2014-2015, Jon Schlinkert. + * Licensed under the MIT License. */ -utils.isEmpty = function(node, fn) { - assert(utils.isNode(node), 'expected node to be an instance of Node'); - if (!Array.isArray(node.nodes)) { - if (node.type !== 'text') { - return true; - } - if (typeof fn === 'function') { - return fn(node, node.parent); - } - return !utils.trim(node.val); - } - for (var i = 0; i < node.nodes.length; i++) { - var child = node.nodes[i]; - if (utils.isOpen(child) || utils.isClose(child)) { - continue; - } - if (!utils.isEmpty(child, fn)) { - return false; - } - } +var isArray = __webpack_require__(642); - return true; +module.exports = function isObject(val) { + return val != null && typeof val === 'object' && isArray(val) === false; }; -/** - * Returns true if the `state.inside` stack for the given type exists - * and has one or more nodes on it. - * - * ```js - * var state = { inside: {}}; - * var node = new Node({type: 'brace'}); - * console.log(utils.isInsideType(state, 'brace')); //=> false - * utils.addType(state, node); - * console.log(utils.isInsideType(state, 'brace')); //=> true - * utils.removeType(state, node); - * console.log(utils.isInsideType(state, 'brace')); //=> false - * ``` - * @param {Object} `state` - * @param {String} `type` - * @return {Boolean} - * @api public - */ - -utils.isInsideType = function(state, type) { - assert(isObject(state), 'expected state to be an object'); - assert(isString(type), 'expected type to be a string'); - if (!state.hasOwnProperty('inside')) { - return false; - } +/***/ }), +/* 642 */ +/***/ (function(module, exports) { - if (!state.inside.hasOwnProperty(type)) { - return false; - } +var toString = {}.toString; - return state.inside[type].length > 0; +module.exports = Array.isArray || function (arr) { + return toString.call(arr) == '[object Array]'; }; -/** - * Returns true if `node` is either a child or grand-child of the given `type`, - * or `state.inside[type]` is a non-empty array. + +/***/ }), +/* 643 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +/*! + * has-values * - * ```js - * var state = { inside: {}}; - * var node = new Node({type: 'brace'}); - * var open = new Node({type: 'brace.open'}); - * console.log(utils.isInside(state, open, 'brace')); //=> false - * utils.pushNode(node, open); - * console.log(utils.isInside(state, open, 'brace')); //=> true - * ``` - * @param {Object} `state` Either the `compiler.state` object, if it exists, or a user-supplied state object. - * @param {Object} `node` Instance of [snapdragon-node][] - * @param {String} `type` The `node.type` to check for. - * @return {Boolean} - * @api public + * Copyright (c) 2014-2015, Jon Schlinkert. + * Licensed under the MIT License. */ -utils.isInside = function(state, node, type) { - assert(utils.isNode(node), 'expected node to be an instance of Node'); - assert(isObject(state), 'expected state to be an object'); - if (Array.isArray(type)) { - for (var i = 0; i < type.length; i++) { - if (utils.isInside(state, node, type[i])) { - return true; - } - } + +module.exports = function hasValue(o, noZero) { + if (o === null || o === undefined) { return false; } - var parent = node.parent; - if (typeof type === 'string') { - return (parent && parent.type === type) || utils.isInsideType(state, type); + if (typeof o === 'boolean') { + return true; } - if (typeOf(type) === 'regexp') { - if (parent && parent.type && type.test(parent.type)) { - return true; + if (typeof o === 'number') { + if (o === 0 && noZero === true) { + return false; } + return true; + } - var keys = Object.keys(state.inside); - var len = keys.length; - var idx = -1; - while (++idx < len) { - var key = keys[idx]; - var val = state.inside[key]; + if (o.length !== undefined) { + return o.length !== 0; + } - if (Array.isArray(val) && val.length !== 0 && type.test(key)) { - return true; - } + for (var key in o) { + if (o.hasOwnProperty(key)) { + return true; } } return false; }; -/** - * Get the last `n` element from the given `array`. Used for getting - * a node from `node.nodes.` - * - * @param {Array} `array` - * @param {Number} `n` - * @return {undefined} - * @api public - */ -utils.last = function(arr, n) { - return arr[arr.length - (n || 1)]; -}; +/***/ }), +/* 644 */ +/***/ (function(module, exports, __webpack_require__) { -/** - * Cast the given `val` to an array. +"use strict"; +/*! + * has-value * - * ```js - * console.log(utils.arrayify('')); - * //=> [] - * console.log(utils.arrayify('foo')); - * //=> ['foo'] - * console.log(utils.arrayify(['foo'])); - * //=> ['foo'] - * ``` - * @param {any} `val` - * @return {Array} - * @api public + * Copyright (c) 2014-2017, Jon Schlinkert. + * Licensed under the MIT License. */ -utils.arrayify = function(val) { - if (typeof val === 'string' && val !== '') { - return [val]; - } - if (!Array.isArray(val)) { - return []; - } - return val; -}; - -/** - * Convert the given `val` to a string by joining with `,`. Useful - * for creating a cheerio/CSS/DOM-style selector from a list of strings. - * - * @param {any} `val` - * @return {Array} - * @api public - */ -utils.stringify = function(val) { - return utils.arrayify(val).join(','); -}; -/** - * Ensure that the given value is a string and call `.trim()` on it, - * or return an empty string. - * - * @param {String} `str` - * @return {String} - * @api public - */ +var isObject = __webpack_require__(587); +var hasValues = __webpack_require__(645); +var get = __webpack_require__(636); -utils.trim = function(str) { - return typeof str === 'string' ? str.trim() : ''; +module.exports = function(val, prop) { + return hasValues(isObject(val) && prop ? get(val, prop) : val); }; -/** - * Return true if val is an object - */ - -function isObject(val) { - return typeOf(val) === 'object'; -} - -/** - * Return true if val is a string - */ -function isString(val) { - return typeof val === 'string'; -} +/***/ }), +/* 645 */ +/***/ (function(module, exports, __webpack_require__) { -/** - * Return true if val is a function +"use strict"; +/*! + * has-values + * + * Copyright (c) 2014-2015, 2017, Jon Schlinkert. + * Released under the MIT License. */ -function isFunction(val) { - return typeof val === 'function'; -} -/** - * Return true if val is an array - */ -function isArray(val) { - return Array.isArray(val); -} +var typeOf = __webpack_require__(646); +var isNumber = __webpack_require__(609); -/** - * Shim to ensure the `.append` methods work with any version of snapdragon - */ +module.exports = function hasValue(val) { + // is-number checks for NaN and other edge cases + if (isNumber(val)) { + return true; + } -function append(compiler, val, node) { - if (typeof compiler.append !== 'function') { - return compiler.emit(val, node); + switch (typeOf(val)) { + case 'null': + case 'boolean': + case 'function': + return true; + case 'string': + case 'arguments': + return val.length !== 0; + case 'error': + return val.message !== ''; + case 'array': + var len = val.length; + if (len === 0) { + return false; + } + for (var i = 0; i < len; i++) { + if (hasValue(val[i])) { + return true; + } + } + return false; + case 'file': + case 'map': + case 'set': + return val.size !== 0; + case 'object': + var keys = Object.keys(val); + if (keys.length === 0) { + return false; + } + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + if (hasValue(val[key])) { + return true; + } + } + return false; + default: { + return false; + } } - return compiler.append(val, node); -} - -/** - * Simplified assertion. Throws an error is `val` is falsey. - */ - -function assert(val, message) { - if (!val) throw new Error(message); -} +}; /***/ }), -/* 615 */ +/* 646 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(605); +var isBuffer = __webpack_require__(611); var toString = Object.prototype.toString; /** @@ -69051,6 +73251,9 @@ module.exports = function kindOf(val) { if (type === '[object Error]') { return 'error'; } + if (type === '[object Promise]') { + return 'promise'; + } // buffer if (isBuffer(val)) { @@ -69093,1358 +73296,1050 @@ module.exports = function kindOf(val) { if (type === '[object Int32Array]') { return 'int32array'; } - if (type === '[object Uint32Array]') { - return 'uint32array'; - } - if (type === '[object Float32Array]') { - return 'float32array'; - } - if (type === '[object Float64Array]') { - return 'float64array'; - } - - // must be a plain object - return 'object'; -}; - - -/***/ }), -/* 616 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -var extend = __webpack_require__(594); -var Snapdragon = __webpack_require__(617); -var compilers = __webpack_require__(596); -var parsers = __webpack_require__(611); -var utils = __webpack_require__(597); - -/** - * Customize Snapdragon parser and renderer - */ - -function Braces(options) { - this.options = extend({}, options); -} - -/** - * Initialize braces - */ - -Braces.prototype.init = function(options) { - if (this.isInitialized) return; - this.isInitialized = true; - var opts = utils.createOptions({}, this.options, options); - this.snapdragon = this.options.snapdragon || new Snapdragon(opts); - this.compiler = this.snapdragon.compiler; - this.parser = this.snapdragon.parser; - - compilers(this.snapdragon, opts); - parsers(this.snapdragon, opts); - - /** - * Call Snapdragon `.parse` method. When AST is returned, we check to - * see if any unclosed braces are left on the stack and, if so, we iterate - * over the stack and correct the AST so that compilers are called in the correct - * order and unbalance braces are properly escaped. - */ - - utils.define(this.snapdragon, 'parse', function(pattern, options) { - var parsed = Snapdragon.prototype.parse.apply(this, arguments); - this.parser.ast.input = pattern; - - var stack = this.parser.stack; - while (stack.length) { - addParent({type: 'brace.close', val: ''}, stack.pop()); - } - - function addParent(node, parent) { - utils.define(node, 'parent', parent); - parent.nodes.push(node); - } - - // add non-enumerable parser reference - utils.define(parsed, 'parser', this.parser); - return parsed; - }); -}; - -/** - * Decorate `.parse` method - */ - -Braces.prototype.parse = function(ast, options) { - if (ast && typeof ast === 'object' && ast.nodes) return ast; - this.init(options); - return this.snapdragon.parse(ast, options); -}; - -/** - * Decorate `.compile` method - */ - -Braces.prototype.compile = function(ast, options) { - if (typeof ast === 'string') { - ast = this.parse(ast, options); - } else { - this.init(options); - } - return this.snapdragon.compile(ast, options); -}; - -/** - * Expand - */ - -Braces.prototype.expand = function(pattern) { - var ast = this.parse(pattern, {expand: true}); - return this.compile(ast, {expand: true}); -}; - -/** - * Optimize - */ - -Braces.prototype.optimize = function(pattern) { - var ast = this.parse(pattern, {optimize: true}); - return this.compile(ast, {optimize: true}); -}; - -/** - * Expose `Braces` - */ - -module.exports = Braces; - - -/***/ }), -/* 617 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -var Base = __webpack_require__(618); -var define = __webpack_require__(648); -var Compiler = __webpack_require__(659); -var Parser = __webpack_require__(682); -var utils = __webpack_require__(662); -var regexCache = {}; -var cache = {}; - -/** - * Create a new instance of `Snapdragon` with the given `options`. - * - * ```js - * var snapdragon = new Snapdragon(); - * ``` - * - * @param {Object} `options` - * @api public - */ - -function Snapdragon(options) { - Base.call(this, null, options); - this.options = utils.extend({source: 'string'}, this.options); - this.compiler = new Compiler(this.options); - this.parser = new Parser(this.options); - - Object.defineProperty(this, 'compilers', { - get: function() { - return this.compiler.compilers; - } - }); - - Object.defineProperty(this, 'parsers', { - get: function() { - return this.parser.parsers; - } - }); - - Object.defineProperty(this, 'regex', { - get: function() { - return this.parser.regex; - } - }); -} - -/** - * Inherit Base - */ - -Base.extend(Snapdragon); - -/** - * Add a parser to `snapdragon.parsers` for capturing the given `type` using - * the specified regex or parser function. A function is useful if you need - * to customize how the token is created and/or have access to the parser - * instance to check options, etc. - * - * ```js - * snapdragon - * .capture('slash', /^\//) - * .capture('dot', function() { - * var pos = this.position(); - * var m = this.match(/^\./); - * if (!m) return; - * return pos({ - * type: 'dot', - * val: m[0] - * }); - * }); - * ``` - * @param {String} `type` - * @param {RegExp|Function} `regex` - * @return {Object} Returns the parser instance for chaining - * @api public - */ - -Snapdragon.prototype.capture = function() { - return this.parser.capture.apply(this.parser, arguments); -}; - -/** - * Register a plugin `fn`. - * - * ```js - * var snapdragon = new Snapdgragon([options]); - * snapdragon.use(function() { - * console.log(this); //<= snapdragon instance - * console.log(this.parser); //<= parser instance - * console.log(this.compiler); //<= compiler instance - * }); - * ``` - * @param {Object} `fn` - * @api public - */ - -Snapdragon.prototype.use = function(fn) { - fn.call(this, this); - return this; -}; - -/** - * Parse the given `str`. - * - * ```js - * var snapdragon = new Snapdgragon([options]); - * // register parsers - * snapdragon.parser.use(function() {}); - * - * // parse - * var ast = snapdragon.parse('foo/bar'); - * console.log(ast); - * ``` - * @param {String} `str` - * @param {Object} `options` Set `options.sourcemap` to true to enable source maps. - * @return {Object} Returns an AST. - * @api public - */ - -Snapdragon.prototype.parse = function(str, options) { - this.options = utils.extend({}, this.options, options); - var parsed = this.parser.parse(str, this.options); - - // add non-enumerable parser reference - define(parsed, 'parser', this.parser); - return parsed; -}; - -/** - * Compile the given `AST`. - * - * ```js - * var snapdragon = new Snapdgragon([options]); - * // register plugins - * snapdragon.use(function() {}); - * // register parser plugins - * snapdragon.parser.use(function() {}); - * // register compiler plugins - * snapdragon.compiler.use(function() {}); - * - * // parse - * var ast = snapdragon.parse('foo/bar'); - * - * // compile - * var res = snapdragon.compile(ast); - * console.log(res.output); - * ``` - * @param {Object} `ast` - * @param {Object} `options` - * @return {Object} Returns an object with an `output` property with the rendered string. - * @api public - */ - -Snapdragon.prototype.compile = function(ast, options) { - this.options = utils.extend({}, this.options, options); - var compiled = this.compiler.compile(ast, this.options); - - // add non-enumerable compiler reference - define(compiled, 'compiler', this.compiler); - return compiled; -}; - -/** - * Expose `Snapdragon` - */ - -module.exports = Snapdragon; - -/** - * Expose `Parser` and `Compiler` - */ - -module.exports.Compiler = Compiler; -module.exports.Parser = Parser; - - -/***/ }), -/* 618 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -var util = __webpack_require__(113); -var define = __webpack_require__(619); -var CacheBase = __webpack_require__(620); -var Emitter = __webpack_require__(621); -var isObject = __webpack_require__(581); -var merge = __webpack_require__(642); -var pascal = __webpack_require__(645); -var cu = __webpack_require__(646); - -/** - * Optionally define a custom `cache` namespace to use. - */ - -function namespace(name) { - var Cache = name ? CacheBase.namespace(name) : CacheBase; - var fns = []; - - /** - * Create an instance of `Base` with the given `config` and `options`. - * - * ```js - * // initialize with `config` and `options` - * var app = new Base({isApp: true}, {abc: true}); - * app.set('foo', 'bar'); - * - * // values defined with the given `config` object will be on the root of the instance - * console.log(app.baz); //=> undefined - * console.log(app.foo); //=> 'bar' - * // or use `.get` - * console.log(app.get('isApp')); //=> true - * console.log(app.get('foo')); //=> 'bar' - * - * // values defined with the given `options` object will be on `app.options - * console.log(app.options.abc); //=> true - * ``` - * - * @param {Object} `config` If supplied, this object is passed to [cache-base][] to merge onto the the instance upon instantiation. - * @param {Object} `options` If supplied, this object is used to initialize the `base.options` object. - * @api public - */ - - function Base(config, options) { - if (!(this instanceof Base)) { - return new Base(config, options); - } - Cache.call(this, config); - this.is('base'); - this.initBase(config, options); - } - - /** - * Inherit cache-base - */ - - util.inherits(Base, Cache); - - /** - * Add static emitter methods - */ - - Emitter(Base); - - /** - * Initialize `Base` defaults with the given `config` object - */ - - Base.prototype.initBase = function(config, options) { - this.options = merge({}, this.options, options); - this.cache = this.cache || {}; - this.define('registered', {}); - if (name) this[name] = {}; - - // make `app._callbacks` non-enumerable - this.define('_callbacks', this._callbacks); - if (isObject(config)) { - this.visit('set', config); - } - Base.run(this, 'use', fns); - }; - - /** - * Set the given `name` on `app._name` and `app.is*` properties. Used for doing - * lookups in plugins. - * - * ```js - * app.is('foo'); - * console.log(app._name); - * //=> 'foo' - * console.log(app.isFoo); - * //=> true - * app.is('bar'); - * console.log(app.isFoo); - * //=> true - * console.log(app.isBar); - * //=> true - * console.log(app._name); - * //=> 'bar' - * ``` - * @name .is - * @param {String} `name` - * @return {Boolean} - * @api public - */ - - Base.prototype.is = function(name) { - if (typeof name !== 'string') { - throw new TypeError('expected name to be a string'); - } - this.define('is' + pascal(name), true); - this.define('_name', name); - this.define('_appname', name); - return this; - }; - - /** - * Returns true if a plugin has already been registered on an instance. - * - * Plugin implementors are encouraged to use this first thing in a plugin - * to prevent the plugin from being called more than once on the same - * instance. - * - * ```js - * var base = new Base(); - * base.use(function(app) { - * if (app.isRegistered('myPlugin')) return; - * // do stuff to `app` - * }); - * - * // to also record the plugin as being registered - * base.use(function(app) { - * if (app.isRegistered('myPlugin', true)) return; - * // do stuff to `app` - * }); - * ``` - * @name .isRegistered - * @emits `plugin` Emits the name of the plugin being registered. Useful for unit tests, to ensure plugins are only registered once. - * @param {String} `name` The plugin name. - * @param {Boolean} `register` If the plugin if not already registered, to record it as being registered pass `true` as the second argument. - * @return {Boolean} Returns true if a plugin is already registered. - * @api public - */ - - Base.prototype.isRegistered = function(name, register) { - if (this.registered.hasOwnProperty(name)) { - return true; - } - if (register !== false) { - this.registered[name] = true; - this.emit('plugin', name); - } - return false; - }; - - /** - * Define a plugin function to be called immediately upon init. Plugins are chainable - * and expose the following arguments to the plugin function: - * - * - `app`: the current instance of `Base` - * - `base`: the [first ancestor instance](#base) of `Base` - * - * ```js - * var app = new Base() - * .use(foo) - * .use(bar) - * .use(baz) - * ``` - * @name .use - * @param {Function} `fn` plugin function to call - * @return {Object} Returns the item instance for chaining. - * @api public - */ - - Base.prototype.use = function(fn) { - fn.call(this, this); - return this; - }; - - /** - * The `.define` method is used for adding non-enumerable property on the instance. - * Dot-notation is **not supported** with `define`. - * - * ```js - * // arbitrary `render` function using lodash `template` - * app.define('render', function(str, locals) { - * return _.template(str)(locals); - * }); - * ``` - * @name .define - * @param {String} `key` The name of the property to define. - * @param {any} `value` - * @return {Object} Returns the instance for chaining. - * @api public - */ + if (type === '[object Uint32Array]') { + return 'uint32array'; + } + if (type === '[object Float32Array]') { + return 'float32array'; + } + if (type === '[object Float64Array]') { + return 'float64array'; + } - Base.prototype.define = function(key, val) { - if (isObject(key)) { - return this.visit('define', key); - } - define(this, key, val); - return this; - }; + // must be a plain object + return 'object'; +}; - /** - * Mix property `key` onto the Base prototype. If base is inherited using - * `Base.extend` this method will be overridden by a new `mixin` method that will - * only add properties to the prototype of the inheriting application. - * - * ```js - * app.mixin('foo', function() { - * // do stuff - * }); - * ``` - * @name .mixin - * @param {String} `key` - * @param {Object|Array} `val` - * @return {Object} Returns the `base` instance for chaining. - * @api public - */ - Base.prototype.mixin = function(key, val) { - Base.prototype[key] = val; - return this; - }; +/***/ }), +/* 647 */ +/***/ (function(module, exports, __webpack_require__) { - /** - * Non-enumberable mixin array, used by the static [Base.mixin]() method. - */ +"use strict"; +/*! + * set-value + * + * Copyright (c) 2014-2015, 2017, Jon Schlinkert. + * Released under the MIT License. + */ - Base.prototype.mixins = Base.prototype.mixins || []; - /** - * Getter/setter used when creating nested instances of `Base`, for storing a reference - * to the first ancestor instance. This works by setting an instance of `Base` on the `parent` - * property of a "child" instance. The `base` property defaults to the current instance if - * no `parent` property is defined. - * - * ```js - * // create an instance of `Base`, this is our first ("base") instance - * var first = new Base(); - * first.foo = 'bar'; // arbitrary property, to make it easier to see what's happening later - * - * // create another instance - * var second = new Base(); - * // create a reference to the first instance (`first`) - * second.parent = first; - * - * // create another instance - * var third = new Base(); - * // create a reference to the previous instance (`second`) - * // repeat this pattern every time a "child" instance is created - * third.parent = second; - * - * // we can always access the first instance using the `base` property - * console.log(first.base.foo); - * //=> 'bar' - * console.log(second.base.foo); - * //=> 'bar' - * console.log(third.base.foo); - * //=> 'bar' - * // and now you know how to get to third base ;) - * ``` - * @name .base - * @api public - */ - Object.defineProperty(Base.prototype, 'base', { - configurable: true, - get: function() { - return this.parent ? this.parent.base : this; - } - }); +var split = __webpack_require__(604); +var extend = __webpack_require__(638); +var isPlainObject = __webpack_require__(594); +var isObject = __webpack_require__(634); - /** - * Static method for adding global plugin functions that will - * be added to an instance when created. - * - * ```js - * Base.use(function(app) { - * app.foo = 'bar'; - * }); - * var app = new Base(); - * console.log(app.foo); - * //=> 'bar' - * ``` - * @name #use - * @param {Function} `fn` Plugin function to use on each instance. - * @return {Object} Returns the `Base` constructor for chaining - * @api public - */ +module.exports = function(obj, prop, val) { + if (!isObject(obj)) { + return obj; + } - define(Base, 'use', function(fn) { - fns.push(fn); - return Base; - }); + if (Array.isArray(prop)) { + prop = [].concat.apply([], prop).join('.'); + } - /** - * Run an array of functions by passing each function - * to a method on the given object specified by the given property. - * - * @param {Object} `obj` Object containing method to use. - * @param {String} `prop` Name of the method on the object to use. - * @param {Array} `arr` Array of functions to pass to the method. - */ + if (typeof prop !== 'string') { + return obj; + } - define(Base, 'run', function(obj, prop, arr) { - var len = arr.length, i = 0; - while (len--) { - obj[prop](arr[i++]); + var keys = split(prop, {sep: '.', brackets: true}).filter(isValidKey); + var len = keys.length; + var idx = -1; + var current = obj; + + while (++idx < len) { + var key = keys[idx]; + if (idx !== len - 1) { + if (!isObject(current[key])) { + current[key] = {}; + } + current = current[key]; + continue; } - return Base; - }); - /** - * Static method for inheriting the prototype and static methods of the `Base` class. - * This method greatly simplifies the process of creating inheritance-based applications. - * See [static-extend][] for more details. - * - * ```js - * var extend = cu.extend(Parent); - * Parent.extend(Child); - * - * // optional methods - * Parent.extend(Child, { - * foo: function() {}, - * bar: function() {} - * }); - * ``` - * @name #extend - * @param {Function} `Ctor` constructor to extend - * @param {Object} `methods` Optional prototype properties to mix in. - * @return {Object} Returns the `Base` constructor for chaining - * @api public - */ + if (isPlainObject(current[key]) && isPlainObject(val)) { + current[key] = extend({}, current[key], val); + } else { + current[key] = val; + } + } - define(Base, 'extend', cu.extend(Base, function(Ctor, Parent) { - Ctor.prototype.mixins = Ctor.prototype.mixins || []; + return obj; +}; - define(Ctor, 'mixin', function(fn) { - var mixin = fn(Ctor.prototype, Ctor); - if (typeof mixin === 'function') { - Ctor.prototype.mixins.push(mixin); - } - return Ctor; - }); +function isValidKey(key) { + return key !== '__proto__' && key !== 'constructor' && key !== 'prototype'; +} - define(Ctor, 'mixins', function(Child) { - Base.run(Child, 'mixin', Ctor.prototype.mixins); - return Ctor; - }); - Ctor.prototype.mixin = function(key, value) { - Ctor.prototype[key] = value; - return this; - }; - return Base; - })); +/***/ }), +/* 648 */ +/***/ (function(module, exports, __webpack_require__) { - /** - * Used for adding methods to the `Base` prototype, and/or to the prototype of child instances. - * When a mixin function returns a function, the returned function is pushed onto the `.mixins` - * array, making it available to be used on inheriting classes whenever `Base.mixins()` is - * called (e.g. `Base.mixins(Child)`). - * - * ```js - * Base.mixin(function(proto) { - * proto.foo = function(msg) { - * return 'foo ' + msg; - * }; - * }); - * ``` - * @name #mixin - * @param {Function} `fn` Function to call - * @return {Object} Returns the `Base` constructor for chaining - * @api public - */ +"use strict"; - define(Base, 'mixin', function(fn) { - var mixin = fn(Base.prototype, Base); - if (typeof mixin === 'function') { - Base.prototype.mixins.push(mixin); + +var isExtendable = __webpack_require__(649); +var forIn = __webpack_require__(650); + +function mixinDeep(target, objects) { + var len = arguments.length, i = 0; + while (++i < len) { + var obj = arguments[i]; + if (isObject(obj)) { + forIn(obj, copy, target); } - return Base; - }); + } + return target; +} - /** - * Static method for running global mixin functions against a child constructor. - * Mixins must be registered before calling this method. - * - * ```js - * Base.extend(Child); - * Base.mixins(Child); - * ``` - * @name #mixins - * @param {Function} `Child` Constructor function of a child class - * @return {Object} Returns the `Base` constructor for chaining - * @api public - */ +/** + * Copy properties from the source object to the + * target object. + * + * @param {*} `val` + * @param {String} `key` + */ - define(Base, 'mixins', function(Child) { - Base.run(Child, 'mixin', Base.prototype.mixins); - return Base; - }); +function copy(val, key) { + if (!isValidKey(key)) { + return; + } - /** - * Similar to `util.inherit`, but copies all static properties, prototype properties, and - * getters/setters from `Provider` to `Receiver`. See [class-utils][]{#inherit} for more details. - * - * ```js - * Base.inherit(Foo, Bar); - * ``` - * @name #inherit - * @param {Function} `Receiver` Receiving (child) constructor - * @param {Function} `Provider` Providing (parent) constructor - * @return {Object} Returns the `Base` constructor for chaining - * @api public - */ + var obj = this[key]; + if (isObject(val) && isObject(obj)) { + mixinDeep(obj, val); + } else { + this[key] = val; + } +} - define(Base, 'inherit', cu.inherit); - define(Base, 'bubble', cu.bubble); - return Base; +/** + * Returns true if `val` is an object or function. + * + * @param {any} val + * @return {Boolean} + */ + +function isObject(val) { + return isExtendable(val) && !Array.isArray(val); } /** - * Expose `Base` with default settings + * Returns true if `key` is a valid key to use when extending objects. + * + * @param {String} `key` + * @return {Boolean} */ -module.exports = namespace(); +function isValidKey(key) { + return key !== '__proto__' && key !== 'constructor' && key !== 'prototype'; +}; /** - * Allow users to define a namespace + * Expose `mixinDeep` */ -module.exports.namespace = namespace; +module.exports = mixinDeep; /***/ }), -/* 619 */ +/* 649 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /*! - * define-property + * is-extendable * - * Copyright (c) 2015, 2017, Jon Schlinkert. + * Copyright (c) 2015-2017, Jon Schlinkert. * Released under the MIT License. */ -var isDescriptor = __webpack_require__(582); +var isPlainObject = __webpack_require__(594); -module.exports = function defineProperty(obj, prop, val) { - if (typeof obj !== 'object' && typeof obj !== 'function') { - throw new TypeError('expected an object or function.'); - } +module.exports = function isExtendable(val) { + return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); +}; - if (typeof prop !== 'string') { - throw new TypeError('expected `prop` to be a string.'); - } - if (isDescriptor(val) && ('set' in val || 'get' in val)) { - return Object.defineProperty(obj, prop, val); +/***/ }), +/* 650 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +/*! + * for-in + * + * Copyright (c) 2014-2017, Jon Schlinkert. + * Released under the MIT License. + */ + + + +module.exports = function forIn(obj, fn, thisArg) { + for (var key in obj) { + if (fn.call(thisArg, obj[key], key, obj) === false) { + break; + } } +}; - return Object.defineProperty(obj, prop, { - configurable: true, - enumerable: false, - writable: true, - value: val + +/***/ }), +/* 651 */ +/***/ (function(module, exports) { + +/*! + * pascalcase + * + * Copyright (c) 2015, Jon Schlinkert. + * Licensed under the MIT License. + */ + +function pascalcase(str) { + if (typeof str !== 'string') { + throw new TypeError('expected a string.'); + } + str = str.replace(/([A-Z])/g, ' $1'); + if (str.length === 1) { return str.toUpperCase(); } + str = str.replace(/^[\W_]+|[\W_]+$/g, '').toLowerCase(); + str = str.charAt(0).toUpperCase() + str.slice(1); + return str.replace(/[\W_]+(\w|$)/g, function (_, ch) { + return ch.toUpperCase(); }); -}; +} + +module.exports = pascalcase; /***/ }), -/* 620 */ +/* 652 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(581); -var Emitter = __webpack_require__(621); -var visit = __webpack_require__(622); -var toPath = __webpack_require__(625); -var union = __webpack_require__(627); -var del = __webpack_require__(633); -var get = __webpack_require__(630); -var has = __webpack_require__(638); -var set = __webpack_require__(641); +var util = __webpack_require__(113); +var utils = __webpack_require__(653); /** - * Create a `Cache` constructor that when instantiated will - * store values on the given `prop`. + * Expose class utils + */ + +var cu = module.exports; + +/** + * Expose class utils: `cu` + */ + +cu.isObject = function isObject(val) { + return utils.isObj(val) || typeof val === 'function'; +}; + +/** + * Returns true if an array has any of the given elements, or an + * object has any of the give keys. * * ```js - * var Cache = require('cache-base').namespace('data'); - * var cache = new Cache(); + * cu.has(['a', 'b', 'c'], 'c'); + * //=> true * - * cache.set('foo', 'bar'); - * //=> {data: {foo: 'bar'}} + * cu.has(['a', 'b', 'c'], ['c', 'z']); + * //=> true + * + * cu.has({a: 'b', c: 'd'}, ['c', 'z']); + * //=> true * ``` - * @param {String} `prop` The property name to use for storing values. - * @return {Function} Returns a custom `Cache` constructor + * @param {Object} `obj` + * @param {String|Array} `val` + * @return {Boolean} * @api public */ -function namespace(prop) { - - /** - * Create a new `Cache`. Internally the `Cache` constructor is created using - * the `namespace` function, with `cache` defined as the storage object. - * - * ```js - * var app = new Cache(); - * ``` - * @param {Object} `cache` Optionally pass an object to initialize with. - * @constructor - * @api public - */ - - function Cache(cache) { - if (prop) { - this[prop] = {}; - } - if (cache) { - this.set(cache); - } - } - - /** - * Inherit Emitter - */ - - Emitter(Cache.prototype); - - /** - * Assign `value` to `key`. Also emits `set` with - * the key and value. - * - * ```js - * app.on('set', function(key, val) { - * // do something when `set` is emitted - * }); - * - * app.set(key, value); - * - * // also takes an object or array - * app.set({name: 'Halle'}); - * app.set([{foo: 'bar'}, {baz: 'quux'}]); - * console.log(app); - * //=> {name: 'Halle', foo: 'bar', baz: 'quux'} - * ``` - * - * @name .set - * @emits `set` with `key` and `value` as arguments. - * @param {String} `key` - * @param {any} `value` - * @return {Object} Returns the instance for chaining. - * @api public - */ - - Cache.prototype.set = function(key, val) { - if (Array.isArray(key) && arguments.length === 2) { - key = toPath(key); - } - if (isObject(key) || Array.isArray(key)) { - this.visit('set', key); - } else { - set(prop ? this[prop] : this, key, val); - this.emit('set', key, val); - } - return this; - }; - - /** - * Union `array` to `key`. Also emits `set` with - * the key and value. - * - * ```js - * app.union('a.b', ['foo']); - * app.union('a.b', ['bar']); - * console.log(app.get('a')); - * //=> {b: ['foo', 'bar']} - * ``` - * @name .union - * @param {String} `key` - * @param {any} `value` - * @return {Object} Returns the instance for chaining. - * @api public - */ +cu.has = function has(obj, val) { + val = cu.arrayify(val); + var len = val.length; - Cache.prototype.union = function(key, val) { - if (Array.isArray(key) && arguments.length === 2) { - key = toPath(key); + if (cu.isObject(obj)) { + for (var key in obj) { + if (val.indexOf(key) > -1) { + return true; + } } - var ctx = prop ? this[prop] : this; - union(ctx, key, arrayify(val)); - this.emit('union', val); - return this; - }; - /** - * Return the value of `key`. Dot notation may be used - * to get [nested property values][get-value]. - * - * ```js - * app.set('a.b.c', 'd'); - * app.get('a.b'); - * //=> {c: 'd'} - * - * app.get(['a', 'b']); - * //=> {c: 'd'} - * ``` - * - * @name .get - * @emits `get` with `key` and `value` as arguments. - * @param {String} `key` The name of the property to get. Dot-notation may be used. - * @return {any} Returns the value of `key` - * @api public - */ + var keys = cu.nativeKeys(obj); + return cu.has(keys, val); + } - Cache.prototype.get = function(key) { - key = toPath(arguments); + if (Array.isArray(obj)) { + var arr = obj; + while (len--) { + if (arr.indexOf(val[len]) > -1) { + return true; + } + } + return false; + } - var ctx = prop ? this[prop] : this; - var val = get(ctx, key); + throw new TypeError('expected an array or object.'); +}; - this.emit('get', key, val); - return val; - }; +/** + * Returns true if an array or object has all of the given values. + * + * ```js + * cu.hasAll(['a', 'b', 'c'], 'c'); + * //=> true + * + * cu.hasAll(['a', 'b', 'c'], ['c', 'z']); + * //=> false + * + * cu.hasAll({a: 'b', c: 'd'}, ['c', 'z']); + * //=> false + * ``` + * @param {Object|Array} `val` + * @param {String|Array} `values` + * @return {Boolean} + * @api public + */ - /** - * Return true if app has a stored value for `key`, - * false only if value is `undefined`. - * - * ```js - * app.set('foo', 'bar'); - * app.has('foo'); - * //=> true - * ``` - * - * @name .has - * @emits `has` with `key` and true or false as arguments. - * @param {String} `key` - * @return {Boolean} - * @api public - */ +cu.hasAll = function hasAll(val, values) { + values = cu.arrayify(values); + var len = values.length; + while (len--) { + if (!cu.has(val, values[len])) { + return false; + } + } + return true; +}; - Cache.prototype.has = function(key) { - key = toPath(arguments); +/** + * Cast the given value to an array. + * + * ```js + * cu.arrayify('foo'); + * //=> ['foo'] + * + * cu.arrayify(['foo']); + * //=> ['foo'] + * ``` + * + * @param {String|Array} `val` + * @return {Array} + * @api public + */ - var ctx = prop ? this[prop] : this; - var val = get(ctx, key); +cu.arrayify = function arrayify(val) { + return val ? (Array.isArray(val) ? val : [val]) : []; +}; - var has = typeof val !== 'undefined'; - this.emit('has', key, has); - return has; - }; +/** + * Noop + */ - /** - * Delete one or more properties from the instance. - * - * ```js - * app.del(); // delete all - * // or - * app.del('foo'); - * // or - * app.del(['foo', 'bar']); - * ``` - * @name .del - * @emits `del` with the `key` as the only argument. - * @param {String|Array} `key` Property name or array of property names. - * @return {Object} Returns the instance for chaining. - * @api public - */ +cu.noop = function noop() { + return; +}; - Cache.prototype.del = function(key) { - if (Array.isArray(key)) { - this.visit('del', key); - } else { - del(prop ? this[prop] : this, key); - this.emit('del', key); - } - return this; - }; +/** + * Returns the first argument passed to the function. + */ - /** - * Reset the entire cache to an empty object. - * - * ```js - * app.clear(); - * ``` - * @api public - */ +cu.identity = function identity(val) { + return val; +}; - Cache.prototype.clear = function() { - if (prop) { - this[prop] = {}; - } - }; +/** + * Returns true if a value has a `contructor` + * + * ```js + * cu.hasConstructor({}); + * //=> true + * + * cu.hasConstructor(Object.create(null)); + * //=> false + * ``` + * @param {Object} `value` + * @return {Boolean} + * @api public + */ - /** - * Visit `method` over the properties in the given object, or map - * visit over the object-elements in an array. - * - * @name .visit - * @param {String} `method` The name of the `base` method to call. - * @param {Object|Array} `val` The object or array to iterate over. - * @return {Object} Returns the instance for chaining. - * @api public - */ +cu.hasConstructor = function hasConstructor(val) { + return cu.isObject(val) && typeof val.constructor !== 'undefined'; +}; - Cache.prototype.visit = function(method, val) { - visit(this, method, val); - return this; - }; +/** + * Get the native `ownPropertyNames` from the constructor of the + * given `object`. An empty array is returned if the object does + * not have a constructor. + * + * ```js + * cu.nativeKeys({a: 'b', b: 'c', c: 'd'}) + * //=> ['a', 'b', 'c'] + * + * cu.nativeKeys(function(){}) + * //=> ['length', 'caller'] + * ``` + * + * @param {Object} `obj` Object that has a `constructor`. + * @return {Array} Array of keys. + * @api public + */ - return Cache; -} +cu.nativeKeys = function nativeKeys(val) { + if (!cu.hasConstructor(val)) return []; + return Object.getOwnPropertyNames(val); +}; /** - * Cast val to an array + * Returns property descriptor `key` if it's an "own" property + * of the given object. + * + * ```js + * function App() {} + * Object.defineProperty(App.prototype, 'count', { + * get: function() { + * return Object.keys(this).length; + * } + * }); + * cu.getDescriptor(App.prototype, 'count'); + * // returns: + * // { + * // get: [Function], + * // set: undefined, + * // enumerable: false, + * // configurable: false + * // } + * ``` + * + * @param {Object} `obj` + * @param {String} `key` + * @return {Object} Returns descriptor `key` + * @api public */ -function arrayify(val) { - return val ? (Array.isArray(val) ? val : [val]) : []; -} +cu.getDescriptor = function getDescriptor(obj, key) { + if (!cu.isObject(obj)) { + throw new TypeError('expected an object.'); + } + if (typeof key !== 'string') { + throw new TypeError('expected key to be a string.'); + } + return Object.getOwnPropertyDescriptor(obj, key); +}; /** - * Expose `Cache` + * Copy a descriptor from one object to another. + * + * ```js + * function App() {} + * Object.defineProperty(App.prototype, 'count', { + * get: function() { + * return Object.keys(this).length; + * } + * }); + * var obj = {}; + * cu.copyDescriptor(obj, App.prototype, 'count'); + * ``` + * @param {Object} `receiver` + * @param {Object} `provider` + * @param {String} `name` + * @return {Object} + * @api public */ -module.exports = namespace(); +cu.copyDescriptor = function copyDescriptor(receiver, provider, name) { + if (!cu.isObject(receiver)) { + throw new TypeError('expected receiving object to be an object.'); + } + if (!cu.isObject(provider)) { + throw new TypeError('expected providing object to be an object.'); + } + if (typeof name !== 'string') { + throw new TypeError('expected name to be a string.'); + } + + var val = cu.getDescriptor(provider, name); + if (val) Object.defineProperty(receiver, name, val); +}; /** - * Expose `Cache.namespace` + * Copy static properties, prototype properties, and descriptors + * from one object to another. + * + * @param {Object} `receiver` + * @param {Object} `provider` + * @param {String|Array} `omit` One or more properties to omit + * @return {Object} + * @api public */ -module.exports.namespace = namespace; +cu.copy = function copy(receiver, provider, omit) { + if (!cu.isObject(receiver)) { + throw new TypeError('expected receiving object to be an object.'); + } + if (!cu.isObject(provider)) { + throw new TypeError('expected providing object to be an object.'); + } + var props = Object.getOwnPropertyNames(provider); + var keys = Object.keys(provider); + var len = props.length, + key; + omit = cu.arrayify(omit); + while (len--) { + key = props[len]; -/***/ }), -/* 621 */ -/***/ (function(module, exports, __webpack_require__) { + if (cu.has(keys, key)) { + utils.define(receiver, key, provider[key]); + } else if (!(key in receiver) && !cu.has(omit, key)) { + cu.copyDescriptor(receiver, provider, key); + } + } +}; - -/** - * Expose `Emitter`. - */ - -if (true) { - module.exports = Emitter; -} - -/** - * Initialize a new `Emitter`. - * - * @api public - */ - -function Emitter(obj) { - if (obj) return mixin(obj); -}; - -/** - * Mixin the emitter properties. - * - * @param {Object} obj - * @return {Object} - * @api private - */ - -function mixin(obj) { - for (var key in Emitter.prototype) { - obj[key] = Emitter.prototype[key]; - } - return obj; -} - -/** - * Listen on the given `event` with `fn`. - * - * @param {String} event - * @param {Function} fn - * @return {Emitter} - * @api public - */ - -Emitter.prototype.on = -Emitter.prototype.addEventListener = function(event, fn){ - this._callbacks = this._callbacks || {}; - (this._callbacks['$' + event] = this._callbacks['$' + event] || []) - .push(fn); - return this; -}; - -/** - * Adds an `event` listener that will be invoked a single - * time then automatically removed. - * - * @param {String} event - * @param {Function} fn - * @return {Emitter} - * @api public - */ - -Emitter.prototype.once = function(event, fn){ - function on() { - this.off(event, on); - fn.apply(this, arguments); - } - - on.fn = fn; - this.on(event, on); - return this; -}; - -/** - * Remove the given callback for `event` or all - * registered callbacks. - * - * @param {String} event - * @param {Function} fn - * @return {Emitter} - * @api public - */ - -Emitter.prototype.off = -Emitter.prototype.removeListener = -Emitter.prototype.removeAllListeners = -Emitter.prototype.removeEventListener = function(event, fn){ - this._callbacks = this._callbacks || {}; - - // all - if (0 == arguments.length) { - this._callbacks = {}; - return this; - } - - // specific event - var callbacks = this._callbacks['$' + event]; - if (!callbacks) return this; - - // remove all handlers - if (1 == arguments.length) { - delete this._callbacks['$' + event]; - return this; - } - - // remove specific handler - var cb; - for (var i = 0; i < callbacks.length; i++) { - cb = callbacks[i]; - if (cb === fn || cb.fn === fn) { - callbacks.splice(i, 1); - break; - } - } - return this; -}; - -/** - * Emit `event` with the given args. - * - * @param {String} event - * @param {Mixed} ... - * @return {Emitter} - */ - -Emitter.prototype.emit = function(event){ - this._callbacks = this._callbacks || {}; - var args = [].slice.call(arguments, 1) - , callbacks = this._callbacks['$' + event]; - - if (callbacks) { - callbacks = callbacks.slice(0); - for (var i = 0, len = callbacks.length; i < len; ++i) { - callbacks[i].apply(this, args); - } - } - - return this; -}; - -/** - * Return array of callbacks for `event`. - * - * @param {String} event - * @return {Array} - * @api public - */ - -Emitter.prototype.listeners = function(event){ - this._callbacks = this._callbacks || {}; - return this._callbacks['$' + event] || []; -}; - -/** - * Check if this emitter has `event` handlers. - * - * @param {String} event - * @return {Boolean} - * @api public - */ - -Emitter.prototype.hasListeners = function(event){ - return !! this.listeners(event).length; -}; +/** + * Inherit the static properties, prototype properties, and descriptors + * from of an object. + * + * @param {Object} `receiver` + * @param {Object} `provider` + * @param {String|Array} `omit` One or more properties to omit + * @return {Object} + * @api public + */ + +cu.inherit = function inherit(receiver, provider, omit) { + if (!cu.isObject(receiver)) { + throw new TypeError('expected receiving object to be an object.'); + } + if (!cu.isObject(provider)) { + throw new TypeError('expected providing object to be an object.'); + } + + var keys = []; + for (var key in provider) { + keys.push(key); + receiver[key] = provider[key]; + } + + keys = keys.concat(cu.arrayify(omit)); + + var a = provider.prototype || provider; + var b = receiver.prototype || receiver; + cu.copy(b, a, keys); +}; + +/** + * Returns a function for extending the static properties, + * prototype properties, and descriptors from the `Parent` + * constructor onto `Child` constructors. + * + * ```js + * var extend = cu.extend(Parent); + * Parent.extend(Child); + * + * // optional methods + * Parent.extend(Child, { + * foo: function() {}, + * bar: function() {} + * }); + * ``` + * @param {Function} `Parent` Parent ctor + * @param {Function} `extend` Optional extend function to handle custom extensions. Useful when updating methods that require a specific prototype. + * @param {Function} `Child` Child ctor + * @param {Object} `proto` Optionally pass additional prototype properties to inherit. + * @return {Object} + * @api public + */ + +cu.extend = function() { + // keep it lazy, instead of assigning to `cu.extend` + return utils.staticExtend.apply(null, arguments); +}; + +/** + * Bubble up events emitted from static methods on the Parent ctor. + * + * @param {Object} `Parent` + * @param {Array} `events` Event names to bubble up + * @api public + */ + +cu.bubble = function(Parent, events) { + events = events || []; + Parent.bubble = function(Child, arr) { + if (Array.isArray(arr)) { + events = utils.union([], events, arr); + } + var len = events.length; + var idx = -1; + while (++idx < len) { + var name = events[idx]; + Parent.on(name, Child.emit.bind(Child, name)); + } + cu.bubble(Child, events); + }; +}; /***/ }), -/* 622 */ +/* 653 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var utils = {}; + + + +/** + * Lazily required module dependencies + */ + +utils.union = __webpack_require__(635); +utils.define = __webpack_require__(654); +utils.isObj = __webpack_require__(587); +utils.staticExtend = __webpack_require__(661); + + +/** + * Expose `utils` + */ + +module.exports = utils; + + +/***/ }), +/* 654 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /*! - * collection-visit + * define-property * - * Copyright (c) 2015, 2017, Jon Schlinkert. - * Released under the MIT License. + * Copyright (c) 2015, Jon Schlinkert. + * Licensed under the MIT License. */ -var visit = __webpack_require__(623); -var mapVisit = __webpack_require__(624); +var isDescriptor = __webpack_require__(655); -module.exports = function(collection, method, val) { - var result; +module.exports = function defineProperty(obj, prop, val) { + if (typeof obj !== 'object' && typeof obj !== 'function') { + throw new TypeError('expected an object or function.'); + } - if (typeof val === 'string' && (method in collection)) { - var args = [].slice.call(arguments, 2); - result = collection[method].apply(collection, args); - } else if (Array.isArray(val)) { - result = mapVisit.apply(null, arguments); - } else { - result = visit.apply(null, arguments); + if (typeof prop !== 'string') { + throw new TypeError('expected `prop` to be a string.'); } - if (typeof result !== 'undefined') { - return result; + if (isDescriptor(val) && ('set' in val || 'get' in val)) { + return Object.defineProperty(obj, prop, val); } - return collection; + return Object.defineProperty(obj, prop, { + configurable: true, + enumerable: false, + writable: true, + value: val + }); }; /***/ }), -/* 623 */ +/* 655 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /*! - * object-visit + * is-descriptor * - * Copyright (c) 2015, 2017, Jon Schlinkert. + * Copyright (c) 2015-2017, Jon Schlinkert. * Released under the MIT License. */ -var isObject = __webpack_require__(581); +var typeOf = __webpack_require__(656); +var isAccessor = __webpack_require__(657); +var isData = __webpack_require__(659); -module.exports = function visit(thisArg, method, target, val) { - if (!isObject(thisArg) && typeof thisArg !== 'function') { - throw new Error('object-visit expects `thisArg` to be an object.'); +module.exports = function isDescriptor(obj, key) { + if (typeOf(obj) !== 'object') { + return false; + } + if ('get' in obj) { + return isAccessor(obj, key); } + return isData(obj, key); +}; - if (typeof method !== 'string') { - throw new Error('object-visit expects `method` name to be a string'); + +/***/ }), +/* 656 */ +/***/ (function(module, exports) { + +var toString = Object.prototype.toString; + +/** + * Get the native `typeof` a value. + * + * @param {*} `val` + * @return {*} Native javascript type + */ + +module.exports = function kindOf(val) { + var type = typeof val; + + // primitivies + if (type === 'undefined') { + return 'undefined'; + } + if (val === null) { + return 'null'; + } + if (val === true || val === false || val instanceof Boolean) { + return 'boolean'; + } + if (type === 'string' || val instanceof String) { + return 'string'; + } + if (type === 'number' || val instanceof Number) { + return 'number'; } - if (typeof thisArg[method] !== 'function') { - return thisArg; + // functions + if (type === 'function' || val instanceof Function) { + if (typeof val.constructor.name !== 'undefined' && val.constructor.name.slice(0, 9) === 'Generator') { + return 'generatorfunction'; + } + return 'function'; } - var args = [].slice.call(arguments, 3); - target = target || {}; + // array + if (typeof Array.isArray !== 'undefined' && Array.isArray(val)) { + return 'array'; + } - for (var key in target) { - var arr = [key, target[key]].concat(args); - thisArg[method].apply(thisArg, arr); + // check for instances of RegExp and Date before calling `toString` + if (val instanceof RegExp) { + return 'regexp'; } - return thisArg; + if (val instanceof Date) { + return 'date'; + } + + // other objects + type = toString.call(val); + + if (type === '[object RegExp]') { + return 'regexp'; + } + if (type === '[object Date]') { + return 'date'; + } + if (type === '[object Arguments]') { + return 'arguments'; + } + if (type === '[object Error]') { + return 'error'; + } + if (type === '[object Promise]') { + return 'promise'; + } + + // buffer + if (isBuffer(val)) { + return 'buffer'; + } + + // es6: Map, WeakMap, Set, WeakSet + if (type === '[object Set]') { + return 'set'; + } + if (type === '[object WeakSet]') { + return 'weakset'; + } + if (type === '[object Map]') { + return 'map'; + } + if (type === '[object WeakMap]') { + return 'weakmap'; + } + if (type === '[object Symbol]') { + return 'symbol'; + } + + if (type === '[object Map Iterator]') { + return 'mapiterator'; + } + if (type === '[object Set Iterator]') { + return 'setiterator'; + } + if (type === '[object String Iterator]') { + return 'stringiterator'; + } + if (type === '[object Array Iterator]') { + return 'arrayiterator'; + } + + // typed arrays + if (type === '[object Int8Array]') { + return 'int8array'; + } + if (type === '[object Uint8Array]') { + return 'uint8array'; + } + if (type === '[object Uint8ClampedArray]') { + return 'uint8clampedarray'; + } + if (type === '[object Int16Array]') { + return 'int16array'; + } + if (type === '[object Uint16Array]') { + return 'uint16array'; + } + if (type === '[object Int32Array]') { + return 'int32array'; + } + if (type === '[object Uint32Array]') { + return 'uint32array'; + } + if (type === '[object Float32Array]') { + return 'float32array'; + } + if (type === '[object Float64Array]') { + return 'float64array'; + } + + // must be a plain object + return 'object'; }; +/** + * If you need to support Safari 5-7 (8-10 yr-old browser), + * take a look at https://github.com/feross/is-buffer + */ + +function isBuffer(val) { + return val.constructor + && typeof val.constructor.isBuffer === 'function' + && val.constructor.isBuffer(val); +} + /***/ }), -/* 624 */ +/* 657 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; +/*! + * is-accessor-descriptor + * + * Copyright (c) 2015, Jon Schlinkert. + * Licensed under the MIT License. + */ -var util = __webpack_require__(113); -var visit = __webpack_require__(623); + +var typeOf = __webpack_require__(658); + +// accessor descriptor properties +var accessor = { + get: 'function', + set: 'function', + configurable: 'boolean', + enumerable: 'boolean' +}; + +function isAccessorDescriptor(obj, prop) { + if (typeof prop === 'string') { + var val = Object.getOwnPropertyDescriptor(obj, prop); + return typeof val !== 'undefined'; + } + + if (typeOf(obj) !== 'object') { + return false; + } + + if (has(obj, 'value') || has(obj, 'writable')) { + return false; + } + + if (!has(obj, 'get') || typeof obj.get !== 'function') { + return false; + } + + // tldr: it's valid to have "set" be undefined + // "set" might be undefined if `Object.getOwnPropertyDescriptor` + // was used to get the value, and only `get` was defined by the user + if (has(obj, 'set') && typeof obj[key] !== 'function' && typeof obj[key] !== 'undefined') { + return false; + } + + for (var key in obj) { + if (!accessor.hasOwnProperty(key)) { + continue; + } + + if (typeOf(obj[key]) === accessor[key]) { + continue; + } + + if (typeof obj[key] !== 'undefined') { + return false; + } + } + return true; +} + +function has(obj, key) { + return {}.hasOwnProperty.call(obj, key); +} /** - * Map `visit` over an array of objects. + * Expose `isAccessorDescriptor` + */ + +module.exports = isAccessorDescriptor; + + +/***/ }), +/* 658 */ +/***/ (function(module, exports, __webpack_require__) { + +var isBuffer = __webpack_require__(611); +var toString = Object.prototype.toString; + +/** + * Get the native `typeof` a value. * - * @param {Object} `collection` The context in which to invoke `method` - * @param {String} `method` Name of the method to call on `collection` - * @param {Object} `arr` Array of objects. + * @param {*} `val` + * @return {*} Native javascript type */ -module.exports = function mapVisit(collection, method, val) { - if (isObject(val)) { - return visit.apply(null, arguments); +module.exports = function kindOf(val) { + // primitivies + if (typeof val === 'undefined') { + return 'undefined'; + } + if (val === null) { + return 'null'; + } + if (val === true || val === false || val instanceof Boolean) { + return 'boolean'; + } + if (typeof val === 'string' || val instanceof String) { + return 'string'; + } + if (typeof val === 'number' || val instanceof Number) { + return 'number'; } - if (!Array.isArray(val)) { - throw new TypeError('expected an array: ' + util.inspect(val)); + // functions + if (typeof val === 'function' || val instanceof Function) { + return 'function'; } - var args = [].slice.call(arguments, 3); + // array + if (typeof Array.isArray !== 'undefined' && Array.isArray(val)) { + return 'array'; + } - for (var i = 0; i < val.length; i++) { - var ele = val[i]; - if (isObject(ele)) { - visit.apply(null, [collection, method, ele].concat(args)); - } else { - collection[method].apply(collection, [ele].concat(args)); - } + // check for instances of RegExp and Date before calling `toString` + if (val instanceof RegExp) { + return 'regexp'; + } + if (val instanceof Date) { + return 'date'; } -}; -function isObject(val) { - return val && (typeof val === 'function' || (!Array.isArray(val) && typeof val === 'object')); -} + // other objects + var type = toString.call(val); + + if (type === '[object RegExp]') { + return 'regexp'; + } + if (type === '[object Date]') { + return 'date'; + } + if (type === '[object Arguments]') { + return 'arguments'; + } + if (type === '[object Error]') { + return 'error'; + } + + // buffer + if (isBuffer(val)) { + return 'buffer'; + } + + // es6: Map, WeakMap, Set, WeakSet + if (type === '[object Set]') { + return 'set'; + } + if (type === '[object WeakSet]') { + return 'weakset'; + } + if (type === '[object Map]') { + return 'map'; + } + if (type === '[object WeakMap]') { + return 'weakmap'; + } + if (type === '[object Symbol]') { + return 'symbol'; + } + + // typed arrays + if (type === '[object Int8Array]') { + return 'int8array'; + } + if (type === '[object Uint8Array]') { + return 'uint8array'; + } + if (type === '[object Uint8ClampedArray]') { + return 'uint8clampedarray'; + } + if (type === '[object Int16Array]') { + return 'int16array'; + } + if (type === '[object Uint16Array]') { + return 'uint16array'; + } + if (type === '[object Int32Array]') { + return 'int32array'; + } + if (type === '[object Uint32Array]') { + return 'uint32array'; + } + if (type === '[object Float32Array]') { + return 'float32array'; + } + if (type === '[object Float64Array]') { + return 'float64array'; + } + + // must be a plain object + return 'object'; +}; /***/ }), -/* 625 */ +/* 659 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /*! - * to-object-path + * is-data-descriptor * * Copyright (c) 2015, Jon Schlinkert. * Licensed under the MIT License. @@ -70452,37 +74347,59 @@ function isObject(val) { -var typeOf = __webpack_require__(626); +var typeOf = __webpack_require__(660); + +// data descriptor properties +var data = { + configurable: 'boolean', + enumerable: 'boolean', + writable: 'boolean' +}; + +function isDataDescriptor(obj, prop) { + if (typeOf(obj) !== 'object') { + return false; + } + + if (typeof prop === 'string') { + var val = Object.getOwnPropertyDescriptor(obj, prop); + return typeof val !== 'undefined'; + } + + if (!('value' in obj) && !('writable' in obj)) { + return false; + } + + for (var key in obj) { + if (key === 'value') continue; -module.exports = function toPath(args) { - if (typeOf(args) !== 'arguments') { - args = arguments; - } - return filter(args).join('.'); -}; + if (!data.hasOwnProperty(key)) { + continue; + } -function filter(arr) { - var len = arr.length; - var idx = -1; - var res = []; + if (typeOf(obj[key]) === data[key]) { + continue; + } - while (++idx < len) { - var ele = arr[idx]; - if (typeOf(ele) === 'arguments' || Array.isArray(ele)) { - res.push.apply(res, filter(ele)); - } else if (typeof ele === 'string') { - res.push(ele); + if (typeof obj[key] !== 'undefined') { + return false; } } - return res; + return true; } +/** + * Expose `isDataDescriptor` + */ + +module.exports = isDataDescriptor; + /***/ }), -/* 626 */ +/* 660 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(605); +var isBuffer = __webpack_require__(611); var toString = Object.prototype.toString; /** @@ -70601,491 +74518,288 @@ module.exports = function kindOf(val) { /***/ }), -/* 627 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -var isObject = __webpack_require__(628); -var union = __webpack_require__(629); -var get = __webpack_require__(630); -var set = __webpack_require__(631); - -module.exports = function unionValue(obj, prop, value) { - if (!isObject(obj)) { - throw new TypeError('union-value expects the first argument to be an object.'); - } - - if (typeof prop !== 'string') { - throw new TypeError('union-value expects `prop` to be a string.'); - } - - var arr = arrayify(get(obj, prop)); - set(obj, prop, union(arr, arrayify(value))); - return obj; -}; - -function arrayify(val) { - if (val === null || typeof val === 'undefined') { - return []; - } - if (Array.isArray(val)) { - return val; - } - return [val]; -} - - -/***/ }), -/* 628 */ +/* 661 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /*! - * is-extendable + * static-extend * - * Copyright (c) 2015, Jon Schlinkert. + * Copyright (c) 2016, Jon Schlinkert. * Licensed under the MIT License. */ -module.exports = function isExtendable(val) { - return typeof val !== 'undefined' && val !== null - && (typeof val === 'object' || typeof val === 'function'); -}; - - -/***/ }), -/* 629 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -module.exports = function union(init) { - if (!Array.isArray(init)) { - throw new TypeError('arr-union expects the first argument to be an array.'); - } - - var len = arguments.length; - var i = 0; - - while (++i < len) { - var arg = arguments[i]; - if (!arg) continue; - - if (!Array.isArray(arg)) { - arg = [arg]; - } - - for (var j = 0; j < arg.length; j++) { - var ele = arg[j]; - - if (init.indexOf(ele) >= 0) { - continue; - } - init.push(ele); - } - } - return init; -}; - - -/***/ }), -/* 630 */ -/***/ (function(module, exports) { +var copy = __webpack_require__(662); +var define = __webpack_require__(654); +var util = __webpack_require__(113); -/*! - * get-value +/** + * Returns a function for extending the static properties, + * prototype properties, and descriptors from the `Parent` + * constructor onto `Child` constructors. * - * Copyright (c) 2014-2015, Jon Schlinkert. - * Licensed under the MIT License. - */ - -module.exports = function(obj, prop, a, b, c) { - if (!isObject(obj) || !prop) { - return obj; - } - - prop = toString(prop); - - // allowing for multiple properties to be passed as - // a string or array, but much faster (3-4x) than doing - // `[].slice.call(arguments)` - if (a) prop += '.' + toString(a); - if (b) prop += '.' + toString(b); - if (c) prop += '.' + toString(c); - - if (prop in obj) { - return obj[prop]; - } - - var segs = prop.split('.'); - var len = segs.length; - var i = -1; - - while (obj && (++i < len)) { - var key = segs[i]; - while (key[key.length - 1] === '\\') { - key = key.slice(0, -1) + '.' + segs[++i]; - } - obj = obj[key]; - } - return obj; -}; - -function isObject(val) { - return val !== null && (typeof val === 'object' || typeof val === 'function'); -} - -function toString(val) { - if (!val) return ''; - if (Array.isArray(val)) { - return val.join('.'); - } - return val; -} - - -/***/ }), -/* 631 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/*! - * set-value + * ```js + * var extend = require('static-extend'); + * Parent.extend = extend(Parent); * - * Copyright (c) 2014-2015, 2017, Jon Schlinkert. - * Released under the MIT License. + * // optionally pass a custom merge function as the second arg + * Parent.extend = extend(Parent, function(Child) { + * Child.prototype.mixin = function(key, val) { + * Child.prototype[key] = val; + * }; + * }); + * + * // extend "child" constructors + * Parent.extend(Child); + * + * // optionally define prototype methods as the second arg + * Parent.extend(Child, { + * foo: function() {}, + * bar: function() {} + * }); + * ``` + * @param {Function} `Parent` Parent ctor + * @param {Function} `extendFn` Optional extend function for handling any necessary custom merging. Useful when updating methods that require a specific prototype. + * @param {Function} `Child` Child ctor + * @param {Object} `proto` Optionally pass additional prototype properties to inherit. + * @return {Object} + * @api public */ - - -var split = __webpack_require__(598); -var extend = __webpack_require__(632); -var isPlainObject = __webpack_require__(588); -var isObject = __webpack_require__(628); - -module.exports = function(obj, prop, val) { - if (!isObject(obj)) { - return obj; +function extend(Parent, extendFn) { + if (typeof Parent !== 'function') { + throw new TypeError('expected Parent to be a function.'); } - if (Array.isArray(prop)) { - prop = [].concat.apply([], prop).join('.'); - } + return function(Ctor, proto) { + if (typeof Ctor !== 'function') { + throw new TypeError('expected Ctor to be a function.'); + } - if (typeof prop !== 'string') { - return obj; - } + util.inherits(Ctor, Parent); + copy(Ctor, Parent); - var keys = split(prop, {sep: '.', brackets: true}).filter(isValidKey); - var len = keys.length; - var idx = -1; - var current = obj; + // proto can be null or a plain object + if (typeof proto === 'object') { + var obj = Object.create(proto); - while (++idx < len) { - var key = keys[idx]; - if (idx !== len - 1) { - if (!isObject(current[key])) { - current[key] = {}; + for (var k in obj) { + Ctor.prototype[k] = obj[k]; } - current = current[key]; - continue; - } - - if (isPlainObject(current[key]) && isPlainObject(val)) { - current[key] = extend({}, current[key], val); - } else { - current[key] = val; } - } - - return obj; -}; - -function isValidKey(key) { - return key !== '__proto__' && key !== 'constructor' && key !== 'prototype'; -} - - -/***/ }), -/* 632 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -var isObject = __webpack_require__(628); - -module.exports = function extend(o/*, objects*/) { - if (!isObject(o)) { o = {}; } - - var len = arguments.length; - for (var i = 1; i < len; i++) { - var obj = arguments[i]; + // keep a reference to the parent prototype + define(Ctor.prototype, '_parent_', { + configurable: true, + set: function() {}, + get: function() { + return Parent.prototype; + } + }); - if (isObject(obj)) { - assign(o, obj); + if (typeof extendFn === 'function') { + extendFn(Ctor, Parent); } - } - return o; -}; -function assign(a, b) { - for (var key in b) { - if (hasOwn(b, key)) { - a[key] = b[key]; - } - } -} + Ctor.extend = extend(Ctor, extendFn); + }; +}; /** - * Returns true if the given `key` is an own property of `obj`. + * Expose `extend` */ -function hasOwn(obj, key) { - return Object.prototype.hasOwnProperty.call(obj, key); -} +module.exports = extend; /***/ }), -/* 633 */ +/* 662 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -/*! - * unset-value - * - * Copyright (c) 2015, 2017, Jon Schlinkert. - * Released under the MIT License. - */ - - -var isObject = __webpack_require__(581); -var has = __webpack_require__(634); -module.exports = function unset(obj, prop) { - if (!isObject(obj)) { - throw new TypeError('expected an object.'); - } - if (obj.hasOwnProperty(prop)) { - delete obj[prop]; - return true; - } - - if (has(obj, prop)) { - var segs = prop.split('.'); - var last = segs.pop(); - while (segs.length && segs[segs.length - 1].slice(-1) === '\\') { - last = segs.pop().slice(0, -1) + '.' + last; - } - while (segs.length) obj = obj[prop = segs.shift()]; - return (delete obj[last]); - } - return true; -}; - - -/***/ }), -/* 634 */ -/***/ (function(module, exports, __webpack_require__) { +var typeOf = __webpack_require__(663); +var copyDescriptor = __webpack_require__(664); +var define = __webpack_require__(654); -"use strict"; -/*! - * has-value +/** + * Copy static properties, prototype properties, and descriptors from one object to another. * - * Copyright (c) 2014-2016, Jon Schlinkert. - * Licensed under the MIT License. + * ```js + * function App() {} + * var proto = App.prototype; + * App.prototype.set = function() {}; + * App.prototype.get = function() {}; + * + * var obj = {}; + * copy(obj, proto); + * ``` + * @param {Object} `receiver` + * @param {Object} `provider` + * @param {String|Array} `omit` One or more properties to omit + * @return {Object} + * @api public */ +function copy(receiver, provider, omit) { + if (!isObject(receiver)) { + throw new TypeError('expected receiving object to be an object.'); + } + if (!isObject(provider)) { + throw new TypeError('expected providing object to be an object.'); + } + var props = nativeKeys(provider); + var keys = Object.keys(provider); + var len = props.length; + omit = arrayify(omit); -var isObject = __webpack_require__(635); -var hasValues = __webpack_require__(637); -var get = __webpack_require__(630); + while (len--) { + var key = props[len]; -module.exports = function(obj, prop, noZero) { - if (isObject(obj)) { - return hasValues(get(obj, prop), noZero); + if (has(keys, key)) { + define(receiver, key, provider[key]); + } else if (!(key in receiver) && !has(omit, key)) { + copyDescriptor(receiver, provider, key); + } } - return hasValues(obj, prop); }; - -/***/ }), -/* 635 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/*! - * isobject - * - * Copyright (c) 2014-2015, Jon Schlinkert. - * Licensed under the MIT License. +/** + * Return true if the given value is an object or function */ +function isObject(val) { + return typeOf(val) === 'object' || typeof val === 'function'; +} - -var isArray = __webpack_require__(636); - -module.exports = function isObject(val) { - return val != null && typeof val === 'object' && isArray(val) === false; -}; - - -/***/ }), -/* 636 */ -/***/ (function(module, exports) { - -var toString = {}.toString; - -module.exports = Array.isArray || function (arr) { - return toString.call(arr) == '[object Array]'; -}; - - -/***/ }), -/* 637 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/*! - * has-values +/** + * Returns true if an array has any of the given elements, or an + * object has any of the give keys. * - * Copyright (c) 2014-2015, Jon Schlinkert. - * Licensed under the MIT License. + * ```js + * has(['a', 'b', 'c'], 'c'); + * //=> true + * + * has(['a', 'b', 'c'], ['c', 'z']); + * //=> true + * + * has({a: 'b', c: 'd'}, ['c', 'z']); + * //=> true + * ``` + * @param {Object} `obj` + * @param {String|Array} `val` + * @return {Boolean} */ +function has(obj, val) { + val = arrayify(val); + var len = val.length; - -module.exports = function hasValue(o, noZero) { - if (o === null || o === undefined) { - return false; - } - - if (typeof o === 'boolean') { - return true; - } - - if (typeof o === 'number') { - if (o === 0 && noZero === true) { - return false; + if (isObject(obj)) { + for (var key in obj) { + if (val.indexOf(key) > -1) { + return true; + } } - return true; - } - if (o.length !== undefined) { - return o.length !== 0; + var keys = nativeKeys(obj); + return has(keys, val); } - for (var key in o) { - if (o.hasOwnProperty(key)) { - return true; + if (Array.isArray(obj)) { + var arr = obj; + while (len--) { + if (arr.indexOf(val[len]) > -1) { + return true; + } } + return false; } - return false; -}; + throw new TypeError('expected an array or object.'); +} -/***/ }), -/* 638 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/*! - * has-value +/** + * Cast the given value to an array. * - * Copyright (c) 2014-2017, Jon Schlinkert. - * Licensed under the MIT License. - */ - - - -var isObject = __webpack_require__(581); -var hasValues = __webpack_require__(639); -var get = __webpack_require__(630); + * ```js + * arrayify('foo'); + * //=> ['foo'] + * + * arrayify(['foo']); + * //=> ['foo'] + * ``` + * + * @param {String|Array} `val` + * @return {Array} + */ -module.exports = function(val, prop) { - return hasValues(isObject(val) && prop ? get(val, prop) : val); -}; +function arrayify(val) { + return val ? (Array.isArray(val) ? val : [val]) : []; +} +/** + * Returns true if a value has a `contructor` + * + * ```js + * hasConstructor({}); + * //=> true + * + * hasConstructor(Object.create(null)); + * //=> false + * ``` + * @param {Object} `value` + * @return {Boolean} + */ -/***/ }), -/* 639 */ -/***/ (function(module, exports, __webpack_require__) { +function hasConstructor(val) { + return isObject(val) && typeof val.constructor !== 'undefined'; +} -"use strict"; -/*! - * has-values +/** + * Get the native `ownPropertyNames` from the constructor of the + * given `object`. An empty array is returned if the object does + * not have a constructor. * - * Copyright (c) 2014-2015, 2017, Jon Schlinkert. - * Released under the MIT License. + * ```js + * nativeKeys({a: 'b', b: 'c', c: 'd'}) + * //=> ['a', 'b', 'c'] + * + * nativeKeys(function(){}) + * //=> ['length', 'caller'] + * ``` + * + * @param {Object} `obj` Object that has a `constructor`. + * @return {Array} Array of keys. */ +function nativeKeys(val) { + if (!hasConstructor(val)) return []; + return Object.getOwnPropertyNames(val); +} +/** + * Expose `copy` + */ -var typeOf = __webpack_require__(640); -var isNumber = __webpack_require__(603); +module.exports = copy; -module.exports = function hasValue(val) { - // is-number checks for NaN and other edge cases - if (isNumber(val)) { - return true; - } +/** + * Expose `copy.has` for tests + */ - switch (typeOf(val)) { - case 'null': - case 'boolean': - case 'function': - return true; - case 'string': - case 'arguments': - return val.length !== 0; - case 'error': - return val.message !== ''; - case 'array': - var len = val.length; - if (len === 0) { - return false; - } - for (var i = 0; i < len; i++) { - if (hasValue(val[i])) { - return true; - } - } - return false; - case 'file': - case 'map': - case 'set': - return val.size !== 0; - case 'object': - var keys = Object.keys(val); - if (keys.length === 0) { - return false; - } - for (var i = 0; i < keys.length; i++) { - var key = keys[i]; - if (hasValue(val[key])) { - return true; - } - } - return false; - default: { - return false; - } - } -}; +module.exports.has = has; /***/ }), -/* 640 */ +/* 663 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(605); +var isBuffer = __webpack_require__(611); var toString = Object.prototype.toString; /** @@ -71146,9 +74860,6 @@ module.exports = function kindOf(val) { if (type === '[object Error]') { return 'error'; } - if (type === '[object Promise]') { - return 'promise'; - } // buffer if (isBuffer(val)) { @@ -71207,2681 +74918,3108 @@ module.exports = function kindOf(val) { /***/ }), -/* 641 */ +/* 664 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /*! - * set-value + * copy-descriptor * - * Copyright (c) 2014-2015, 2017, Jon Schlinkert. - * Released under the MIT License. + * Copyright (c) 2015, Jon Schlinkert. + * Licensed under the MIT License. */ -var split = __webpack_require__(598); -var extend = __webpack_require__(632); -var isPlainObject = __webpack_require__(588); -var isObject = __webpack_require__(628); +/** + * Copy a descriptor from one object to another. + * + * ```js + * function App() { + * this.cache = {}; + * } + * App.prototype.set = function(key, val) { + * this.cache[key] = val; + * return this; + * }; + * Object.defineProperty(App.prototype, 'count', { + * get: function() { + * return Object.keys(this.cache).length; + * } + * }); + * + * copy(App.prototype, 'count', 'len'); + * + * // create an instance + * var app = new App(); + * + * app.set('a', true); + * app.set('b', true); + * app.set('c', true); + * + * console.log(app.count); + * //=> 3 + * console.log(app.len); + * //=> 3 + * ``` + * @name copy + * @param {Object} `receiver` The target object + * @param {Object} `provider` The provider object + * @param {String} `from` The key to copy on provider. + * @param {String} `to` Optionally specify a new key name to use. + * @return {Object} + * @api public + */ -module.exports = function(obj, prop, val) { - if (!isObject(obj)) { - return obj; +module.exports = function copyDescriptor(receiver, provider, from, to) { + if (!isObject(provider) && typeof provider !== 'function') { + to = from; + from = provider; + provider = receiver; } - - if (Array.isArray(prop)) { - prop = [].concat.apply([], prop).join('.'); + if (!isObject(receiver) && typeof receiver !== 'function') { + throw new TypeError('expected the first argument to be an object'); } - - if (typeof prop !== 'string') { - return obj; + if (!isObject(provider) && typeof provider !== 'function') { + throw new TypeError('expected provider to be an object'); } - var keys = split(prop, {sep: '.', brackets: true}).filter(isValidKey); - var len = keys.length; - var idx = -1; - var current = obj; - - while (++idx < len) { - var key = keys[idx]; - if (idx !== len - 1) { - if (!isObject(current[key])) { - current[key] = {}; - } - current = current[key]; - continue; - } + if (typeof to !== 'string') { + to = from; + } + if (typeof from !== 'string') { + throw new TypeError('expected key to be a string'); + } - if (isPlainObject(current[key]) && isPlainObject(val)) { - current[key] = extend({}, current[key], val); - } else { - current[key] = val; - } + if (!(from in provider)) { + throw new Error('property "' + from + '" does not exist'); } - return obj; + var val = Object.getOwnPropertyDescriptor(provider, from); + if (val) Object.defineProperty(receiver, to, val); }; -function isValidKey(key) { - return key !== '__proto__' && key !== 'constructor' && key !== 'prototype'; +function isObject(val) { + return {}.toString.call(val) === '[object Object]'; } + /***/ }), -/* 642 */ +/* 665 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(643); -var forIn = __webpack_require__(644); +var use = __webpack_require__(666); +var define = __webpack_require__(654); +var debug = __webpack_require__(205)('snapdragon:compiler'); +var utils = __webpack_require__(668); -function mixinDeep(target, objects) { - var len = arguments.length, i = 0; - while (++i < len) { - var obj = arguments[i]; - if (isObject(obj)) { - forIn(obj, copy, target); - } - } - return target; +/** + * Create a new `Compiler` with the given `options`. + * @param {Object} `options` + */ + +function Compiler(options, state) { + debug('initializing', __filename); + this.options = utils.extend({source: 'string'}, options); + this.state = state || {}; + this.compilers = {}; + this.output = ''; + this.set('eos', function(node) { + return this.emit(node.val, node); + }); + this.set('noop', function(node) { + return this.emit(node.val, node); + }); + this.set('bos', function(node) { + return this.emit(node.val, node); + }); + use(this); } /** - * Copy properties from the source object to the - * target object. - * - * @param {*} `val` - * @param {String} `key` + * Prototype methods */ -function copy(val, key) { - if (!isValidKey(key)) { - return; - } +Compiler.prototype = { - var obj = this[key]; - if (isObject(val) && isObject(obj)) { - mixinDeep(obj, val); - } else { - this[key] = val; + /** + * Throw an error message with details including the cursor position. + * @param {String} `msg` Message to use in the Error. + */ + + error: function(msg, node) { + var pos = node.position || {start: {column: 0}}; + var message = this.options.source + ' column:' + pos.start.column + ': ' + msg; + + var err = new Error(message); + err.reason = msg; + err.column = pos.start.column; + err.source = this.pattern; + + if (this.options.silent) { + this.errors.push(err); + } else { + throw err; + } + }, + + /** + * Define a non-enumberable property on the `Compiler` instance. + * + * ```js + * compiler.define('foo', 'bar'); + * ``` + * @name .define + * @param {String} `key` propery name + * @param {any} `val` property value + * @return {Object} Returns the Compiler instance for chaining. + * @api public + */ + + define: function(key, val) { + define(this, key, val); + return this; + }, + + /** + * Emit `node.val` + */ + + emit: function(str, node) { + this.output += str; + return str; + }, + + /** + * Add a compiler `fn` with the given `name` + */ + + set: function(name, fn) { + this.compilers[name] = fn; + return this; + }, + + /** + * Get compiler `name`. + */ + + get: function(name) { + return this.compilers[name]; + }, + + /** + * Get the previous AST node. + */ + + prev: function(n) { + return this.ast.nodes[this.idx - (n || 1)] || { type: 'bos', val: '' }; + }, + + /** + * Get the next AST node. + */ + + next: function(n) { + return this.ast.nodes[this.idx + (n || 1)] || { type: 'eos', val: '' }; + }, + + /** + * Visit `node`. + */ + + visit: function(node, nodes, i) { + var fn = this.compilers[node.type]; + this.idx = i; + + if (typeof fn !== 'function') { + throw this.error('compiler "' + node.type + '" is not registered', node); + } + return fn.call(this, node, nodes, i); + }, + + /** + * Map visit over array of `nodes`. + */ + + mapVisit: function(nodes) { + if (!Array.isArray(nodes)) { + throw new TypeError('expected an array'); + } + var len = nodes.length; + var idx = -1; + while (++idx < len) { + this.visit(nodes[idx], nodes, idx); + } + return this; + }, + + /** + * Compile `ast`. + */ + + compile: function(ast, options) { + var opts = utils.extend({}, this.options, options); + this.ast = ast; + this.parsingErrors = this.ast.errors; + this.output = ''; + + // source map support + if (opts.sourcemap) { + var sourcemaps = __webpack_require__(687); + sourcemaps(this); + this.mapVisit(this.ast.nodes); + this.applySourceMaps(); + this.map = opts.sourcemap === 'generator' ? this.map : this.map.toJSON(); + return this; + } + + this.mapVisit(this.ast.nodes); + return this; } -} +}; /** - * Returns true if `val` is an object or function. + * Expose `Compiler` + */ + +module.exports = Compiler; + + +/***/ }), +/* 666 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +/*! + * use * - * @param {any} val - * @return {Boolean} + * Copyright (c) 2015, 2017, Jon Schlinkert. + * Released under the MIT License. */ -function isObject(val) { - return isExtendable(val) && !Array.isArray(val); -} -/** - * Returns true if `key` is a valid key to use when extending objects. - * - * @param {String} `key` - * @return {Boolean} - */ -function isValidKey(key) { - return key !== '__proto__' && key !== 'constructor' && key !== 'prototype'; +var utils = __webpack_require__(667); + +module.exports = function base(app, opts) { + if (!utils.isObject(app) && typeof app !== 'function') { + throw new TypeError('use: expect `app` be an object or function'); + } + + if (!utils.isObject(opts)) { + opts = {}; + } + + var prop = utils.isString(opts.prop) ? opts.prop : 'fns'; + if (!Array.isArray(app[prop])) { + utils.define(app, prop, []); + } + + /** + * Define a plugin function to be passed to use. The only + * parameter exposed to the plugin is `app`, the object or function. + * passed to `use(app)`. `app` is also exposed as `this` in plugins. + * + * Additionally, **if a plugin returns a function, the function will + * be pushed onto the `fns` array**, allowing the plugin to be + * called at a later point by the `run` method. + * + * ```js + * var use = require('use'); + * + * // define a plugin + * function foo(app) { + * // do stuff + * } + * + * var app = function(){}; + * use(app); + * + * // register plugins + * app.use(foo); + * app.use(bar); + * app.use(baz); + * ``` + * @name .use + * @param {Function} `fn` plugin function to call + * @api public + */ + + utils.define(app, 'use', use); + + /** + * Run all plugins on `fns`. Any plugin that returns a function + * when called by `use` is pushed onto the `fns` array. + * + * ```js + * var config = {}; + * app.run(config); + * ``` + * @name .run + * @param {Object} `value` Object to be modified by plugins. + * @return {Object} Returns the object passed to `run` + * @api public + */ + + utils.define(app, 'run', function(val) { + if (!utils.isObject(val)) return; + decorate(val); + + var self = this || app; + var fns = self[prop]; + var len = fns.length; + var idx = -1; + + while (++idx < len) { + val.use(fns[idx]); + } + return val; + }); + + /** + * Call plugin `fn`. If a function is returned push it into the + * `fns` array to be called by the `run` method. + */ + + function use(fn, options) { + if (typeof fn !== 'function') { + throw new TypeError('.use expects `fn` be a function'); + } + + var self = this || app; + if (typeof opts.fn === 'function') { + opts.fn.call(self, self, options); + } + + var plugin = fn.call(self, self); + if (typeof plugin === 'function') { + var fns = self[prop]; + fns.push(plugin); + } + return self; + } + + /** + * Ensure the `.use` method exists on `val` + */ + + function decorate(val) { + if (!val.use || !val.run) { + base(val); + } + } + + return app; }; -/** - * Expose `mixinDeep` - */ - -module.exports = mixinDeep; - /***/ }), -/* 643 */ +/* 667 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -/*! - * is-extendable - * - * Copyright (c) 2015-2017, Jon Schlinkert. - * Released under the MIT License. - */ - - -var isPlainObject = __webpack_require__(588); -module.exports = function isExtendable(val) { - return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); -}; +var utils = {}; -/***/ }), -/* 644 */ -/***/ (function(module, exports, __webpack_require__) { -"use strict"; -/*! - * for-in - * - * Copyright (c) 2014-2017, Jon Schlinkert. - * Released under the MIT License. +/** + * Lazily required module dependencies */ +utils.define = __webpack_require__(654); +utils.isObject = __webpack_require__(587); -module.exports = function forIn(obj, fn, thisArg) { - for (var key in obj) { - if (fn.call(thisArg, obj[key], key, obj) === false) { - break; - } - } +utils.isString = function(val) { + return val && typeof val === 'string'; }; - -/***/ }), -/* 645 */ -/***/ (function(module, exports) { - -/*! - * pascalcase - * - * Copyright (c) 2015, Jon Schlinkert. - * Licensed under the MIT License. +/** + * Expose `utils` modules */ -function pascalcase(str) { - if (typeof str !== 'string') { - throw new TypeError('expected a string.'); - } - str = str.replace(/([A-Z])/g, ' $1'); - if (str.length === 1) { return str.toUpperCase(); } - str = str.replace(/^[\W_]+|[\W_]+$/g, '').toLowerCase(); - str = str.charAt(0).toUpperCase() + str.slice(1); - return str.replace(/[\W_]+(\w|$)/g, function (_, ch) { - return ch.toUpperCase(); - }); -} - -module.exports = pascalcase; +module.exports = utils; /***/ }), -/* 646 */ +/* 668 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var util = __webpack_require__(113); -var utils = __webpack_require__(647); - -/** - * Expose class utils - */ - -var cu = module.exports; - /** - * Expose class utils: `cu` + * Module dependencies */ -cu.isObject = function isObject(val) { - return utils.isObj(val) || typeof val === 'function'; -}; +exports.extend = __webpack_require__(638); +exports.SourceMap = __webpack_require__(669); +exports.sourceMapResolve = __webpack_require__(680); /** - * Returns true if an array has any of the given elements, or an - * object has any of the give keys. - * - * ```js - * cu.has(['a', 'b', 'c'], 'c'); - * //=> true - * - * cu.has(['a', 'b', 'c'], ['c', 'z']); - * //=> true - * - * cu.has({a: 'b', c: 'd'}, ['c', 'z']); - * //=> true - * ``` - * @param {Object} `obj` - * @param {String|Array} `val` - * @return {Boolean} - * @api public + * Convert backslash in the given string to forward slashes */ -cu.has = function has(obj, val) { - val = cu.arrayify(val); - var len = val.length; - - if (cu.isObject(obj)) { - for (var key in obj) { - if (val.indexOf(key) > -1) { - return true; - } - } - - var keys = cu.nativeKeys(obj); - return cu.has(keys, val); - } - - if (Array.isArray(obj)) { - var arr = obj; - while (len--) { - if (arr.indexOf(val[len]) > -1) { - return true; - } - } - return false; - } - - throw new TypeError('expected an array or object.'); +exports.unixify = function(fp) { + return fp.split(/\\+/).join('/'); }; /** - * Returns true if an array or object has all of the given values. - * - * ```js - * cu.hasAll(['a', 'b', 'c'], 'c'); - * //=> true - * - * cu.hasAll(['a', 'b', 'c'], ['c', 'z']); - * //=> false + * Return true if `val` is a non-empty string * - * cu.hasAll({a: 'b', c: 'd'}, ['c', 'z']); - * //=> false - * ``` - * @param {Object|Array} `val` - * @param {String|Array} `values` + * @param {String} `str` * @return {Boolean} - * @api public */ -cu.hasAll = function hasAll(val, values) { - values = cu.arrayify(values); - var len = values.length; - while (len--) { - if (!cu.has(val, values[len])) { - return false; - } - } - return true; +exports.isString = function(str) { + return str && typeof str === 'string'; }; /** - * Cast the given value to an array. - * - * ```js - * cu.arrayify('foo'); - * //=> ['foo'] - * - * cu.arrayify(['foo']); - * //=> ['foo'] - * ``` - * - * @param {String|Array} `val` + * Cast `val` to an array * @return {Array} - * @api public */ -cu.arrayify = function arrayify(val) { +exports.arrayify = function(val) { + if (typeof val === 'string') return [val]; return val ? (Array.isArray(val) ? val : [val]) : []; }; /** - * Noop + * Get the last `n` element from the given `array` + * @param {Array} `array` + * @return {*} */ -cu.noop = function noop() { - return; +exports.last = function(arr, n) { + return arr[arr.length - (n || 1)]; }; -/** - * Returns the first argument passed to the function. - */ -cu.identity = function identity(val) { - return val; -}; +/***/ }), +/* 669 */ +/***/ (function(module, exports, __webpack_require__) { -/** - * Returns true if a value has a `contructor` - * - * ```js - * cu.hasConstructor({}); - * //=> true - * - * cu.hasConstructor(Object.create(null)); - * //=> false - * ``` - * @param {Object} `value` - * @return {Boolean} - * @api public +/* + * Copyright 2009-2011 Mozilla Foundation and contributors + * Licensed under the New BSD license. See LICENSE.txt or: + * http://opensource.org/licenses/BSD-3-Clause */ +exports.SourceMapGenerator = __webpack_require__(670).SourceMapGenerator; +exports.SourceMapConsumer = __webpack_require__(676).SourceMapConsumer; +exports.SourceNode = __webpack_require__(679).SourceNode; -cu.hasConstructor = function hasConstructor(val) { - return cu.isObject(val) && typeof val.constructor !== 'undefined'; -}; - -/** - * Get the native `ownPropertyNames` from the constructor of the - * given `object`. An empty array is returned if the object does - * not have a constructor. - * - * ```js - * cu.nativeKeys({a: 'b', b: 'c', c: 'd'}) - * //=> ['a', 'b', 'c'] - * - * cu.nativeKeys(function(){}) - * //=> ['length', 'caller'] - * ``` - * - * @param {Object} `obj` Object that has a `constructor`. - * @return {Array} Array of keys. - * @api public - */ -cu.nativeKeys = function nativeKeys(val) { - if (!cu.hasConstructor(val)) return []; - return Object.getOwnPropertyNames(val); -}; +/***/ }), +/* 670 */ +/***/ (function(module, exports, __webpack_require__) { -/** - * Returns property descriptor `key` if it's an "own" property - * of the given object. - * - * ```js - * function App() {} - * Object.defineProperty(App.prototype, 'count', { - * get: function() { - * return Object.keys(this).length; - * } - * }); - * cu.getDescriptor(App.prototype, 'count'); - * // returns: - * // { - * // get: [Function], - * // set: undefined, - * // enumerable: false, - * // configurable: false - * // } - * ``` - * - * @param {Object} `obj` - * @param {String} `key` - * @return {Object} Returns descriptor `key` - * @api public +/* -*- Mode: js; js-indent-level: 2; -*- */ +/* + * Copyright 2011 Mozilla Foundation and contributors + * Licensed under the New BSD license. See LICENSE or: + * http://opensource.org/licenses/BSD-3-Clause */ -cu.getDescriptor = function getDescriptor(obj, key) { - if (!cu.isObject(obj)) { - throw new TypeError('expected an object.'); - } - if (typeof key !== 'string') { - throw new TypeError('expected key to be a string.'); - } - return Object.getOwnPropertyDescriptor(obj, key); -}; +var base64VLQ = __webpack_require__(671); +var util = __webpack_require__(673); +var ArraySet = __webpack_require__(674).ArraySet; +var MappingList = __webpack_require__(675).MappingList; /** - * Copy a descriptor from one object to another. + * An instance of the SourceMapGenerator represents a source map which is + * being built incrementally. You may pass an object with the following + * properties: * - * ```js - * function App() {} - * Object.defineProperty(App.prototype, 'count', { - * get: function() { - * return Object.keys(this).length; - * } - * }); - * var obj = {}; - * cu.copyDescriptor(obj, App.prototype, 'count'); - * ``` - * @param {Object} `receiver` - * @param {Object} `provider` - * @param {String} `name` - * @return {Object} - * @api public + * - file: The filename of the generated source. + * - sourceRoot: A root for all relative URLs in this source map. */ - -cu.copyDescriptor = function copyDescriptor(receiver, provider, name) { - if (!cu.isObject(receiver)) { - throw new TypeError('expected receiving object to be an object.'); - } - if (!cu.isObject(provider)) { - throw new TypeError('expected providing object to be an object.'); - } - if (typeof name !== 'string') { - throw new TypeError('expected name to be a string.'); +function SourceMapGenerator(aArgs) { + if (!aArgs) { + aArgs = {}; } + this._file = util.getArg(aArgs, 'file', null); + this._sourceRoot = util.getArg(aArgs, 'sourceRoot', null); + this._skipValidation = util.getArg(aArgs, 'skipValidation', false); + this._sources = new ArraySet(); + this._names = new ArraySet(); + this._mappings = new MappingList(); + this._sourcesContents = null; +} - var val = cu.getDescriptor(provider, name); - if (val) Object.defineProperty(receiver, name, val); -}; +SourceMapGenerator.prototype._version = 3; /** - * Copy static properties, prototype properties, and descriptors - * from one object to another. + * Creates a new SourceMapGenerator based on a SourceMapConsumer * - * @param {Object} `receiver` - * @param {Object} `provider` - * @param {String|Array} `omit` One or more properties to omit - * @return {Object} - * @api public + * @param aSourceMapConsumer The SourceMap. */ +SourceMapGenerator.fromSourceMap = + function SourceMapGenerator_fromSourceMap(aSourceMapConsumer) { + var sourceRoot = aSourceMapConsumer.sourceRoot; + var generator = new SourceMapGenerator({ + file: aSourceMapConsumer.file, + sourceRoot: sourceRoot + }); + aSourceMapConsumer.eachMapping(function (mapping) { + var newMapping = { + generated: { + line: mapping.generatedLine, + column: mapping.generatedColumn + } + }; -cu.copy = function copy(receiver, provider, omit) { - if (!cu.isObject(receiver)) { - throw new TypeError('expected receiving object to be an object.'); - } - if (!cu.isObject(provider)) { - throw new TypeError('expected providing object to be an object.'); - } - var props = Object.getOwnPropertyNames(provider); - var keys = Object.keys(provider); - var len = props.length, - key; - omit = cu.arrayify(omit); + if (mapping.source != null) { + newMapping.source = mapping.source; + if (sourceRoot != null) { + newMapping.source = util.relative(sourceRoot, newMapping.source); + } - while (len--) { - key = props[len]; + newMapping.original = { + line: mapping.originalLine, + column: mapping.originalColumn + }; - if (cu.has(keys, key)) { - utils.define(receiver, key, provider[key]); - } else if (!(key in receiver) && !cu.has(omit, key)) { - cu.copyDescriptor(receiver, provider, key); - } - } -}; + if (mapping.name != null) { + newMapping.name = mapping.name; + } + } + + generator.addMapping(newMapping); + }); + aSourceMapConsumer.sources.forEach(function (sourceFile) { + var content = aSourceMapConsumer.sourceContentFor(sourceFile); + if (content != null) { + generator.setSourceContent(sourceFile, content); + } + }); + return generator; + }; /** - * Inherit the static properties, prototype properties, and descriptors - * from of an object. + * Add a single mapping from original source line and column to the generated + * source's line and column for this source map being created. The mapping + * object should have the following properties: * - * @param {Object} `receiver` - * @param {Object} `provider` - * @param {String|Array} `omit` One or more properties to omit - * @return {Object} - * @api public + * - generated: An object with the generated line and column positions. + * - original: An object with the original line and column positions. + * - source: The original source file (relative to the sourceRoot). + * - name: An optional original token name for this mapping. */ +SourceMapGenerator.prototype.addMapping = + function SourceMapGenerator_addMapping(aArgs) { + var generated = util.getArg(aArgs, 'generated'); + var original = util.getArg(aArgs, 'original', null); + var source = util.getArg(aArgs, 'source', null); + var name = util.getArg(aArgs, 'name', null); -cu.inherit = function inherit(receiver, provider, omit) { - if (!cu.isObject(receiver)) { - throw new TypeError('expected receiving object to be an object.'); - } - if (!cu.isObject(provider)) { - throw new TypeError('expected providing object to be an object.'); - } + if (!this._skipValidation) { + this._validateMapping(generated, original, source, name); + } - var keys = []; - for (var key in provider) { - keys.push(key); - receiver[key] = provider[key]; - } + if (source != null) { + source = String(source); + if (!this._sources.has(source)) { + this._sources.add(source); + } + } - keys = keys.concat(cu.arrayify(omit)); + if (name != null) { + name = String(name); + if (!this._names.has(name)) { + this._names.add(name); + } + } - var a = provider.prototype || provider; - var b = receiver.prototype || receiver; - cu.copy(b, a, keys); -}; + this._mappings.add({ + generatedLine: generated.line, + generatedColumn: generated.column, + originalLine: original != null && original.line, + originalColumn: original != null && original.column, + source: source, + name: name + }); + }; /** - * Returns a function for extending the static properties, - * prototype properties, and descriptors from the `Parent` - * constructor onto `Child` constructors. - * - * ```js - * var extend = cu.extend(Parent); - * Parent.extend(Child); - * - * // optional methods - * Parent.extend(Child, { - * foo: function() {}, - * bar: function() {} - * }); - * ``` - * @param {Function} `Parent` Parent ctor - * @param {Function} `extend` Optional extend function to handle custom extensions. Useful when updating methods that require a specific prototype. - * @param {Function} `Child` Child ctor - * @param {Object} `proto` Optionally pass additional prototype properties to inherit. - * @return {Object} - * @api public + * Set the source content for a source file. */ +SourceMapGenerator.prototype.setSourceContent = + function SourceMapGenerator_setSourceContent(aSourceFile, aSourceContent) { + var source = aSourceFile; + if (this._sourceRoot != null) { + source = util.relative(this._sourceRoot, source); + } -cu.extend = function() { - // keep it lazy, instead of assigning to `cu.extend` - return utils.staticExtend.apply(null, arguments); -}; + if (aSourceContent != null) { + // Add the source content to the _sourcesContents map. + // Create a new _sourcesContents map if the property is null. + if (!this._sourcesContents) { + this._sourcesContents = Object.create(null); + } + this._sourcesContents[util.toSetString(source)] = aSourceContent; + } else if (this._sourcesContents) { + // Remove the source file from the _sourcesContents map. + // If the _sourcesContents map is empty, set the property to null. + delete this._sourcesContents[util.toSetString(source)]; + if (Object.keys(this._sourcesContents).length === 0) { + this._sourcesContents = null; + } + } + }; /** - * Bubble up events emitted from static methods on the Parent ctor. + * Applies the mappings of a sub-source-map for a specific source file to the + * source map being generated. Each mapping to the supplied source file is + * rewritten using the supplied source map. Note: The resolution for the + * resulting mappings is the minimium of this map and the supplied map. * - * @param {Object} `Parent` - * @param {Array} `events` Event names to bubble up - * @api public + * @param aSourceMapConsumer The source map to be applied. + * @param aSourceFile Optional. The filename of the source file. + * If omitted, SourceMapConsumer's file property will be used. + * @param aSourceMapPath Optional. The dirname of the path to the source map + * to be applied. If relative, it is relative to the SourceMapConsumer. + * This parameter is needed when the two source maps aren't in the same + * directory, and the source map to be applied contains relative source + * paths. If so, those relative source paths need to be rewritten + * relative to the SourceMapGenerator. */ - -cu.bubble = function(Parent, events) { - events = events || []; - Parent.bubble = function(Child, arr) { - if (Array.isArray(arr)) { - events = utils.union([], events, arr); +SourceMapGenerator.prototype.applySourceMap = + function SourceMapGenerator_applySourceMap(aSourceMapConsumer, aSourceFile, aSourceMapPath) { + var sourceFile = aSourceFile; + // If aSourceFile is omitted, we will use the file property of the SourceMap + if (aSourceFile == null) { + if (aSourceMapConsumer.file == null) { + throw new Error( + 'SourceMapGenerator.prototype.applySourceMap requires either an explicit source file, ' + + 'or the source map\'s "file" property. Both were omitted.' + ); + } + sourceFile = aSourceMapConsumer.file; } - var len = events.length; - var idx = -1; - while (++idx < len) { - var name = events[idx]; - Parent.on(name, Child.emit.bind(Child, name)); + var sourceRoot = this._sourceRoot; + // Make "sourceFile" relative if an absolute Url is passed. + if (sourceRoot != null) { + sourceFile = util.relative(sourceRoot, sourceFile); } - cu.bubble(Child, events); - }; -}; - - -/***/ }), -/* 647 */ -/***/ (function(module, exports, __webpack_require__) { + // Applying the SourceMap can add and remove items from the sources and + // the names array. + var newSources = new ArraySet(); + var newNames = new ArraySet(); -"use strict"; + // Find mappings for the "sourceFile" + this._mappings.unsortedForEach(function (mapping) { + if (mapping.source === sourceFile && mapping.originalLine != null) { + // Check if it can be mapped by the source map, then update the mapping. + var original = aSourceMapConsumer.originalPositionFor({ + line: mapping.originalLine, + column: mapping.originalColumn + }); + if (original.source != null) { + // Copy mapping + mapping.source = original.source; + if (aSourceMapPath != null) { + mapping.source = util.join(aSourceMapPath, mapping.source) + } + if (sourceRoot != null) { + mapping.source = util.relative(sourceRoot, mapping.source); + } + mapping.originalLine = original.line; + mapping.originalColumn = original.column; + if (original.name != null) { + mapping.name = original.name; + } + } + } + var source = mapping.source; + if (source != null && !newSources.has(source)) { + newSources.add(source); + } -var utils = {}; + var name = mapping.name; + if (name != null && !newNames.has(name)) { + newNames.add(name); + } + }, this); + this._sources = newSources; + this._names = newNames; + // Copy sourcesContents of applied map. + aSourceMapConsumer.sources.forEach(function (sourceFile) { + var content = aSourceMapConsumer.sourceContentFor(sourceFile); + if (content != null) { + if (aSourceMapPath != null) { + sourceFile = util.join(aSourceMapPath, sourceFile); + } + if (sourceRoot != null) { + sourceFile = util.relative(sourceRoot, sourceFile); + } + this.setSourceContent(sourceFile, content); + } + }, this); + }; /** - * Lazily required module dependencies + * A mapping can have one of the three levels of data: + * + * 1. Just the generated position. + * 2. The Generated position, original position, and original source. + * 3. Generated and original position, original source, as well as a name + * token. + * + * To maintain consistency, we validate that any new mapping being added falls + * in to one of these categories. */ +SourceMapGenerator.prototype._validateMapping = + function SourceMapGenerator_validateMapping(aGenerated, aOriginal, aSource, + aName) { + // When aOriginal is truthy but has empty values for .line and .column, + // it is most likely a programmer error. In this case we throw a very + // specific error message to try to guide them the right way. + // For example: https://github.com/Polymer/polymer-bundler/pull/519 + if (aOriginal && typeof aOriginal.line !== 'number' && typeof aOriginal.column !== 'number') { + throw new Error( + 'original.line and original.column are not numbers -- you probably meant to omit ' + + 'the original mapping entirely and only map the generated position. If so, pass ' + + 'null for the original mapping instead of an object with empty or null values.' + ); + } -utils.union = __webpack_require__(629); -utils.define = __webpack_require__(648); -utils.isObj = __webpack_require__(581); -utils.staticExtend = __webpack_require__(655); - + if (aGenerated && 'line' in aGenerated && 'column' in aGenerated + && aGenerated.line > 0 && aGenerated.column >= 0 + && !aOriginal && !aSource && !aName) { + // Case 1. + return; + } + else if (aGenerated && 'line' in aGenerated && 'column' in aGenerated + && aOriginal && 'line' in aOriginal && 'column' in aOriginal + && aGenerated.line > 0 && aGenerated.column >= 0 + && aOriginal.line > 0 && aOriginal.column >= 0 + && aSource) { + // Cases 2 and 3. + return; + } + else { + throw new Error('Invalid mapping: ' + JSON.stringify({ + generated: aGenerated, + source: aSource, + original: aOriginal, + name: aName + })); + } + }; /** - * Expose `utils` + * Serialize the accumulated mappings in to the stream of base 64 VLQs + * specified by the source map format. */ +SourceMapGenerator.prototype._serializeMappings = + function SourceMapGenerator_serializeMappings() { + var previousGeneratedColumn = 0; + var previousGeneratedLine = 1; + var previousOriginalColumn = 0; + var previousOriginalLine = 0; + var previousName = 0; + var previousSource = 0; + var result = ''; + var next; + var mapping; + var nameIdx; + var sourceIdx; -module.exports = utils; - - -/***/ }), -/* 648 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/*! - * define-property - * - * Copyright (c) 2015, Jon Schlinkert. - * Licensed under the MIT License. - */ + var mappings = this._mappings.toArray(); + for (var i = 0, len = mappings.length; i < len; i++) { + mapping = mappings[i]; + next = '' + if (mapping.generatedLine !== previousGeneratedLine) { + previousGeneratedColumn = 0; + while (mapping.generatedLine !== previousGeneratedLine) { + next += ';'; + previousGeneratedLine++; + } + } + else { + if (i > 0) { + if (!util.compareByGeneratedPositionsInflated(mapping, mappings[i - 1])) { + continue; + } + next += ','; + } + } + next += base64VLQ.encode(mapping.generatedColumn + - previousGeneratedColumn); + previousGeneratedColumn = mapping.generatedColumn; -var isDescriptor = __webpack_require__(649); + if (mapping.source != null) { + sourceIdx = this._sources.indexOf(mapping.source); + next += base64VLQ.encode(sourceIdx - previousSource); + previousSource = sourceIdx; -module.exports = function defineProperty(obj, prop, val) { - if (typeof obj !== 'object' && typeof obj !== 'function') { - throw new TypeError('expected an object or function.'); - } + // lines are stored 0-based in SourceMap spec version 3 + next += base64VLQ.encode(mapping.originalLine - 1 + - previousOriginalLine); + previousOriginalLine = mapping.originalLine - 1; - if (typeof prop !== 'string') { - throw new TypeError('expected `prop` to be a string.'); - } + next += base64VLQ.encode(mapping.originalColumn + - previousOriginalColumn); + previousOriginalColumn = mapping.originalColumn; - if (isDescriptor(val) && ('set' in val || 'get' in val)) { - return Object.defineProperty(obj, prop, val); - } + if (mapping.name != null) { + nameIdx = this._names.indexOf(mapping.name); + next += base64VLQ.encode(nameIdx - previousName); + previousName = nameIdx; + } + } - return Object.defineProperty(obj, prop, { - configurable: true, - enumerable: false, - writable: true, - value: val - }); -}; + result += next; + } + return result; + }; -/***/ }), -/* 649 */ -/***/ (function(module, exports, __webpack_require__) { +SourceMapGenerator.prototype._generateSourcesContent = + function SourceMapGenerator_generateSourcesContent(aSources, aSourceRoot) { + return aSources.map(function (source) { + if (!this._sourcesContents) { + return null; + } + if (aSourceRoot != null) { + source = util.relative(aSourceRoot, source); + } + var key = util.toSetString(source); + return Object.prototype.hasOwnProperty.call(this._sourcesContents, key) + ? this._sourcesContents[key] + : null; + }, this); + }; -"use strict"; -/*! - * is-descriptor - * - * Copyright (c) 2015-2017, Jon Schlinkert. - * Released under the MIT License. +/** + * Externalize the source map. */ +SourceMapGenerator.prototype.toJSON = + function SourceMapGenerator_toJSON() { + var map = { + version: this._version, + sources: this._sources.toArray(), + names: this._names.toArray(), + mappings: this._serializeMappings() + }; + if (this._file != null) { + map.file = this._file; + } + if (this._sourceRoot != null) { + map.sourceRoot = this._sourceRoot; + } + if (this._sourcesContents) { + map.sourcesContent = this._generateSourcesContent(map.sources, map.sourceRoot); + } + return map; + }; +/** + * Render the source map being generated to a string. + */ +SourceMapGenerator.prototype.toString = + function SourceMapGenerator_toString() { + return JSON.stringify(this.toJSON()); + }; -var typeOf = __webpack_require__(650); -var isAccessor = __webpack_require__(651); -var isData = __webpack_require__(653); - -module.exports = function isDescriptor(obj, key) { - if (typeOf(obj) !== 'object') { - return false; - } - if ('get' in obj) { - return isAccessor(obj, key); - } - return isData(obj, key); -}; +exports.SourceMapGenerator = SourceMapGenerator; /***/ }), -/* 650 */ -/***/ (function(module, exports) { - -var toString = Object.prototype.toString; +/* 671 */ +/***/ (function(module, exports, __webpack_require__) { -/** - * Get the native `typeof` a value. +/* -*- Mode: js; js-indent-level: 2; -*- */ +/* + * Copyright 2011 Mozilla Foundation and contributors + * Licensed under the New BSD license. See LICENSE or: + * http://opensource.org/licenses/BSD-3-Clause * - * @param {*} `val` - * @return {*} Native javascript type + * Based on the Base 64 VLQ implementation in Closure Compiler: + * https://code.google.com/p/closure-compiler/source/browse/trunk/src/com/google/debugging/sourcemap/Base64VLQ.java + * + * Copyright 2011 The Closure Compiler Authors. All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -module.exports = function kindOf(val) { - var type = typeof val; +var base64 = __webpack_require__(672); - // primitivies - if (type === 'undefined') { - return 'undefined'; - } - if (val === null) { - return 'null'; - } - if (val === true || val === false || val instanceof Boolean) { - return 'boolean'; - } - if (type === 'string' || val instanceof String) { - return 'string'; - } - if (type === 'number' || val instanceof Number) { - return 'number'; - } +// A single base 64 digit can contain 6 bits of data. For the base 64 variable +// length quantities we use in the source map spec, the first bit is the sign, +// the next four bits are the actual value, and the 6th bit is the +// continuation bit. The continuation bit tells us whether there are more +// digits in this value following this digit. +// +// Continuation +// | Sign +// | | +// V V +// 101011 - // functions - if (type === 'function' || val instanceof Function) { - if (typeof val.constructor.name !== 'undefined' && val.constructor.name.slice(0, 9) === 'Generator') { - return 'generatorfunction'; - } - return 'function'; - } +var VLQ_BASE_SHIFT = 5; - // array - if (typeof Array.isArray !== 'undefined' && Array.isArray(val)) { - return 'array'; - } +// binary: 100000 +var VLQ_BASE = 1 << VLQ_BASE_SHIFT; - // check for instances of RegExp and Date before calling `toString` - if (val instanceof RegExp) { - return 'regexp'; - } - if (val instanceof Date) { - return 'date'; - } +// binary: 011111 +var VLQ_BASE_MASK = VLQ_BASE - 1; - // other objects - type = toString.call(val); +// binary: 100000 +var VLQ_CONTINUATION_BIT = VLQ_BASE; - if (type === '[object RegExp]') { - return 'regexp'; - } - if (type === '[object Date]') { - return 'date'; - } - if (type === '[object Arguments]') { - return 'arguments'; - } - if (type === '[object Error]') { - return 'error'; - } - if (type === '[object Promise]') { - return 'promise'; - } +/** + * Converts from a two-complement value to a value where the sign bit is + * placed in the least significant bit. For example, as decimals: + * 1 becomes 2 (10 binary), -1 becomes 3 (11 binary) + * 2 becomes 4 (100 binary), -2 becomes 5 (101 binary) + */ +function toVLQSigned(aValue) { + return aValue < 0 + ? ((-aValue) << 1) + 1 + : (aValue << 1) + 0; +} - // buffer - if (isBuffer(val)) { - return 'buffer'; - } +/** + * Converts to a two-complement value from a value where the sign bit is + * placed in the least significant bit. For example, as decimals: + * 2 (10 binary) becomes 1, 3 (11 binary) becomes -1 + * 4 (100 binary) becomes 2, 5 (101 binary) becomes -2 + */ +function fromVLQSigned(aValue) { + var isNegative = (aValue & 1) === 1; + var shifted = aValue >> 1; + return isNegative + ? -shifted + : shifted; +} - // es6: Map, WeakMap, Set, WeakSet - if (type === '[object Set]') { - return 'set'; - } - if (type === '[object WeakSet]') { - return 'weakset'; - } - if (type === '[object Map]') { - return 'map'; - } - if (type === '[object WeakMap]') { - return 'weakmap'; - } - if (type === '[object Symbol]') { - return 'symbol'; - } - - if (type === '[object Map Iterator]') { - return 'mapiterator'; - } - if (type === '[object Set Iterator]') { - return 'setiterator'; - } - if (type === '[object String Iterator]') { - return 'stringiterator'; - } - if (type === '[object Array Iterator]') { - return 'arrayiterator'; - } - - // typed arrays - if (type === '[object Int8Array]') { - return 'int8array'; - } - if (type === '[object Uint8Array]') { - return 'uint8array'; - } - if (type === '[object Uint8ClampedArray]') { - return 'uint8clampedarray'; - } - if (type === '[object Int16Array]') { - return 'int16array'; - } - if (type === '[object Uint16Array]') { - return 'uint16array'; - } - if (type === '[object Int32Array]') { - return 'int32array'; - } - if (type === '[object Uint32Array]') { - return 'uint32array'; - } - if (type === '[object Float32Array]') { - return 'float32array'; - } - if (type === '[object Float64Array]') { - return 'float64array'; - } +/** + * Returns the base 64 VLQ encoded value. + */ +exports.encode = function base64VLQ_encode(aValue) { + var encoded = ""; + var digit; - // must be a plain object - return 'object'; + var vlq = toVLQSigned(aValue); + + do { + digit = vlq & VLQ_BASE_MASK; + vlq >>>= VLQ_BASE_SHIFT; + if (vlq > 0) { + // There are still more digits in this value, so we must make sure the + // continuation bit is marked. + digit |= VLQ_CONTINUATION_BIT; + } + encoded += base64.encode(digit); + } while (vlq > 0); + + return encoded; }; /** - * If you need to support Safari 5-7 (8-10 yr-old browser), - * take a look at https://github.com/feross/is-buffer + * Decodes the next base 64 VLQ value from the given string and returns the + * value and the rest of the string via the out parameter. */ +exports.decode = function base64VLQ_decode(aStr, aIndex, aOutParam) { + var strLen = aStr.length; + var result = 0; + var shift = 0; + var continuation, digit; -function isBuffer(val) { - return val.constructor - && typeof val.constructor.isBuffer === 'function' - && val.constructor.isBuffer(val); -} + do { + if (aIndex >= strLen) { + throw new Error("Expected more digits in base 64 VLQ value."); + } + + digit = base64.decode(aStr.charCodeAt(aIndex++)); + if (digit === -1) { + throw new Error("Invalid base64 digit: " + aStr.charAt(aIndex - 1)); + } + + continuation = !!(digit & VLQ_CONTINUATION_BIT); + digit &= VLQ_BASE_MASK; + result = result + (digit << shift); + shift += VLQ_BASE_SHIFT; + } while (continuation); + + aOutParam.value = fromVLQSigned(result); + aOutParam.rest = aIndex; +}; /***/ }), -/* 651 */ -/***/ (function(module, exports, __webpack_require__) { +/* 672 */ +/***/ (function(module, exports) { -"use strict"; -/*! - * is-accessor-descriptor - * - * Copyright (c) 2015, Jon Schlinkert. - * Licensed under the MIT License. +/* -*- Mode: js; js-indent-level: 2; -*- */ +/* + * Copyright 2011 Mozilla Foundation and contributors + * Licensed under the New BSD license. See LICENSE or: + * http://opensource.org/licenses/BSD-3-Clause */ +var intToCharMap = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split(''); +/** + * Encode an integer in the range of 0 to 63 to a single base 64 digit. + */ +exports.encode = function (number) { + if (0 <= number && number < intToCharMap.length) { + return intToCharMap[number]; + } + throw new TypeError("Must be between 0 and 63: " + number); +}; -var typeOf = __webpack_require__(652); +/** + * Decode a single base 64 character code digit to an integer. Returns -1 on + * failure. + */ +exports.decode = function (charCode) { + var bigA = 65; // 'A' + var bigZ = 90; // 'Z' -// accessor descriptor properties -var accessor = { - get: 'function', - set: 'function', - configurable: 'boolean', - enumerable: 'boolean' -}; + var littleA = 97; // 'a' + var littleZ = 122; // 'z' -function isAccessorDescriptor(obj, prop) { - if (typeof prop === 'string') { - var val = Object.getOwnPropertyDescriptor(obj, prop); - return typeof val !== 'undefined'; - } + var zero = 48; // '0' + var nine = 57; // '9' - if (typeOf(obj) !== 'object') { - return false; - } + var plus = 43; // '+' + var slash = 47; // '/' - if (has(obj, 'value') || has(obj, 'writable')) { - return false; - } + var littleOffset = 26; + var numberOffset = 52; - if (!has(obj, 'get') || typeof obj.get !== 'function') { - return false; + // 0 - 25: ABCDEFGHIJKLMNOPQRSTUVWXYZ + if (bigA <= charCode && charCode <= bigZ) { + return (charCode - bigA); } - // tldr: it's valid to have "set" be undefined - // "set" might be undefined if `Object.getOwnPropertyDescriptor` - // was used to get the value, and only `get` was defined by the user - if (has(obj, 'set') && typeof obj[key] !== 'function' && typeof obj[key] !== 'undefined') { - return false; + // 26 - 51: abcdefghijklmnopqrstuvwxyz + if (littleA <= charCode && charCode <= littleZ) { + return (charCode - littleA + littleOffset); } - for (var key in obj) { - if (!accessor.hasOwnProperty(key)) { - continue; - } - - if (typeOf(obj[key]) === accessor[key]) { - continue; - } - - if (typeof obj[key] !== 'undefined') { - return false; - } + // 52 - 61: 0123456789 + if (zero <= charCode && charCode <= nine) { + return (charCode - zero + numberOffset); } - return true; -} -function has(obj, key) { - return {}.hasOwnProperty.call(obj, key); -} + // 62: + + if (charCode == plus) { + return 62; + } -/** - * Expose `isAccessorDescriptor` - */ + // 63: / + if (charCode == slash) { + return 63; + } -module.exports = isAccessorDescriptor; + // Invalid base64 digit. + return -1; +}; /***/ }), -/* 652 */ -/***/ (function(module, exports, __webpack_require__) { +/* 673 */ +/***/ (function(module, exports) { -var isBuffer = __webpack_require__(605); -var toString = Object.prototype.toString; +/* -*- Mode: js; js-indent-level: 2; -*- */ +/* + * Copyright 2011 Mozilla Foundation and contributors + * Licensed under the New BSD license. See LICENSE or: + * http://opensource.org/licenses/BSD-3-Clause + */ /** - * Get the native `typeof` a value. + * This is a helper function for getting values from parameter/options + * objects. * - * @param {*} `val` - * @return {*} Native javascript type + * @param args The object we are extracting values from + * @param name The name of the property we are getting. + * @param defaultValue An optional value to return if the property is missing + * from the object. If this is not specified and the property is missing, an + * error will be thrown. */ - -module.exports = function kindOf(val) { - // primitivies - if (typeof val === 'undefined') { - return 'undefined'; - } - if (val === null) { - return 'null'; - } - if (val === true || val === false || val instanceof Boolean) { - return 'boolean'; - } - if (typeof val === 'string' || val instanceof String) { - return 'string'; - } - if (typeof val === 'number' || val instanceof Number) { - return 'number'; +function getArg(aArgs, aName, aDefaultValue) { + if (aName in aArgs) { + return aArgs[aName]; + } else if (arguments.length === 3) { + return aDefaultValue; + } else { + throw new Error('"' + aName + '" is a required argument.'); } +} +exports.getArg = getArg; - // functions - if (typeof val === 'function' || val instanceof Function) { - return 'function'; - } +var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.]*)(?::(\d+))?(\S*)$/; +var dataUrlRegexp = /^data:.+\,.+$/; - // array - if (typeof Array.isArray !== 'undefined' && Array.isArray(val)) { - return 'array'; +function urlParse(aUrl) { + var match = aUrl.match(urlRegexp); + if (!match) { + return null; } + return { + scheme: match[1], + auth: match[2], + host: match[3], + port: match[4], + path: match[5] + }; +} +exports.urlParse = urlParse; - // check for instances of RegExp and Date before calling `toString` - if (val instanceof RegExp) { - return 'regexp'; - } - if (val instanceof Date) { - return 'date'; +function urlGenerate(aParsedUrl) { + var url = ''; + if (aParsedUrl.scheme) { + url += aParsedUrl.scheme + ':'; } - - // other objects - var type = toString.call(val); - - if (type === '[object RegExp]') { - return 'regexp'; + url += '//'; + if (aParsedUrl.auth) { + url += aParsedUrl.auth + '@'; } - if (type === '[object Date]') { - return 'date'; + if (aParsedUrl.host) { + url += aParsedUrl.host; } - if (type === '[object Arguments]') { - return 'arguments'; + if (aParsedUrl.port) { + url += ":" + aParsedUrl.port } - if (type === '[object Error]') { - return 'error'; + if (aParsedUrl.path) { + url += aParsedUrl.path; } + return url; +} +exports.urlGenerate = urlGenerate; - // buffer - if (isBuffer(val)) { - return 'buffer'; +/** + * Normalizes a path, or the path portion of a URL: + * + * - Replaces consecutive slashes with one slash. + * - Removes unnecessary '.' parts. + * - Removes unnecessary '
/..' parts. + * + * Based on code in the Node.js 'path' core module. + * + * @param aPath The path or url to normalize. + */ +function normalize(aPath) { + var path = aPath; + var url = urlParse(aPath); + if (url) { + if (!url.path) { + return aPath; + } + path = url.path; } + var isAbsolute = exports.isAbsolute(path); - // es6: Map, WeakMap, Set, WeakSet - if (type === '[object Set]') { - return 'set'; - } - if (type === '[object WeakSet]') { - return 'weakset'; - } - if (type === '[object Map]') { - return 'map'; - } - if (type === '[object WeakMap]') { - return 'weakmap'; - } - if (type === '[object Symbol]') { - return 'symbol'; + var parts = path.split(/\/+/); + for (var part, up = 0, i = parts.length - 1; i >= 0; i--) { + part = parts[i]; + if (part === '.') { + parts.splice(i, 1); + } else if (part === '..') { + up++; + } else if (up > 0) { + if (part === '') { + // The first part is blank if the path is absolute. Trying to go + // above the root is a no-op. Therefore we can remove all '..' parts + // directly after the root. + parts.splice(i + 1, up); + up = 0; + } else { + parts.splice(i, 2); + up--; + } + } } + path = parts.join('/'); - // typed arrays - if (type === '[object Int8Array]') { - return 'int8array'; - } - if (type === '[object Uint8Array]') { - return 'uint8array'; + if (path === '') { + path = isAbsolute ? '/' : '.'; } - if (type === '[object Uint8ClampedArray]') { - return 'uint8clampedarray'; + + if (url) { + url.path = path; + return urlGenerate(url); } - if (type === '[object Int16Array]') { - return 'int16array'; + return path; +} +exports.normalize = normalize; + +/** + * Joins two paths/URLs. + * + * @param aRoot The root path or URL. + * @param aPath The path or URL to be joined with the root. + * + * - If aPath is a URL or a data URI, aPath is returned, unless aPath is a + * scheme-relative URL: Then the scheme of aRoot, if any, is prepended + * first. + * - Otherwise aPath is a path. If aRoot is a URL, then its path portion + * is updated with the result and aRoot is returned. Otherwise the result + * is returned. + * - If aPath is absolute, the result is aPath. + * - Otherwise the two paths are joined with a slash. + * - Joining for example 'http://' and 'www.example.com' is also supported. + */ +function join(aRoot, aPath) { + if (aRoot === "") { + aRoot = "."; } - if (type === '[object Uint16Array]') { - return 'uint16array'; + if (aPath === "") { + aPath = "."; } - if (type === '[object Int32Array]') { - return 'int32array'; + var aPathUrl = urlParse(aPath); + var aRootUrl = urlParse(aRoot); + if (aRootUrl) { + aRoot = aRootUrl.path || '/'; } - if (type === '[object Uint32Array]') { - return 'uint32array'; + + // `join(foo, '//www.example.org')` + if (aPathUrl && !aPathUrl.scheme) { + if (aRootUrl) { + aPathUrl.scheme = aRootUrl.scheme; + } + return urlGenerate(aPathUrl); } - if (type === '[object Float32Array]') { - return 'float32array'; + + if (aPathUrl || aPath.match(dataUrlRegexp)) { + return aPath; } - if (type === '[object Float64Array]') { - return 'float64array'; + + // `join('http://', 'www.example.com')` + if (aRootUrl && !aRootUrl.host && !aRootUrl.path) { + aRootUrl.host = aPath; + return urlGenerate(aRootUrl); } - // must be a plain object - return 'object'; -}; + var joined = aPath.charAt(0) === '/' + ? aPath + : normalize(aRoot.replace(/\/+$/, '') + '/' + aPath); + if (aRootUrl) { + aRootUrl.path = joined; + return urlGenerate(aRootUrl); + } + return joined; +} +exports.join = join; -/***/ }), -/* 653 */ -/***/ (function(module, exports, __webpack_require__) { +exports.isAbsolute = function (aPath) { + return aPath.charAt(0) === '/' || !!aPath.match(urlRegexp); +}; -"use strict"; -/*! - * is-data-descriptor +/** + * Make a path relative to a URL or another path. * - * Copyright (c) 2015, Jon Schlinkert. - * Licensed under the MIT License. + * @param aRoot The root path or URL. + * @param aPath The path or URL to be made relative to aRoot. */ +function relative(aRoot, aPath) { + if (aRoot === "") { + aRoot = "."; + } + aRoot = aRoot.replace(/\/$/, ''); + // It is possible for the path to be above the root. In this case, simply + // checking whether the root is a prefix of the path won't work. Instead, we + // need to remove components from the root one by one, until either we find + // a prefix that fits, or we run out of components to remove. + var level = 0; + while (aPath.indexOf(aRoot + '/') !== 0) { + var index = aRoot.lastIndexOf("/"); + if (index < 0) { + return aPath; + } -var typeOf = __webpack_require__(654); + // If the only part of the root that is left is the scheme (i.e. http://, + // file:///, etc.), one or more slashes (/), or simply nothing at all, we + // have exhausted all components, so the path is not relative to the root. + aRoot = aRoot.slice(0, index); + if (aRoot.match(/^([^\/]+:\/)?\/*$/)) { + return aPath; + } -// data descriptor properties -var data = { - configurable: 'boolean', - enumerable: 'boolean', - writable: 'boolean' -}; + ++level; + } -function isDataDescriptor(obj, prop) { - if (typeOf(obj) !== 'object') { - return false; + // Make sure we add a "../" for each component we removed from the root. + return Array(level + 1).join("../") + aPath.substr(aRoot.length + 1); +} +exports.relative = relative; + +var supportsNullProto = (function () { + var obj = Object.create(null); + return !('__proto__' in obj); +}()); + +function identity (s) { + return s; +} + +/** + * Because behavior goes wacky when you set `__proto__` on objects, we + * have to prefix all the strings in our set with an arbitrary character. + * + * See https://github.com/mozilla/source-map/pull/31 and + * https://github.com/mozilla/source-map/issues/30 + * + * @param String aStr + */ +function toSetString(aStr) { + if (isProtoString(aStr)) { + return '$' + aStr; } - if (typeof prop === 'string') { - var val = Object.getOwnPropertyDescriptor(obj, prop); - return typeof val !== 'undefined'; + return aStr; +} +exports.toSetString = supportsNullProto ? identity : toSetString; + +function fromSetString(aStr) { + if (isProtoString(aStr)) { + return aStr.slice(1); } - if (!('value' in obj) && !('writable' in obj)) { + return aStr; +} +exports.fromSetString = supportsNullProto ? identity : fromSetString; + +function isProtoString(s) { + if (!s) { return false; } - for (var key in obj) { - if (key === 'value') continue; + var length = s.length; - if (!data.hasOwnProperty(key)) { - continue; - } + if (length < 9 /* "__proto__".length */) { + return false; + } - if (typeOf(obj[key]) === data[key]) { - continue; - } + if (s.charCodeAt(length - 1) !== 95 /* '_' */ || + s.charCodeAt(length - 2) !== 95 /* '_' */ || + s.charCodeAt(length - 3) !== 111 /* 'o' */ || + s.charCodeAt(length - 4) !== 116 /* 't' */ || + s.charCodeAt(length - 5) !== 111 /* 'o' */ || + s.charCodeAt(length - 6) !== 114 /* 'r' */ || + s.charCodeAt(length - 7) !== 112 /* 'p' */ || + s.charCodeAt(length - 8) !== 95 /* '_' */ || + s.charCodeAt(length - 9) !== 95 /* '_' */) { + return false; + } - if (typeof obj[key] !== 'undefined') { + for (var i = length - 10; i >= 0; i--) { + if (s.charCodeAt(i) !== 36 /* '$' */) { return false; } } + return true; } /** - * Expose `isDataDescriptor` + * Comparator between two mappings where the original positions are compared. + * + * Optionally pass in `true` as `onlyCompareGenerated` to consider two + * mappings with the same original source/line/column, but different generated + * line and column the same. Useful when searching for a mapping with a + * stubbed out mapping. */ +function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) { + var cmp = mappingA.source - mappingB.source; + if (cmp !== 0) { + return cmp; + } -module.exports = isDataDescriptor; + cmp = mappingA.originalLine - mappingB.originalLine; + if (cmp !== 0) { + return cmp; + } + cmp = mappingA.originalColumn - mappingB.originalColumn; + if (cmp !== 0 || onlyCompareOriginal) { + return cmp; + } -/***/ }), -/* 654 */ -/***/ (function(module, exports, __webpack_require__) { + cmp = mappingA.generatedColumn - mappingB.generatedColumn; + if (cmp !== 0) { + return cmp; + } -var isBuffer = __webpack_require__(605); -var toString = Object.prototype.toString; + cmp = mappingA.generatedLine - mappingB.generatedLine; + if (cmp !== 0) { + return cmp; + } + + return mappingA.name - mappingB.name; +} +exports.compareByOriginalPositions = compareByOriginalPositions; /** - * Get the native `typeof` a value. + * Comparator between two mappings with deflated source and name indices where + * the generated positions are compared. * - * @param {*} `val` - * @return {*} Native javascript type + * Optionally pass in `true` as `onlyCompareGenerated` to consider two + * mappings with the same generated line and column, but different + * source/name/original line and column the same. Useful when searching for a + * mapping with a stubbed out mapping. */ - -module.exports = function kindOf(val) { - // primitivies - if (typeof val === 'undefined') { - return 'undefined'; - } - if (val === null) { - return 'null'; - } - if (val === true || val === false || val instanceof Boolean) { - return 'boolean'; - } - if (typeof val === 'string' || val instanceof String) { - return 'string'; - } - if (typeof val === 'number' || val instanceof Number) { - return 'number'; +function compareByGeneratedPositionsDeflated(mappingA, mappingB, onlyCompareGenerated) { + var cmp = mappingA.generatedLine - mappingB.generatedLine; + if (cmp !== 0) { + return cmp; } - // functions - if (typeof val === 'function' || val instanceof Function) { - return 'function'; + cmp = mappingA.generatedColumn - mappingB.generatedColumn; + if (cmp !== 0 || onlyCompareGenerated) { + return cmp; } - // array - if (typeof Array.isArray !== 'undefined' && Array.isArray(val)) { - return 'array'; + cmp = mappingA.source - mappingB.source; + if (cmp !== 0) { + return cmp; } - // check for instances of RegExp and Date before calling `toString` - if (val instanceof RegExp) { - return 'regexp'; + cmp = mappingA.originalLine - mappingB.originalLine; + if (cmp !== 0) { + return cmp; } - if (val instanceof Date) { - return 'date'; + + cmp = mappingA.originalColumn - mappingB.originalColumn; + if (cmp !== 0) { + return cmp; } - // other objects - var type = toString.call(val); + return mappingA.name - mappingB.name; +} +exports.compareByGeneratedPositionsDeflated = compareByGeneratedPositionsDeflated; - if (type === '[object RegExp]') { - return 'regexp'; - } - if (type === '[object Date]') { - return 'date'; - } - if (type === '[object Arguments]') { - return 'arguments'; - } - if (type === '[object Error]') { - return 'error'; +function strcmp(aStr1, aStr2) { + if (aStr1 === aStr2) { + return 0; } - // buffer - if (isBuffer(val)) { - return 'buffer'; + if (aStr1 > aStr2) { + return 1; } - // es6: Map, WeakMap, Set, WeakSet - if (type === '[object Set]') { - return 'set'; - } - if (type === '[object WeakSet]') { - return 'weakset'; - } - if (type === '[object Map]') { - return 'map'; - } - if (type === '[object WeakMap]') { - return 'weakmap'; - } - if (type === '[object Symbol]') { - return 'symbol'; - } + return -1; +} - // typed arrays - if (type === '[object Int8Array]') { - return 'int8array'; - } - if (type === '[object Uint8Array]') { - return 'uint8array'; - } - if (type === '[object Uint8ClampedArray]') { - return 'uint8clampedarray'; - } - if (type === '[object Int16Array]') { - return 'int16array'; - } - if (type === '[object Uint16Array]') { - return 'uint16array'; +/** + * Comparator between two mappings with inflated source and name strings where + * the generated positions are compared. + */ +function compareByGeneratedPositionsInflated(mappingA, mappingB) { + var cmp = mappingA.generatedLine - mappingB.generatedLine; + if (cmp !== 0) { + return cmp; } - if (type === '[object Int32Array]') { - return 'int32array'; + + cmp = mappingA.generatedColumn - mappingB.generatedColumn; + if (cmp !== 0) { + return cmp; } - if (type === '[object Uint32Array]') { - return 'uint32array'; + + cmp = strcmp(mappingA.source, mappingB.source); + if (cmp !== 0) { + return cmp; } - if (type === '[object Float32Array]') { - return 'float32array'; + + cmp = mappingA.originalLine - mappingB.originalLine; + if (cmp !== 0) { + return cmp; } - if (type === '[object Float64Array]') { - return 'float64array'; + + cmp = mappingA.originalColumn - mappingB.originalColumn; + if (cmp !== 0) { + return cmp; } - // must be a plain object - return 'object'; -}; + return strcmp(mappingA.name, mappingB.name); +} +exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflated; /***/ }), -/* 655 */ +/* 674 */ /***/ (function(module, exports, __webpack_require__) { -"use strict"; -/*! - * static-extend - * - * Copyright (c) 2016, Jon Schlinkert. - * Licensed under the MIT License. +/* -*- Mode: js; js-indent-level: 2; -*- */ +/* + * Copyright 2011 Mozilla Foundation and contributors + * Licensed under the New BSD license. See LICENSE or: + * http://opensource.org/licenses/BSD-3-Clause */ - - -var copy = __webpack_require__(656); -var define = __webpack_require__(648); -var util = __webpack_require__(113); +var util = __webpack_require__(673); +var has = Object.prototype.hasOwnProperty; +var hasNativeMap = typeof Map !== "undefined"; /** - * Returns a function for extending the static properties, - * prototype properties, and descriptors from the `Parent` - * constructor onto `Child` constructors. - * - * ```js - * var extend = require('static-extend'); - * Parent.extend = extend(Parent); - * - * // optionally pass a custom merge function as the second arg - * Parent.extend = extend(Parent, function(Child) { - * Child.prototype.mixin = function(key, val) { - * Child.prototype[key] = val; - * }; - * }); - * - * // extend "child" constructors - * Parent.extend(Child); - * - * // optionally define prototype methods as the second arg - * Parent.extend(Child, { - * foo: function() {}, - * bar: function() {} - * }); - * ``` - * @param {Function} `Parent` Parent ctor - * @param {Function} `extendFn` Optional extend function for handling any necessary custom merging. Useful when updating methods that require a specific prototype. - * @param {Function} `Child` Child ctor - * @param {Object} `proto` Optionally pass additional prototype properties to inherit. - * @return {Object} - * @api public + * A data structure which is a combination of an array and a set. Adding a new + * member is O(1), testing for membership is O(1), and finding the index of an + * element is O(1). Removing elements from the set is not supported. Only + * strings are supported for membership. */ +function ArraySet() { + this._array = []; + this._set = hasNativeMap ? new Map() : Object.create(null); +} -function extend(Parent, extendFn) { - if (typeof Parent !== 'function') { - throw new TypeError('expected Parent to be a function.'); +/** + * Static method for creating ArraySet instances from an existing array. + */ +ArraySet.fromArray = function ArraySet_fromArray(aArray, aAllowDuplicates) { + var set = new ArraySet(); + for (var i = 0, len = aArray.length; i < len; i++) { + set.add(aArray[i], aAllowDuplicates); } - - return function(Ctor, proto) { - if (typeof Ctor !== 'function') { - throw new TypeError('expected Ctor to be a function.'); - } - - util.inherits(Ctor, Parent); - copy(Ctor, Parent); - - // proto can be null or a plain object - if (typeof proto === 'object') { - var obj = Object.create(proto); - - for (var k in obj) { - Ctor.prototype[k] = obj[k]; - } - } - - // keep a reference to the parent prototype - define(Ctor.prototype, '_parent_', { - configurable: true, - set: function() {}, - get: function() { - return Parent.prototype; - } - }); - - if (typeof extendFn === 'function') { - extendFn(Ctor, Parent); - } - - Ctor.extend = extend(Ctor, extendFn); - }; + return set; }; /** - * Expose `extend` + * Return how many unique items are in this ArraySet. If duplicates have been + * added, than those do not count towards the size. + * + * @returns Number */ - -module.exports = extend; - - -/***/ }), -/* 656 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -var typeOf = __webpack_require__(657); -var copyDescriptor = __webpack_require__(658); -var define = __webpack_require__(648); +ArraySet.prototype.size = function ArraySet_size() { + return hasNativeMap ? this._set.size : Object.getOwnPropertyNames(this._set).length; +}; /** - * Copy static properties, prototype properties, and descriptors from one object to another. - * - * ```js - * function App() {} - * var proto = App.prototype; - * App.prototype.set = function() {}; - * App.prototype.get = function() {}; + * Add the given string to this set. * - * var obj = {}; - * copy(obj, proto); - * ``` - * @param {Object} `receiver` - * @param {Object} `provider` - * @param {String|Array} `omit` One or more properties to omit - * @return {Object} - * @api public + * @param String aStr */ - -function copy(receiver, provider, omit) { - if (!isObject(receiver)) { - throw new TypeError('expected receiving object to be an object.'); - } - if (!isObject(provider)) { - throw new TypeError('expected providing object to be an object.'); +ArraySet.prototype.add = function ArraySet_add(aStr, aAllowDuplicates) { + var sStr = hasNativeMap ? aStr : util.toSetString(aStr); + var isDuplicate = hasNativeMap ? this.has(aStr) : has.call(this._set, sStr); + var idx = this._array.length; + if (!isDuplicate || aAllowDuplicates) { + this._array.push(aStr); } - - var props = nativeKeys(provider); - var keys = Object.keys(provider); - var len = props.length; - omit = arrayify(omit); - - while (len--) { - var key = props[len]; - - if (has(keys, key)) { - define(receiver, key, provider[key]); - } else if (!(key in receiver) && !has(omit, key)) { - copyDescriptor(receiver, provider, key); + if (!isDuplicate) { + if (hasNativeMap) { + this._set.set(aStr, idx); + } else { + this._set[sStr] = idx; } } }; /** - * Return true if the given value is an object or function + * Is the given string a member of this set? + * + * @param String aStr */ - -function isObject(val) { - return typeOf(val) === 'object' || typeof val === 'function'; -} +ArraySet.prototype.has = function ArraySet_has(aStr) { + if (hasNativeMap) { + return this._set.has(aStr); + } else { + var sStr = util.toSetString(aStr); + return has.call(this._set, sStr); + } +}; /** - * Returns true if an array has any of the given elements, or an - * object has any of the give keys. - * - * ```js - * has(['a', 'b', 'c'], 'c'); - * //=> true - * - * has(['a', 'b', 'c'], ['c', 'z']); - * //=> true + * What is the index of the given string in the array? * - * has({a: 'b', c: 'd'}, ['c', 'z']); - * //=> true - * ``` - * @param {Object} `obj` - * @param {String|Array} `val` - * @return {Boolean} + * @param String aStr */ - -function has(obj, val) { - val = arrayify(val); - var len = val.length; - - if (isObject(obj)) { - for (var key in obj) { - if (val.indexOf(key) > -1) { - return true; - } +ArraySet.prototype.indexOf = function ArraySet_indexOf(aStr) { + if (hasNativeMap) { + var idx = this._set.get(aStr); + if (idx >= 0) { + return idx; } - - var keys = nativeKeys(obj); - return has(keys, val); - } - - if (Array.isArray(obj)) { - var arr = obj; - while (len--) { - if (arr.indexOf(val[len]) > -1) { - return true; - } + } else { + var sStr = util.toSetString(aStr); + if (has.call(this._set, sStr)) { + return this._set[sStr]; } - return false; } - throw new TypeError('expected an array or object.'); -} + throw new Error('"' + aStr + '" is not in the set.'); +}; /** - * Cast the given value to an array. - * - * ```js - * arrayify('foo'); - * //=> ['foo'] - * - * arrayify(['foo']); - * //=> ['foo'] - * ``` + * What is the element at the given index? * - * @param {String|Array} `val` - * @return {Array} + * @param Number aIdx */ - -function arrayify(val) { - return val ? (Array.isArray(val) ? val : [val]) : []; -} +ArraySet.prototype.at = function ArraySet_at(aIdx) { + if (aIdx >= 0 && aIdx < this._array.length) { + return this._array[aIdx]; + } + throw new Error('No element indexed by ' + aIdx); +}; /** - * Returns true if a value has a `contructor` - * - * ```js - * hasConstructor({}); - * //=> true - * - * hasConstructor(Object.create(null)); - * //=> false - * ``` - * @param {Object} `value` - * @return {Boolean} + * Returns the array representation of this set (which has the proper indices + * indicated by indexOf). Note that this is a copy of the internal array used + * for storing the members so that no one can mess with internal state. */ +ArraySet.prototype.toArray = function ArraySet_toArray() { + return this._array.slice(); +}; -function hasConstructor(val) { - return isObject(val) && typeof val.constructor !== 'undefined'; -} +exports.ArraySet = ArraySet; + + +/***/ }), +/* 675 */ +/***/ (function(module, exports, __webpack_require__) { + +/* -*- Mode: js; js-indent-level: 2; -*- */ +/* + * Copyright 2014 Mozilla Foundation and contributors + * Licensed under the New BSD license. See LICENSE or: + * http://opensource.org/licenses/BSD-3-Clause + */ + +var util = __webpack_require__(673); /** - * Get the native `ownPropertyNames` from the constructor of the - * given `object`. An empty array is returned if the object does - * not have a constructor. - * - * ```js - * nativeKeys({a: 'b', b: 'c', c: 'd'}) - * //=> ['a', 'b', 'c'] - * - * nativeKeys(function(){}) - * //=> ['length', 'caller'] - * ``` - * - * @param {Object} `obj` Object that has a `constructor`. - * @return {Array} Array of keys. + * Determine whether mappingB is after mappingA with respect to generated + * position. */ +function generatedPositionAfter(mappingA, mappingB) { + // Optimized for most common case + var lineA = mappingA.generatedLine; + var lineB = mappingB.generatedLine; + var columnA = mappingA.generatedColumn; + var columnB = mappingB.generatedColumn; + return lineB > lineA || lineB == lineA && columnB >= columnA || + util.compareByGeneratedPositionsInflated(mappingA, mappingB) <= 0; +} -function nativeKeys(val) { - if (!hasConstructor(val)) return []; - return Object.getOwnPropertyNames(val); +/** + * A data structure to provide a sorted view of accumulated mappings in a + * performance conscious manner. It trades a neglibable overhead in general + * case for a large speedup in case of mappings being added in order. + */ +function MappingList() { + this._array = []; + this._sorted = true; + // Serves as infimum + this._last = {generatedLine: -1, generatedColumn: 0}; } /** - * Expose `copy` + * Iterate through internal items. This method takes the same arguments that + * `Array.prototype.forEach` takes. + * + * NOTE: The order of the mappings is NOT guaranteed. */ +MappingList.prototype.unsortedForEach = + function MappingList_forEach(aCallback, aThisArg) { + this._array.forEach(aCallback, aThisArg); + }; -module.exports = copy; +/** + * Add the given source mapping. + * + * @param Object aMapping + */ +MappingList.prototype.add = function MappingList_add(aMapping) { + if (generatedPositionAfter(this._last, aMapping)) { + this._last = aMapping; + this._array.push(aMapping); + } else { + this._sorted = false; + this._array.push(aMapping); + } +}; /** - * Expose `copy.has` for tests + * Returns the flat, sorted array of mappings. The mappings are sorted by + * generated position. + * + * WARNING: This method returns internal data without copying, for + * performance. The return value must NOT be mutated, and should be treated as + * an immutable borrow. If you want to take ownership, you must make your own + * copy. */ +MappingList.prototype.toArray = function MappingList_toArray() { + if (!this._sorted) { + this._array.sort(util.compareByGeneratedPositionsInflated); + this._sorted = true; + } + return this._array; +}; -module.exports.has = has; +exports.MappingList = MappingList; /***/ }), -/* 657 */ +/* 676 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(605); -var toString = Object.prototype.toString; - -/** - * Get the native `typeof` a value. - * - * @param {*} `val` - * @return {*} Native javascript type +/* -*- Mode: js; js-indent-level: 2; -*- */ +/* + * Copyright 2011 Mozilla Foundation and contributors + * Licensed under the New BSD license. See LICENSE or: + * http://opensource.org/licenses/BSD-3-Clause */ -module.exports = function kindOf(val) { - // primitivies - if (typeof val === 'undefined') { - return 'undefined'; - } - if (val === null) { - return 'null'; - } - if (val === true || val === false || val instanceof Boolean) { - return 'boolean'; - } - if (typeof val === 'string' || val instanceof String) { - return 'string'; - } - if (typeof val === 'number' || val instanceof Number) { - return 'number'; - } +var util = __webpack_require__(673); +var binarySearch = __webpack_require__(677); +var ArraySet = __webpack_require__(674).ArraySet; +var base64VLQ = __webpack_require__(671); +var quickSort = __webpack_require__(678).quickSort; - // functions - if (typeof val === 'function' || val instanceof Function) { - return 'function'; +function SourceMapConsumer(aSourceMap) { + var sourceMap = aSourceMap; + if (typeof aSourceMap === 'string') { + sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, '')); } - // array - if (typeof Array.isArray !== 'undefined' && Array.isArray(val)) { - return 'array'; - } + return sourceMap.sections != null + ? new IndexedSourceMapConsumer(sourceMap) + : new BasicSourceMapConsumer(sourceMap); +} - // check for instances of RegExp and Date before calling `toString` - if (val instanceof RegExp) { - return 'regexp'; - } - if (val instanceof Date) { - return 'date'; - } +SourceMapConsumer.fromSourceMap = function(aSourceMap) { + return BasicSourceMapConsumer.fromSourceMap(aSourceMap); +} - // other objects - var type = toString.call(val); +/** + * The version of the source mapping spec that we are consuming. + */ +SourceMapConsumer.prototype._version = 3; - if (type === '[object RegExp]') { - return 'regexp'; - } - if (type === '[object Date]') { - return 'date'; - } - if (type === '[object Arguments]') { - return 'arguments'; - } - if (type === '[object Error]') { - return 'error'; - } +// `__generatedMappings` and `__originalMappings` are arrays that hold the +// parsed mapping coordinates from the source map's "mappings" attribute. They +// are lazily instantiated, accessed via the `_generatedMappings` and +// `_originalMappings` getters respectively, and we only parse the mappings +// and create these arrays once queried for a source location. We jump through +// these hoops because there can be many thousands of mappings, and parsing +// them is expensive, so we only want to do it if we must. +// +// Each object in the arrays is of the form: +// +// { +// generatedLine: The line number in the generated code, +// generatedColumn: The column number in the generated code, +// source: The path to the original source file that generated this +// chunk of code, +// originalLine: The line number in the original source that +// corresponds to this chunk of generated code, +// originalColumn: The column number in the original source that +// corresponds to this chunk of generated code, +// name: The name of the original symbol which generated this chunk of +// code. +// } +// +// All properties except for `generatedLine` and `generatedColumn` can be +// `null`. +// +// `_generatedMappings` is ordered by the generated positions. +// +// `_originalMappings` is ordered by the original positions. - // buffer - if (isBuffer(val)) { - return 'buffer'; - } +SourceMapConsumer.prototype.__generatedMappings = null; +Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', { + get: function () { + if (!this.__generatedMappings) { + this._parseMappings(this._mappings, this.sourceRoot); + } - // es6: Map, WeakMap, Set, WeakSet - if (type === '[object Set]') { - return 'set'; - } - if (type === '[object WeakSet]') { - return 'weakset'; - } - if (type === '[object Map]') { - return 'map'; - } - if (type === '[object WeakMap]') { - return 'weakmap'; - } - if (type === '[object Symbol]') { - return 'symbol'; + return this.__generatedMappings; } +}); - // typed arrays - if (type === '[object Int8Array]') { - return 'int8array'; - } - if (type === '[object Uint8Array]') { - return 'uint8array'; - } - if (type === '[object Uint8ClampedArray]') { - return 'uint8clampedarray'; - } - if (type === '[object Int16Array]') { - return 'int16array'; - } - if (type === '[object Uint16Array]') { - return 'uint16array'; - } - if (type === '[object Int32Array]') { - return 'int32array'; - } - if (type === '[object Uint32Array]') { - return 'uint32array'; - } - if (type === '[object Float32Array]') { - return 'float32array'; - } - if (type === '[object Float64Array]') { - return 'float64array'; +SourceMapConsumer.prototype.__originalMappings = null; +Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', { + get: function () { + if (!this.__originalMappings) { + this._parseMappings(this._mappings, this.sourceRoot); + } + + return this.__originalMappings; } +}); - // must be a plain object - return 'object'; -}; +SourceMapConsumer.prototype._charIsMappingSeparator = + function SourceMapConsumer_charIsMappingSeparator(aStr, index) { + var c = aStr.charAt(index); + return c === ";" || c === ","; + }; +/** + * Parse the mappings in a string in to a data structure which we can easily + * query (the ordered arrays in the `this.__generatedMappings` and + * `this.__originalMappings` properties). + */ +SourceMapConsumer.prototype._parseMappings = + function SourceMapConsumer_parseMappings(aStr, aSourceRoot) { + throw new Error("Subclasses must implement _parseMappings"); + }; -/***/ }), -/* 658 */ -/***/ (function(module, exports, __webpack_require__) { +SourceMapConsumer.GENERATED_ORDER = 1; +SourceMapConsumer.ORIGINAL_ORDER = 2; -"use strict"; -/*! - * copy-descriptor +SourceMapConsumer.GREATEST_LOWER_BOUND = 1; +SourceMapConsumer.LEAST_UPPER_BOUND = 2; + +/** + * Iterate over each mapping between an original source/line/column and a + * generated line/column in this source map. * - * Copyright (c) 2015, Jon Schlinkert. - * Licensed under the MIT License. + * @param Function aCallback + * The function that is called with each mapping. + * @param Object aContext + * Optional. If specified, this object will be the value of `this` every + * time that `aCallback` is called. + * @param aOrder + * Either `SourceMapConsumer.GENERATED_ORDER` or + * `SourceMapConsumer.ORIGINAL_ORDER`. Specifies whether you want to + * iterate over the mappings sorted by the generated file's line/column + * order or the original's source/line/column order, respectively. Defaults to + * `SourceMapConsumer.GENERATED_ORDER`. */ +SourceMapConsumer.prototype.eachMapping = + function SourceMapConsumer_eachMapping(aCallback, aContext, aOrder) { + var context = aContext || null; + var order = aOrder || SourceMapConsumer.GENERATED_ORDER; + var mappings; + switch (order) { + case SourceMapConsumer.GENERATED_ORDER: + mappings = this._generatedMappings; + break; + case SourceMapConsumer.ORIGINAL_ORDER: + mappings = this._originalMappings; + break; + default: + throw new Error("Unknown order of iteration."); + } + var sourceRoot = this.sourceRoot; + mappings.map(function (mapping) { + var source = mapping.source === null ? null : this._sources.at(mapping.source); + if (source != null && sourceRoot != null) { + source = util.join(sourceRoot, source); + } + return { + source: source, + generatedLine: mapping.generatedLine, + generatedColumn: mapping.generatedColumn, + originalLine: mapping.originalLine, + originalColumn: mapping.originalColumn, + name: mapping.name === null ? null : this._names.at(mapping.name) + }; + }, this).forEach(aCallback, context); + }; /** - * Copy a descriptor from one object to another. - * - * ```js - * function App() { - * this.cache = {}; - * } - * App.prototype.set = function(key, val) { - * this.cache[key] = val; - * return this; - * }; - * Object.defineProperty(App.prototype, 'count', { - * get: function() { - * return Object.keys(this.cache).length; - * } - * }); + * Returns all generated line and column information for the original source, + * line, and column provided. If no column is provided, returns all mappings + * corresponding to a either the line we are searching for or the next + * closest line that has any mappings. Otherwise, returns all mappings + * corresponding to the given line and either the column we are searching for + * or the next closest column that has any offsets. * - * copy(App.prototype, 'count', 'len'); + * The only argument is an object with the following properties: * - * // create an instance - * var app = new App(); + * - source: The filename of the original source. + * - line: The line number in the original source. + * - column: Optional. the column number in the original source. * - * app.set('a', true); - * app.set('b', true); - * app.set('c', true); + * and an array of objects is returned, each with the following properties: * - * console.log(app.count); - * //=> 3 - * console.log(app.len); - * //=> 3 - * ``` - * @name copy - * @param {Object} `receiver` The target object - * @param {Object} `provider` The provider object - * @param {String} `from` The key to copy on provider. - * @param {String} `to` Optionally specify a new key name to use. - * @return {Object} - * @api public + * - line: The line number in the generated source, or null. + * - column: The column number in the generated source, or null. */ +SourceMapConsumer.prototype.allGeneratedPositionsFor = + function SourceMapConsumer_allGeneratedPositionsFor(aArgs) { + var line = util.getArg(aArgs, 'line'); -module.exports = function copyDescriptor(receiver, provider, from, to) { - if (!isObject(provider) && typeof provider !== 'function') { - to = from; - from = provider; - provider = receiver; - } - if (!isObject(receiver) && typeof receiver !== 'function') { - throw new TypeError('expected the first argument to be an object'); - } - if (!isObject(provider) && typeof provider !== 'function') { - throw new TypeError('expected provider to be an object'); - } - - if (typeof to !== 'string') { - to = from; - } - if (typeof from !== 'string') { - throw new TypeError('expected key to be a string'); - } - - if (!(from in provider)) { - throw new Error('property "' + from + '" does not exist'); - } + // When there is no exact match, BasicSourceMapConsumer.prototype._findMapping + // returns the index of the closest mapping less than the needle. By + // setting needle.originalColumn to 0, we thus find the last mapping for + // the given line, provided such a mapping exists. + var needle = { + source: util.getArg(aArgs, 'source'), + originalLine: line, + originalColumn: util.getArg(aArgs, 'column', 0) + }; - var val = Object.getOwnPropertyDescriptor(provider, from); - if (val) Object.defineProperty(receiver, to, val); -}; + if (this.sourceRoot != null) { + needle.source = util.relative(this.sourceRoot, needle.source); + } + if (!this._sources.has(needle.source)) { + return []; + } + needle.source = this._sources.indexOf(needle.source); -function isObject(val) { - return {}.toString.call(val) === '[object Object]'; -} + var mappings = []; + var index = this._findMapping(needle, + this._originalMappings, + "originalLine", + "originalColumn", + util.compareByOriginalPositions, + binarySearch.LEAST_UPPER_BOUND); + if (index >= 0) { + var mapping = this._originalMappings[index]; + if (aArgs.column === undefined) { + var originalLine = mapping.originalLine; -/***/ }), -/* 659 */ -/***/ (function(module, exports, __webpack_require__) { + // Iterate until either we run out of mappings, or we run into + // a mapping for a different line than the one we found. Since + // mappings are sorted, this is guaranteed to find all mappings for + // the line we found. + while (mapping && mapping.originalLine === originalLine) { + mappings.push({ + line: util.getArg(mapping, 'generatedLine', null), + column: util.getArg(mapping, 'generatedColumn', null), + lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null) + }); -"use strict"; + mapping = this._originalMappings[++index]; + } + } else { + var originalColumn = mapping.originalColumn; + // Iterate until either we run out of mappings, or we run into + // a mapping for a different line than the one we were searching for. + // Since mappings are sorted, this is guaranteed to find all mappings for + // the line we are searching for. + while (mapping && + mapping.originalLine === line && + mapping.originalColumn == originalColumn) { + mappings.push({ + line: util.getArg(mapping, 'generatedLine', null), + column: util.getArg(mapping, 'generatedColumn', null), + lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null) + }); -var use = __webpack_require__(660); -var define = __webpack_require__(648); -var debug = __webpack_require__(205)('snapdragon:compiler'); -var utils = __webpack_require__(662); + mapping = this._originalMappings[++index]; + } + } + } -/** - * Create a new `Compiler` with the given `options`. - * @param {Object} `options` - */ + return mappings; + }; -function Compiler(options, state) { - debug('initializing', __filename); - this.options = utils.extend({source: 'string'}, options); - this.state = state || {}; - this.compilers = {}; - this.output = ''; - this.set('eos', function(node) { - return this.emit(node.val, node); - }); - this.set('noop', function(node) { - return this.emit(node.val, node); - }); - this.set('bos', function(node) { - return this.emit(node.val, node); - }); - use(this); -} +exports.SourceMapConsumer = SourceMapConsumer; /** - * Prototype methods - */ - -Compiler.prototype = { - - /** - * Throw an error message with details including the cursor position. - * @param {String} `msg` Message to use in the Error. - */ - - error: function(msg, node) { - var pos = node.position || {start: {column: 0}}; - var message = this.options.source + ' column:' + pos.start.column + ': ' + msg; - - var err = new Error(message); - err.reason = msg; - err.column = pos.start.column; - err.source = this.pattern; - - if (this.options.silent) { - this.errors.push(err); - } else { - throw err; - } - }, - - /** - * Define a non-enumberable property on the `Compiler` instance. - * - * ```js - * compiler.define('foo', 'bar'); - * ``` - * @name .define - * @param {String} `key` propery name - * @param {any} `val` property value - * @return {Object} Returns the Compiler instance for chaining. - * @api public - */ - - define: function(key, val) { - define(this, key, val); - return this; - }, + * A BasicSourceMapConsumer instance represents a parsed source map which we can + * query for information about the original file positions by giving it a file + * position in the generated source. + * + * The only parameter is the raw source map (either as a JSON string, or + * already parsed to an object). According to the spec, source maps have the + * following attributes: + * + * - version: Which version of the source map spec this map is following. + * - sources: An array of URLs to the original source files. + * - names: An array of identifiers which can be referrenced by individual mappings. + * - sourceRoot: Optional. The URL root from which all sources are relative. + * - sourcesContent: Optional. An array of contents of the original source files. + * - mappings: A string of base64 VLQs which contain the actual mappings. + * - file: Optional. The generated file this source map is associated with. + * + * Here is an example source map, taken from the source map spec[0]: + * + * { + * version : 3, + * file: "out.js", + * sourceRoot : "", + * sources: ["foo.js", "bar.js"], + * names: ["src", "maps", "are", "fun"], + * mappings: "AA,AB;;ABCDE;" + * } + * + * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1# + */ +function BasicSourceMapConsumer(aSourceMap) { + var sourceMap = aSourceMap; + if (typeof aSourceMap === 'string') { + sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, '')); + } - /** - * Emit `node.val` - */ + var version = util.getArg(sourceMap, 'version'); + var sources = util.getArg(sourceMap, 'sources'); + // Sass 3.3 leaves out the 'names' array, so we deviate from the spec (which + // requires the array) to play nice here. + var names = util.getArg(sourceMap, 'names', []); + var sourceRoot = util.getArg(sourceMap, 'sourceRoot', null); + var sourcesContent = util.getArg(sourceMap, 'sourcesContent', null); + var mappings = util.getArg(sourceMap, 'mappings'); + var file = util.getArg(sourceMap, 'file', null); - emit: function(str, node) { - this.output += str; - return str; - }, + // Once again, Sass deviates from the spec and supplies the version as a + // string rather than a number, so we use loose equality checking here. + if (version != this._version) { + throw new Error('Unsupported version: ' + version); + } - /** - * Add a compiler `fn` with the given `name` - */ + sources = sources + .map(String) + // Some source maps produce relative source paths like "./foo.js" instead of + // "foo.js". Normalize these first so that future comparisons will succeed. + // See bugzil.la/1090768. + .map(util.normalize) + // Always ensure that absolute sources are internally stored relative to + // the source root, if the source root is absolute. Not doing this would + // be particularly problematic when the source root is a prefix of the + // source (valid, but why??). See github issue #199 and bugzil.la/1188982. + .map(function (source) { + return sourceRoot && util.isAbsolute(sourceRoot) && util.isAbsolute(source) + ? util.relative(sourceRoot, source) + : source; + }); - set: function(name, fn) { - this.compilers[name] = fn; - return this; - }, + // Pass `true` below to allow duplicate names and sources. While source maps + // are intended to be compressed and deduplicated, the TypeScript compiler + // sometimes generates source maps with duplicates in them. See Github issue + // #72 and bugzil.la/889492. + this._names = ArraySet.fromArray(names.map(String), true); + this._sources = ArraySet.fromArray(sources, true); - /** - * Get compiler `name`. - */ + this.sourceRoot = sourceRoot; + this.sourcesContent = sourcesContent; + this._mappings = mappings; + this.file = file; +} - get: function(name) { - return this.compilers[name]; - }, +BasicSourceMapConsumer.prototype = Object.create(SourceMapConsumer.prototype); +BasicSourceMapConsumer.prototype.consumer = SourceMapConsumer; - /** - * Get the previous AST node. - */ +/** + * Create a BasicSourceMapConsumer from a SourceMapGenerator. + * + * @param SourceMapGenerator aSourceMap + * The source map that will be consumed. + * @returns BasicSourceMapConsumer + */ +BasicSourceMapConsumer.fromSourceMap = + function SourceMapConsumer_fromSourceMap(aSourceMap) { + var smc = Object.create(BasicSourceMapConsumer.prototype); - prev: function(n) { - return this.ast.nodes[this.idx - (n || 1)] || { type: 'bos', val: '' }; - }, + var names = smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true); + var sources = smc._sources = ArraySet.fromArray(aSourceMap._sources.toArray(), true); + smc.sourceRoot = aSourceMap._sourceRoot; + smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(), + smc.sourceRoot); + smc.file = aSourceMap._file; - /** - * Get the next AST node. - */ + // Because we are modifying the entries (by converting string sources and + // names to indices into the sources and names ArraySets), we have to make + // a copy of the entry or else bad things happen. Shared mutable state + // strikes again! See github issue #191. - next: function(n) { - return this.ast.nodes[this.idx + (n || 1)] || { type: 'eos', val: '' }; - }, + var generatedMappings = aSourceMap._mappings.toArray().slice(); + var destGeneratedMappings = smc.__generatedMappings = []; + var destOriginalMappings = smc.__originalMappings = []; - /** - * Visit `node`. - */ + for (var i = 0, length = generatedMappings.length; i < length; i++) { + var srcMapping = generatedMappings[i]; + var destMapping = new Mapping; + destMapping.generatedLine = srcMapping.generatedLine; + destMapping.generatedColumn = srcMapping.generatedColumn; - visit: function(node, nodes, i) { - var fn = this.compilers[node.type]; - this.idx = i; + if (srcMapping.source) { + destMapping.source = sources.indexOf(srcMapping.source); + destMapping.originalLine = srcMapping.originalLine; + destMapping.originalColumn = srcMapping.originalColumn; - if (typeof fn !== 'function') { - throw this.error('compiler "' + node.type + '" is not registered', node); - } - return fn.call(this, node, nodes, i); - }, + if (srcMapping.name) { + destMapping.name = names.indexOf(srcMapping.name); + } - /** - * Map visit over array of `nodes`. - */ + destOriginalMappings.push(destMapping); + } - mapVisit: function(nodes) { - if (!Array.isArray(nodes)) { - throw new TypeError('expected an array'); - } - var len = nodes.length; - var idx = -1; - while (++idx < len) { - this.visit(nodes[idx], nodes, idx); + destGeneratedMappings.push(destMapping); } - return this; - }, - /** - * Compile `ast`. - */ + quickSort(smc.__originalMappings, util.compareByOriginalPositions); - compile: function(ast, options) { - var opts = utils.extend({}, this.options, options); - this.ast = ast; - this.parsingErrors = this.ast.errors; - this.output = ''; + return smc; + }; - // source map support - if (opts.sourcemap) { - var sourcemaps = __webpack_require__(681); - sourcemaps(this); - this.mapVisit(this.ast.nodes); - this.applySourceMaps(); - this.map = opts.sourcemap === 'generator' ? this.map : this.map.toJSON(); - return this; - } +/** + * The version of the source mapping spec that we are consuming. + */ +BasicSourceMapConsumer.prototype._version = 3; - this.mapVisit(this.ast.nodes); - return this; +/** + * The list of original sources. + */ +Object.defineProperty(BasicSourceMapConsumer.prototype, 'sources', { + get: function () { + return this._sources.toArray().map(function (s) { + return this.sourceRoot != null ? util.join(this.sourceRoot, s) : s; + }, this); } -}; +}); /** - * Expose `Compiler` + * Provide the JIT with a nice shape / hidden class. */ +function Mapping() { + this.generatedLine = 0; + this.generatedColumn = 0; + this.source = null; + this.originalLine = null; + this.originalColumn = null; + this.name = null; +} -module.exports = Compiler; - - -/***/ }), -/* 660 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/*! - * use - * - * Copyright (c) 2015, 2017, Jon Schlinkert. - * Released under the MIT License. +/** + * Parse the mappings in a string in to a data structure which we can easily + * query (the ordered arrays in the `this.__generatedMappings` and + * `this.__originalMappings` properties). */ +BasicSourceMapConsumer.prototype._parseMappings = + function SourceMapConsumer_parseMappings(aStr, aSourceRoot) { + var generatedLine = 1; + var previousGeneratedColumn = 0; + var previousOriginalLine = 0; + var previousOriginalColumn = 0; + var previousSource = 0; + var previousName = 0; + var length = aStr.length; + var index = 0; + var cachedSegments = {}; + var temp = {}; + var originalMappings = []; + var generatedMappings = []; + var mapping, str, segment, end, value; + while (index < length) { + if (aStr.charAt(index) === ';') { + generatedLine++; + index++; + previousGeneratedColumn = 0; + } + else if (aStr.charAt(index) === ',') { + index++; + } + else { + mapping = new Mapping(); + mapping.generatedLine = generatedLine; + // Because each offset is encoded relative to the previous one, + // many segments often have the same encoding. We can exploit this + // fact by caching the parsed variable length fields of each segment, + // allowing us to avoid a second parse if we encounter the same + // segment again. + for (end = index; end < length; end++) { + if (this._charIsMappingSeparator(aStr, end)) { + break; + } + } + str = aStr.slice(index, end); -var utils = __webpack_require__(661); + segment = cachedSegments[str]; + if (segment) { + index += str.length; + } else { + segment = []; + while (index < end) { + base64VLQ.decode(aStr, index, temp); + value = temp.value; + index = temp.rest; + segment.push(value); + } -module.exports = function base(app, opts) { - if (!utils.isObject(app) && typeof app !== 'function') { - throw new TypeError('use: expect `app` be an object or function'); - } + if (segment.length === 2) { + throw new Error('Found a source, but no line and column'); + } - if (!utils.isObject(opts)) { - opts = {}; - } + if (segment.length === 3) { + throw new Error('Found a source and line, but no column'); + } - var prop = utils.isString(opts.prop) ? opts.prop : 'fns'; - if (!Array.isArray(app[prop])) { - utils.define(app, prop, []); - } + cachedSegments[str] = segment; + } - /** - * Define a plugin function to be passed to use. The only - * parameter exposed to the plugin is `app`, the object or function. - * passed to `use(app)`. `app` is also exposed as `this` in plugins. - * - * Additionally, **if a plugin returns a function, the function will - * be pushed onto the `fns` array**, allowing the plugin to be - * called at a later point by the `run` method. - * - * ```js - * var use = require('use'); - * - * // define a plugin - * function foo(app) { - * // do stuff - * } - * - * var app = function(){}; - * use(app); - * - * // register plugins - * app.use(foo); - * app.use(bar); - * app.use(baz); - * ``` - * @name .use - * @param {Function} `fn` plugin function to call - * @api public - */ + // Generated column. + mapping.generatedColumn = previousGeneratedColumn + segment[0]; + previousGeneratedColumn = mapping.generatedColumn; - utils.define(app, 'use', use); + if (segment.length > 1) { + // Original source. + mapping.source = previousSource + segment[1]; + previousSource += segment[1]; - /** - * Run all plugins on `fns`. Any plugin that returns a function - * when called by `use` is pushed onto the `fns` array. - * - * ```js - * var config = {}; - * app.run(config); - * ``` - * @name .run - * @param {Object} `value` Object to be modified by plugins. - * @return {Object} Returns the object passed to `run` - * @api public - */ + // Original line. + mapping.originalLine = previousOriginalLine + segment[2]; + previousOriginalLine = mapping.originalLine; + // Lines are stored 0-based + mapping.originalLine += 1; - utils.define(app, 'run', function(val) { - if (!utils.isObject(val)) return; - decorate(val); + // Original column. + mapping.originalColumn = previousOriginalColumn + segment[3]; + previousOriginalColumn = mapping.originalColumn; - var self = this || app; - var fns = self[prop]; - var len = fns.length; - var idx = -1; + if (segment.length > 4) { + // Original name. + mapping.name = previousName + segment[4]; + previousName += segment[4]; + } + } - while (++idx < len) { - val.use(fns[idx]); + generatedMappings.push(mapping); + if (typeof mapping.originalLine === 'number') { + originalMappings.push(mapping); + } + } } - return val; - }); - /** - * Call plugin `fn`. If a function is returned push it into the - * `fns` array to be called by the `run` method. - */ + quickSort(generatedMappings, util.compareByGeneratedPositionsDeflated); + this.__generatedMappings = generatedMappings; - function use(fn, options) { - if (typeof fn !== 'function') { - throw new TypeError('.use expects `fn` be a function'); - } + quickSort(originalMappings, util.compareByOriginalPositions); + this.__originalMappings = originalMappings; + }; - var self = this || app; - if (typeof opts.fn === 'function') { - opts.fn.call(self, self, options); - } +/** + * Find the mapping that best matches the hypothetical "needle" mapping that + * we are searching for in the given "haystack" of mappings. + */ +BasicSourceMapConsumer.prototype._findMapping = + function SourceMapConsumer_findMapping(aNeedle, aMappings, aLineName, + aColumnName, aComparator, aBias) { + // To return the position we are searching for, we must first find the + // mapping for the given position and then return the opposite position it + // points to. Because the mappings are sorted, we can use binary search to + // find the best mapping. - var plugin = fn.call(self, self); - if (typeof plugin === 'function') { - var fns = self[prop]; - fns.push(plugin); + if (aNeedle[aLineName] <= 0) { + throw new TypeError('Line must be greater than or equal to 1, got ' + + aNeedle[aLineName]); } - return self; - } - - /** - * Ensure the `.use` method exists on `val` - */ - - function decorate(val) { - if (!val.use || !val.run) { - base(val); + if (aNeedle[aColumnName] < 0) { + throw new TypeError('Column must be greater than or equal to 0, got ' + + aNeedle[aColumnName]); } - } - - return app; -}; - - -/***/ }), -/* 661 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -var utils = {}; - + return binarySearch.search(aNeedle, aMappings, aComparator, aBias); + }; /** - * Lazily required module dependencies + * Compute the last column for each generated mapping. The last column is + * inclusive. */ +BasicSourceMapConsumer.prototype.computeColumnSpans = + function SourceMapConsumer_computeColumnSpans() { + for (var index = 0; index < this._generatedMappings.length; ++index) { + var mapping = this._generatedMappings[index]; -utils.define = __webpack_require__(648); -utils.isObject = __webpack_require__(581); + // Mappings do not contain a field for the last generated columnt. We + // can come up with an optimistic estimate, however, by assuming that + // mappings are contiguous (i.e. given two consecutive mappings, the + // first mapping ends where the second one starts). + if (index + 1 < this._generatedMappings.length) { + var nextMapping = this._generatedMappings[index + 1]; + if (mapping.generatedLine === nextMapping.generatedLine) { + mapping.lastGeneratedColumn = nextMapping.generatedColumn - 1; + continue; + } + } -utils.isString = function(val) { - return val && typeof val === 'string'; -}; + // The last mapping for each line spans the entire line. + mapping.lastGeneratedColumn = Infinity; + } + }; /** - * Expose `utils` modules + * Returns the original source, line, and column information for the generated + * source's line and column positions provided. The only argument is an object + * with the following properties: + * + * - line: The line number in the generated source. + * - column: The column number in the generated source. + * - bias: Either 'SourceMapConsumer.GREATEST_LOWER_BOUND' or + * 'SourceMapConsumer.LEAST_UPPER_BOUND'. Specifies whether to return the + * closest element that is smaller than or greater than the one we are + * searching for, respectively, if the exact element cannot be found. + * Defaults to 'SourceMapConsumer.GREATEST_LOWER_BOUND'. + * + * and an object is returned with the following properties: + * + * - source: The original source file, or null. + * - line: The line number in the original source, or null. + * - column: The column number in the original source, or null. + * - name: The original identifier, or null. */ +BasicSourceMapConsumer.prototype.originalPositionFor = + function SourceMapConsumer_originalPositionFor(aArgs) { + var needle = { + generatedLine: util.getArg(aArgs, 'line'), + generatedColumn: util.getArg(aArgs, 'column') + }; -module.exports = utils; - + var index = this._findMapping( + needle, + this._generatedMappings, + "generatedLine", + "generatedColumn", + util.compareByGeneratedPositionsDeflated, + util.getArg(aArgs, 'bias', SourceMapConsumer.GREATEST_LOWER_BOUND) + ); -/***/ }), -/* 662 */ -/***/ (function(module, exports, __webpack_require__) { + if (index >= 0) { + var mapping = this._generatedMappings[index]; -"use strict"; + if (mapping.generatedLine === needle.generatedLine) { + var source = util.getArg(mapping, 'source', null); + if (source !== null) { + source = this._sources.at(source); + if (this.sourceRoot != null) { + source = util.join(this.sourceRoot, source); + } + } + var name = util.getArg(mapping, 'name', null); + if (name !== null) { + name = this._names.at(name); + } + return { + source: source, + line: util.getArg(mapping, 'originalLine', null), + column: util.getArg(mapping, 'originalColumn', null), + name: name + }; + } + } + return { + source: null, + line: null, + column: null, + name: null + }; + }; /** - * Module dependencies + * Return true if we have the source content for every source in the source + * map, false otherwise. */ - -exports.extend = __webpack_require__(632); -exports.SourceMap = __webpack_require__(663); -exports.sourceMapResolve = __webpack_require__(674); +BasicSourceMapConsumer.prototype.hasContentsOfAllSources = + function BasicSourceMapConsumer_hasContentsOfAllSources() { + if (!this.sourcesContent) { + return false; + } + return this.sourcesContent.length >= this._sources.size() && + !this.sourcesContent.some(function (sc) { return sc == null; }); + }; /** - * Convert backslash in the given string to forward slashes + * Returns the original source content. The only argument is the url of the + * original source file. Returns null if no original source content is + * available. */ +BasicSourceMapConsumer.prototype.sourceContentFor = + function SourceMapConsumer_sourceContentFor(aSource, nullOnMissing) { + if (!this.sourcesContent) { + return null; + } -exports.unixify = function(fp) { - return fp.split(/\\+/).join('/'); -}; + if (this.sourceRoot != null) { + aSource = util.relative(this.sourceRoot, aSource); + } -/** - * Return true if `val` is a non-empty string - * - * @param {String} `str` - * @return {Boolean} - */ + if (this._sources.has(aSource)) { + return this.sourcesContent[this._sources.indexOf(aSource)]; + } -exports.isString = function(str) { - return str && typeof str === 'string'; -}; + var url; + if (this.sourceRoot != null + && (url = util.urlParse(this.sourceRoot))) { + // XXX: file:// URIs and absolute paths lead to unexpected behavior for + // many users. We can help them out when they expect file:// URIs to + // behave like it would if they were running a local HTTP server. See + // https://bugzilla.mozilla.org/show_bug.cgi?id=885597. + var fileUriAbsPath = aSource.replace(/^file:\/\//, ""); + if (url.scheme == "file" + && this._sources.has(fileUriAbsPath)) { + return this.sourcesContent[this._sources.indexOf(fileUriAbsPath)] + } -/** - * Cast `val` to an array - * @return {Array} - */ + if ((!url.path || url.path == "/") + && this._sources.has("/" + aSource)) { + return this.sourcesContent[this._sources.indexOf("/" + aSource)]; + } + } -exports.arrayify = function(val) { - if (typeof val === 'string') return [val]; - return val ? (Array.isArray(val) ? val : [val]) : []; -}; + // This function is used recursively from + // IndexedSourceMapConsumer.prototype.sourceContentFor. In that case, we + // don't want to throw if we can't find the source - we just want to + // return null, so we provide a flag to exit gracefully. + if (nullOnMissing) { + return null; + } + else { + throw new Error('"' + aSource + '" is not in the SourceMap.'); + } + }; /** - * Get the last `n` element from the given `array` - * @param {Array} `array` - * @return {*} + * Returns the generated line and column information for the original source, + * line, and column positions provided. The only argument is an object with + * the following properties: + * + * - source: The filename of the original source. + * - line: The line number in the original source. + * - column: The column number in the original source. + * - bias: Either 'SourceMapConsumer.GREATEST_LOWER_BOUND' or + * 'SourceMapConsumer.LEAST_UPPER_BOUND'. Specifies whether to return the + * closest element that is smaller than or greater than the one we are + * searching for, respectively, if the exact element cannot be found. + * Defaults to 'SourceMapConsumer.GREATEST_LOWER_BOUND'. + * + * and an object is returned with the following properties: + * + * - line: The line number in the generated source, or null. + * - column: The column number in the generated source, or null. */ +BasicSourceMapConsumer.prototype.generatedPositionFor = + function SourceMapConsumer_generatedPositionFor(aArgs) { + var source = util.getArg(aArgs, 'source'); + if (this.sourceRoot != null) { + source = util.relative(this.sourceRoot, source); + } + if (!this._sources.has(source)) { + return { + line: null, + column: null, + lastColumn: null + }; + } + source = this._sources.indexOf(source); -exports.last = function(arr, n) { - return arr[arr.length - (n || 1)]; -}; - - -/***/ }), -/* 663 */ -/***/ (function(module, exports, __webpack_require__) { + var needle = { + source: source, + originalLine: util.getArg(aArgs, 'line'), + originalColumn: util.getArg(aArgs, 'column') + }; -/* - * Copyright 2009-2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE.txt or: - * http://opensource.org/licenses/BSD-3-Clause - */ -exports.SourceMapGenerator = __webpack_require__(664).SourceMapGenerator; -exports.SourceMapConsumer = __webpack_require__(670).SourceMapConsumer; -exports.SourceNode = __webpack_require__(673).SourceNode; + var index = this._findMapping( + needle, + this._originalMappings, + "originalLine", + "originalColumn", + util.compareByOriginalPositions, + util.getArg(aArgs, 'bias', SourceMapConsumer.GREATEST_LOWER_BOUND) + ); + if (index >= 0) { + var mapping = this._originalMappings[index]; -/***/ }), -/* 664 */ -/***/ (function(module, exports, __webpack_require__) { + if (mapping.source === needle.source) { + return { + line: util.getArg(mapping, 'generatedLine', null), + column: util.getArg(mapping, 'generatedColumn', null), + lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null) + }; + } + } -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ + return { + line: null, + column: null, + lastColumn: null + }; + }; -var base64VLQ = __webpack_require__(665); -var util = __webpack_require__(667); -var ArraySet = __webpack_require__(668).ArraySet; -var MappingList = __webpack_require__(669).MappingList; +exports.BasicSourceMapConsumer = BasicSourceMapConsumer; /** - * An instance of the SourceMapGenerator represents a source map which is - * being built incrementally. You may pass an object with the following - * properties: + * An IndexedSourceMapConsumer instance represents a parsed source map which + * we can query for information. It differs from BasicSourceMapConsumer in + * that it takes "indexed" source maps (i.e. ones with a "sections" field) as + * input. * - * - file: The filename of the generated source. - * - sourceRoot: A root for all relative URLs in this source map. - */ -function SourceMapGenerator(aArgs) { - if (!aArgs) { - aArgs = {}; - } - this._file = util.getArg(aArgs, 'file', null); - this._sourceRoot = util.getArg(aArgs, 'sourceRoot', null); - this._skipValidation = util.getArg(aArgs, 'skipValidation', false); - this._sources = new ArraySet(); - this._names = new ArraySet(); - this._mappings = new MappingList(); - this._sourcesContents = null; -} - -SourceMapGenerator.prototype._version = 3; - -/** - * Creates a new SourceMapGenerator based on a SourceMapConsumer + * The only parameter is a raw source map (either as a JSON string, or already + * parsed to an object). According to the spec for indexed source maps, they + * have the following attributes: * - * @param aSourceMapConsumer The SourceMap. + * - version: Which version of the source map spec this map is following. + * - file: Optional. The generated file this source map is associated with. + * - sections: A list of section definitions. + * + * Each value under the "sections" field has two fields: + * - offset: The offset into the original specified at which this section + * begins to apply, defined as an object with a "line" and "column" + * field. + * - map: A source map definition. This source map could also be indexed, + * but doesn't have to be. + * + * Instead of the "map" field, it's also possible to have a "url" field + * specifying a URL to retrieve a source map from, but that's currently + * unsupported. + * + * Here's an example source map, taken from the source map spec[0], but + * modified to omit a section which uses the "url" field. + * + * { + * version : 3, + * file: "app.js", + * sections: [{ + * offset: {line:100, column:10}, + * map: { + * version : 3, + * file: "section.js", + * sources: ["foo.js", "bar.js"], + * names: ["src", "maps", "are", "fun"], + * mappings: "AAAA,E;;ABCDE;" + * } + * }], + * } + * + * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#heading=h.535es3xeprgt */ -SourceMapGenerator.fromSourceMap = - function SourceMapGenerator_fromSourceMap(aSourceMapConsumer) { - var sourceRoot = aSourceMapConsumer.sourceRoot; - var generator = new SourceMapGenerator({ - file: aSourceMapConsumer.file, - sourceRoot: sourceRoot - }); - aSourceMapConsumer.eachMapping(function (mapping) { - var newMapping = { - generated: { - line: mapping.generatedLine, - column: mapping.generatedColumn - } - }; +function IndexedSourceMapConsumer(aSourceMap) { + var sourceMap = aSourceMap; + if (typeof aSourceMap === 'string') { + sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, '')); + } - if (mapping.source != null) { - newMapping.source = mapping.source; - if (sourceRoot != null) { - newMapping.source = util.relative(sourceRoot, newMapping.source); - } + var version = util.getArg(sourceMap, 'version'); + var sections = util.getArg(sourceMap, 'sections'); - newMapping.original = { - line: mapping.originalLine, - column: mapping.originalColumn - }; + if (version != this._version) { + throw new Error('Unsupported version: ' + version); + } - if (mapping.name != null) { - newMapping.name = mapping.name; - } - } + this._sources = new ArraySet(); + this._names = new ArraySet(); - generator.addMapping(newMapping); - }); - aSourceMapConsumer.sources.forEach(function (sourceFile) { - var content = aSourceMapConsumer.sourceContentFor(sourceFile); - if (content != null) { - generator.setSourceContent(sourceFile, content); - } - }); - return generator; + var lastOffset = { + line: -1, + column: 0 }; - -/** - * Add a single mapping from original source line and column to the generated - * source's line and column for this source map being created. The mapping - * object should have the following properties: - * - * - generated: An object with the generated line and column positions. - * - original: An object with the original line and column positions. - * - source: The original source file (relative to the sourceRoot). - * - name: An optional original token name for this mapping. - */ -SourceMapGenerator.prototype.addMapping = - function SourceMapGenerator_addMapping(aArgs) { - var generated = util.getArg(aArgs, 'generated'); - var original = util.getArg(aArgs, 'original', null); - var source = util.getArg(aArgs, 'source', null); - var name = util.getArg(aArgs, 'name', null); - - if (!this._skipValidation) { - this._validateMapping(generated, original, source, name); + this._sections = sections.map(function (s) { + if (s.url) { + // The url field will require support for asynchronicity. + // See https://github.com/mozilla/source-map/issues/16 + throw new Error('Support for url field in sections not implemented.'); } + var offset = util.getArg(s, 'offset'); + var offsetLine = util.getArg(offset, 'line'); + var offsetColumn = util.getArg(offset, 'column'); - if (source != null) { - source = String(source); - if (!this._sources.has(source)) { - this._sources.add(source); - } + if (offsetLine < lastOffset.line || + (offsetLine === lastOffset.line && offsetColumn < lastOffset.column)) { + throw new Error('Section offsets must be ordered and non-overlapping.'); } + lastOffset = offset; - if (name != null) { - name = String(name); - if (!this._names.has(name)) { - this._names.add(name); - } + return { + generatedOffset: { + // The offset fields are 0-based, but we use 1-based indices when + // encoding/decoding from VLQ. + generatedLine: offsetLine + 1, + generatedColumn: offsetColumn + 1 + }, + consumer: new SourceMapConsumer(util.getArg(s, 'map')) } + }); +} - this._mappings.add({ - generatedLine: generated.line, - generatedColumn: generated.column, - originalLine: original != null && original.line, - originalColumn: original != null && original.column, - source: source, - name: name - }); - }; +IndexedSourceMapConsumer.prototype = Object.create(SourceMapConsumer.prototype); +IndexedSourceMapConsumer.prototype.constructor = SourceMapConsumer; /** - * Set the source content for a source file. + * The version of the source mapping spec that we are consuming. */ -SourceMapGenerator.prototype.setSourceContent = - function SourceMapGenerator_setSourceContent(aSourceFile, aSourceContent) { - var source = aSourceFile; - if (this._sourceRoot != null) { - source = util.relative(this._sourceRoot, source); - } +IndexedSourceMapConsumer.prototype._version = 3; - if (aSourceContent != null) { - // Add the source content to the _sourcesContents map. - // Create a new _sourcesContents map if the property is null. - if (!this._sourcesContents) { - this._sourcesContents = Object.create(null); - } - this._sourcesContents[util.toSetString(source)] = aSourceContent; - } else if (this._sourcesContents) { - // Remove the source file from the _sourcesContents map. - // If the _sourcesContents map is empty, set the property to null. - delete this._sourcesContents[util.toSetString(source)]; - if (Object.keys(this._sourcesContents).length === 0) { - this._sourcesContents = null; +/** + * The list of original sources. + */ +Object.defineProperty(IndexedSourceMapConsumer.prototype, 'sources', { + get: function () { + var sources = []; + for (var i = 0; i < this._sections.length; i++) { + for (var j = 0; j < this._sections[i].consumer.sources.length; j++) { + sources.push(this._sections[i].consumer.sources[j]); } } - }; + return sources; + } +}); /** - * Applies the mappings of a sub-source-map for a specific source file to the - * source map being generated. Each mapping to the supplied source file is - * rewritten using the supplied source map. Note: The resolution for the - * resulting mappings is the minimium of this map and the supplied map. + * Returns the original source, line, and column information for the generated + * source's line and column positions provided. The only argument is an object + * with the following properties: * - * @param aSourceMapConsumer The source map to be applied. - * @param aSourceFile Optional. The filename of the source file. - * If omitted, SourceMapConsumer's file property will be used. - * @param aSourceMapPath Optional. The dirname of the path to the source map - * to be applied. If relative, it is relative to the SourceMapConsumer. - * This parameter is needed when the two source maps aren't in the same - * directory, and the source map to be applied contains relative source - * paths. If so, those relative source paths need to be rewritten - * relative to the SourceMapGenerator. + * - line: The line number in the generated source. + * - column: The column number in the generated source. + * + * and an object is returned with the following properties: + * + * - source: The original source file, or null. + * - line: The line number in the original source, or null. + * - column: The column number in the original source, or null. + * - name: The original identifier, or null. */ -SourceMapGenerator.prototype.applySourceMap = - function SourceMapGenerator_applySourceMap(aSourceMapConsumer, aSourceFile, aSourceMapPath) { - var sourceFile = aSourceFile; - // If aSourceFile is omitted, we will use the file property of the SourceMap - if (aSourceFile == null) { - if (aSourceMapConsumer.file == null) { - throw new Error( - 'SourceMapGenerator.prototype.applySourceMap requires either an explicit source file, ' + - 'or the source map\'s "file" property. Both were omitted.' - ); - } - sourceFile = aSourceMapConsumer.file; - } - var sourceRoot = this._sourceRoot; - // Make "sourceFile" relative if an absolute Url is passed. - if (sourceRoot != null) { - sourceFile = util.relative(sourceRoot, sourceFile); - } - // Applying the SourceMap can add and remove items from the sources and - // the names array. - var newSources = new ArraySet(); - var newNames = new ArraySet(); +IndexedSourceMapConsumer.prototype.originalPositionFor = + function IndexedSourceMapConsumer_originalPositionFor(aArgs) { + var needle = { + generatedLine: util.getArg(aArgs, 'line'), + generatedColumn: util.getArg(aArgs, 'column') + }; - // Find mappings for the "sourceFile" - this._mappings.unsortedForEach(function (mapping) { - if (mapping.source === sourceFile && mapping.originalLine != null) { - // Check if it can be mapped by the source map, then update the mapping. - var original = aSourceMapConsumer.originalPositionFor({ - line: mapping.originalLine, - column: mapping.originalColumn - }); - if (original.source != null) { - // Copy mapping - mapping.source = original.source; - if (aSourceMapPath != null) { - mapping.source = util.join(aSourceMapPath, mapping.source) - } - if (sourceRoot != null) { - mapping.source = util.relative(sourceRoot, mapping.source); - } - mapping.originalLine = original.line; - mapping.originalColumn = original.column; - if (original.name != null) { - mapping.name = original.name; - } + // Find the section containing the generated position we're trying to map + // to an original position. + var sectionIndex = binarySearch.search(needle, this._sections, + function(needle, section) { + var cmp = needle.generatedLine - section.generatedOffset.generatedLine; + if (cmp) { + return cmp; } - } - - var source = mapping.source; - if (source != null && !newSources.has(source)) { - newSources.add(source); - } - - var name = mapping.name; - if (name != null && !newNames.has(name)) { - newNames.add(name); - } - }, this); - this._sources = newSources; - this._names = newNames; + return (needle.generatedColumn - + section.generatedOffset.generatedColumn); + }); + var section = this._sections[sectionIndex]; - // Copy sourcesContents of applied map. - aSourceMapConsumer.sources.forEach(function (sourceFile) { - var content = aSourceMapConsumer.sourceContentFor(sourceFile); - if (content != null) { - if (aSourceMapPath != null) { - sourceFile = util.join(aSourceMapPath, sourceFile); - } - if (sourceRoot != null) { - sourceFile = util.relative(sourceRoot, sourceFile); - } - this.setSourceContent(sourceFile, content); - } - }, this); + if (!section) { + return { + source: null, + line: null, + column: null, + name: null + }; + } + + return section.consumer.originalPositionFor({ + line: needle.generatedLine - + (section.generatedOffset.generatedLine - 1), + column: needle.generatedColumn - + (section.generatedOffset.generatedLine === needle.generatedLine + ? section.generatedOffset.generatedColumn - 1 + : 0), + bias: aArgs.bias + }); }; /** - * A mapping can have one of the three levels of data: - * - * 1. Just the generated position. - * 2. The Generated position, original position, and original source. - * 3. Generated and original position, original source, as well as a name - * token. - * - * To maintain consistency, we validate that any new mapping being added falls - * in to one of these categories. + * Return true if we have the source content for every source in the source + * map, false otherwise. */ -SourceMapGenerator.prototype._validateMapping = - function SourceMapGenerator_validateMapping(aGenerated, aOriginal, aSource, - aName) { - // When aOriginal is truthy but has empty values for .line and .column, - // it is most likely a programmer error. In this case we throw a very - // specific error message to try to guide them the right way. - // For example: https://github.com/Polymer/polymer-bundler/pull/519 - if (aOriginal && typeof aOriginal.line !== 'number' && typeof aOriginal.column !== 'number') { - throw new Error( - 'original.line and original.column are not numbers -- you probably meant to omit ' + - 'the original mapping entirely and only map the generated position. If so, pass ' + - 'null for the original mapping instead of an object with empty or null values.' - ); - } +IndexedSourceMapConsumer.prototype.hasContentsOfAllSources = + function IndexedSourceMapConsumer_hasContentsOfAllSources() { + return this._sections.every(function (s) { + return s.consumer.hasContentsOfAllSources(); + }); + }; - if (aGenerated && 'line' in aGenerated && 'column' in aGenerated - && aGenerated.line > 0 && aGenerated.column >= 0 - && !aOriginal && !aSource && !aName) { - // Case 1. - return; +/** + * Returns the original source content. The only argument is the url of the + * original source file. Returns null if no original source content is + * available. + */ +IndexedSourceMapConsumer.prototype.sourceContentFor = + function IndexedSourceMapConsumer_sourceContentFor(aSource, nullOnMissing) { + for (var i = 0; i < this._sections.length; i++) { + var section = this._sections[i]; + + var content = section.consumer.sourceContentFor(aSource, true); + if (content) { + return content; + } } - else if (aGenerated && 'line' in aGenerated && 'column' in aGenerated - && aOriginal && 'line' in aOriginal && 'column' in aOriginal - && aGenerated.line > 0 && aGenerated.column >= 0 - && aOriginal.line > 0 && aOriginal.column >= 0 - && aSource) { - // Cases 2 and 3. - return; + if (nullOnMissing) { + return null; } else { - throw new Error('Invalid mapping: ' + JSON.stringify({ - generated: aGenerated, - source: aSource, - original: aOriginal, - name: aName - })); + throw new Error('"' + aSource + '" is not in the SourceMap.'); } }; /** - * Serialize the accumulated mappings in to the stream of base 64 VLQs - * specified by the source map format. + * Returns the generated line and column information for the original source, + * line, and column positions provided. The only argument is an object with + * the following properties: + * + * - source: The filename of the original source. + * - line: The line number in the original source. + * - column: The column number in the original source. + * + * and an object is returned with the following properties: + * + * - line: The line number in the generated source, or null. + * - column: The column number in the generated source, or null. */ -SourceMapGenerator.prototype._serializeMappings = - function SourceMapGenerator_serializeMappings() { - var previousGeneratedColumn = 0; - var previousGeneratedLine = 1; - var previousOriginalColumn = 0; - var previousOriginalLine = 0; - var previousName = 0; - var previousSource = 0; - var result = ''; - var next; - var mapping; - var nameIdx; - var sourceIdx; - - var mappings = this._mappings.toArray(); - for (var i = 0, len = mappings.length; i < len; i++) { - mapping = mappings[i]; - next = '' +IndexedSourceMapConsumer.prototype.generatedPositionFor = + function IndexedSourceMapConsumer_generatedPositionFor(aArgs) { + for (var i = 0; i < this._sections.length; i++) { + var section = this._sections[i]; - if (mapping.generatedLine !== previousGeneratedLine) { - previousGeneratedColumn = 0; - while (mapping.generatedLine !== previousGeneratedLine) { - next += ';'; - previousGeneratedLine++; - } + // Only consider this section if the requested source is in the list of + // sources of the consumer. + if (section.consumer.sources.indexOf(util.getArg(aArgs, 'source')) === -1) { + continue; } - else { - if (i > 0) { - if (!util.compareByGeneratedPositionsInflated(mapping, mappings[i - 1])) { - continue; - } - next += ','; - } + var generatedPosition = section.consumer.generatedPositionFor(aArgs); + if (generatedPosition) { + var ret = { + line: generatedPosition.line + + (section.generatedOffset.generatedLine - 1), + column: generatedPosition.column + + (section.generatedOffset.generatedLine === generatedPosition.line + ? section.generatedOffset.generatedColumn - 1 + : 0) + }; + return ret; } + } - next += base64VLQ.encode(mapping.generatedColumn - - previousGeneratedColumn); - previousGeneratedColumn = mapping.generatedColumn; + return { + line: null, + column: null + }; + }; - if (mapping.source != null) { - sourceIdx = this._sources.indexOf(mapping.source); - next += base64VLQ.encode(sourceIdx - previousSource); - previousSource = sourceIdx; +/** + * Parse the mappings in a string in to a data structure which we can easily + * query (the ordered arrays in the `this.__generatedMappings` and + * `this.__originalMappings` properties). + */ +IndexedSourceMapConsumer.prototype._parseMappings = + function IndexedSourceMapConsumer_parseMappings(aStr, aSourceRoot) { + this.__generatedMappings = []; + this.__originalMappings = []; + for (var i = 0; i < this._sections.length; i++) { + var section = this._sections[i]; + var sectionMappings = section.consumer._generatedMappings; + for (var j = 0; j < sectionMappings.length; j++) { + var mapping = sectionMappings[j]; - // lines are stored 0-based in SourceMap spec version 3 - next += base64VLQ.encode(mapping.originalLine - 1 - - previousOriginalLine); - previousOriginalLine = mapping.originalLine - 1; + var source = section.consumer._sources.at(mapping.source); + if (section.consumer.sourceRoot !== null) { + source = util.join(section.consumer.sourceRoot, source); + } + this._sources.add(source); + source = this._sources.indexOf(source); - next += base64VLQ.encode(mapping.originalColumn - - previousOriginalColumn); - previousOriginalColumn = mapping.originalColumn; + var name = section.consumer._names.at(mapping.name); + this._names.add(name); + name = this._names.indexOf(name); - if (mapping.name != null) { - nameIdx = this._names.indexOf(mapping.name); - next += base64VLQ.encode(nameIdx - previousName); - previousName = nameIdx; + // The mappings coming from the consumer for the section have + // generated positions relative to the start of the section, so we + // need to offset them to be relative to the start of the concatenated + // generated file. + var adjustedMapping = { + source: source, + generatedLine: mapping.generatedLine + + (section.generatedOffset.generatedLine - 1), + generatedColumn: mapping.generatedColumn + + (section.generatedOffset.generatedLine === mapping.generatedLine + ? section.generatedOffset.generatedColumn - 1 + : 0), + originalLine: mapping.originalLine, + originalColumn: mapping.originalColumn, + name: name + }; + + this.__generatedMappings.push(adjustedMapping); + if (typeof adjustedMapping.originalLine === 'number') { + this.__originalMappings.push(adjustedMapping); } } - - result += next; } - return result; + quickSort(this.__generatedMappings, util.compareByGeneratedPositionsDeflated); + quickSort(this.__originalMappings, util.compareByOriginalPositions); }; -SourceMapGenerator.prototype._generateSourcesContent = - function SourceMapGenerator_generateSourcesContent(aSources, aSourceRoot) { - return aSources.map(function (source) { - if (!this._sourcesContents) { - return null; - } - if (aSourceRoot != null) { - source = util.relative(aSourceRoot, source); - } - var key = util.toSetString(source); - return Object.prototype.hasOwnProperty.call(this._sourcesContents, key) - ? this._sourcesContents[key] - : null; - }, this); - }; +exports.IndexedSourceMapConsumer = IndexedSourceMapConsumer; + + +/***/ }), +/* 677 */ +/***/ (function(module, exports) { + +/* -*- Mode: js; js-indent-level: 2; -*- */ +/* + * Copyright 2011 Mozilla Foundation and contributors + * Licensed under the New BSD license. See LICENSE or: + * http://opensource.org/licenses/BSD-3-Clause + */ + +exports.GREATEST_LOWER_BOUND = 1; +exports.LEAST_UPPER_BOUND = 2; /** - * Externalize the source map. + * Recursive implementation of binary search. + * + * @param aLow Indices here and lower do not contain the needle. + * @param aHigh Indices here and higher do not contain the needle. + * @param aNeedle The element being searched for. + * @param aHaystack The non-empty array being searched. + * @param aCompare Function which takes two elements and returns -1, 0, or 1. + * @param aBias Either 'binarySearch.GREATEST_LOWER_BOUND' or + * 'binarySearch.LEAST_UPPER_BOUND'. Specifies whether to return the + * closest element that is smaller than or greater than the one we are + * searching for, respectively, if the exact element cannot be found. */ -SourceMapGenerator.prototype.toJSON = - function SourceMapGenerator_toJSON() { - var map = { - version: this._version, - sources: this._sources.toArray(), - names: this._names.toArray(), - mappings: this._serializeMappings() - }; - if (this._file != null) { - map.file = this._file; +function recursiveSearch(aLow, aHigh, aNeedle, aHaystack, aCompare, aBias) { + // This function terminates when one of the following is true: + // + // 1. We find the exact element we are looking for. + // + // 2. We did not find the exact element, but we can return the index of + // the next-closest element. + // + // 3. We did not find the exact element, and there is no next-closest + // element than the one we are searching for, so we return -1. + var mid = Math.floor((aHigh - aLow) / 2) + aLow; + var cmp = aCompare(aNeedle, aHaystack[mid], true); + if (cmp === 0) { + // Found the element we are looking for. + return mid; + } + else if (cmp > 0) { + // Our needle is greater than aHaystack[mid]. + if (aHigh - mid > 1) { + // The element is in the upper half. + return recursiveSearch(mid, aHigh, aNeedle, aHaystack, aCompare, aBias); } - if (this._sourceRoot != null) { - map.sourceRoot = this._sourceRoot; + + // The exact needle element was not found in this haystack. Determine if + // we are in termination case (3) or (2) and return the appropriate thing. + if (aBias == exports.LEAST_UPPER_BOUND) { + return aHigh < aHaystack.length ? aHigh : -1; + } else { + return mid; } - if (this._sourcesContents) { - map.sourcesContent = this._generateSourcesContent(map.sources, map.sourceRoot); + } + else { + // Our needle is less than aHaystack[mid]. + if (mid - aLow > 1) { + // The element is in the lower half. + return recursiveSearch(aLow, mid, aNeedle, aHaystack, aCompare, aBias); } - return map; - }; + // we are in termination case (3) or (2) and return the appropriate thing. + if (aBias == exports.LEAST_UPPER_BOUND) { + return mid; + } else { + return aLow < 0 ? -1 : aLow; + } + } +} /** - * Render the source map being generated to a string. + * This is an implementation of binary search which will always try and return + * the index of the closest element if there is no exact hit. This is because + * mappings between original and generated line/col pairs are single points, + * and there is an implicit region between each of them, so a miss just means + * that you aren't on the very start of a region. + * + * @param aNeedle The element you are looking for. + * @param aHaystack The array that is being searched. + * @param aCompare A function which takes the needle and an element in the + * array and returns -1, 0, or 1 depending on whether the needle is less + * than, equal to, or greater than the element, respectively. + * @param aBias Either 'binarySearch.GREATEST_LOWER_BOUND' or + * 'binarySearch.LEAST_UPPER_BOUND'. Specifies whether to return the + * closest element that is smaller than or greater than the one we are + * searching for, respectively, if the exact element cannot be found. + * Defaults to 'binarySearch.GREATEST_LOWER_BOUND'. */ -SourceMapGenerator.prototype.toString = - function SourceMapGenerator_toString() { - return JSON.stringify(this.toJSON()); - }; +exports.search = function search(aNeedle, aHaystack, aCompare, aBias) { + if (aHaystack.length === 0) { + return -1; + } -exports.SourceMapGenerator = SourceMapGenerator; + var index = recursiveSearch(-1, aHaystack.length, aNeedle, aHaystack, + aCompare, aBias || exports.GREATEST_LOWER_BOUND); + if (index < 0) { + return -1; + } + + // We have found either the exact element, or the next-closest element than + // the one we are searching for. However, there may be more than one such + // element. Make sure we always return the smallest of these. + while (index - 1 >= 0) { + if (aCompare(aHaystack[index], aHaystack[index - 1], true) !== 0) { + break; + } + --index; + } + + return index; +}; /***/ }), -/* 665 */ -/***/ (function(module, exports, __webpack_require__) { +/* 678 */ +/***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ /* * Copyright 2011 Mozilla Foundation and contributors * Licensed under the New BSD license. See LICENSE or: * http://opensource.org/licenses/BSD-3-Clause - * - * Based on the Base 64 VLQ implementation in Closure Compiler: - * https://code.google.com/p/closure-compiler/source/browse/trunk/src/com/google/debugging/sourcemap/Base64VLQ.java - * - * Copyright 2011 The Closure Compiler Authors. All rights reserved. - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -var base64 = __webpack_require__(666); - -// A single base 64 digit can contain 6 bits of data. For the base 64 variable -// length quantities we use in the source map spec, the first bit is the sign, -// the next four bits are the actual value, and the 6th bit is the -// continuation bit. The continuation bit tells us whether there are more -// digits in this value following this digit. -// -// Continuation -// | Sign -// | | -// V V -// 101011 - -var VLQ_BASE_SHIFT = 5; - -// binary: 100000 -var VLQ_BASE = 1 << VLQ_BASE_SHIFT; - -// binary: 011111 -var VLQ_BASE_MASK = VLQ_BASE - 1; - -// binary: 100000 -var VLQ_CONTINUATION_BIT = VLQ_BASE; +// It turns out that some (most?) JavaScript engines don't self-host +// `Array.prototype.sort`. This makes sense because C++ will likely remain +// faster than JS when doing raw CPU-intensive sorting. However, when using a +// custom comparator function, calling back and forth between the VM's C++ and +// JIT'd JS is rather slow *and* loses JIT type information, resulting in +// worse generated code for the comparator function than would be optimal. In +// fact, when sorting with a comparator, these costs outweigh the benefits of +// sorting in C++. By using our own JS-implemented Quick Sort (below), we get +// a ~3500ms mean speed-up in `bench/bench.html`. /** - * Converts from a two-complement value to a value where the sign bit is - * placed in the least significant bit. For example, as decimals: - * 1 becomes 2 (10 binary), -1 becomes 3 (11 binary) - * 2 becomes 4 (100 binary), -2 becomes 5 (101 binary) + * Swap the elements indexed by `x` and `y` in the array `ary`. + * + * @param {Array} ary + * The array. + * @param {Number} x + * The index of the first item. + * @param {Number} y + * The index of the second item. */ -function toVLQSigned(aValue) { - return aValue < 0 - ? ((-aValue) << 1) + 1 - : (aValue << 1) + 0; +function swap(ary, x, y) { + var temp = ary[x]; + ary[x] = ary[y]; + ary[y] = temp; } /** - * Converts to a two-complement value from a value where the sign bit is - * placed in the least significant bit. For example, as decimals: - * 2 (10 binary) becomes 1, 3 (11 binary) becomes -1 - * 4 (100 binary) becomes 2, 5 (101 binary) becomes -2 + * Returns a random integer within the range `low .. high` inclusive. + * + * @param {Number} low + * The lower bound on the range. + * @param {Number} high + * The upper bound on the range. */ -function fromVLQSigned(aValue) { - var isNegative = (aValue & 1) === 1; - var shifted = aValue >> 1; - return isNegative - ? -shifted - : shifted; +function randomIntInRange(low, high) { + return Math.round(low + (Math.random() * (high - low))); } /** - * Returns the base 64 VLQ encoded value. + * The Quick Sort algorithm. + * + * @param {Array} ary + * An array to sort. + * @param {function} comparator + * Function to use to compare two items. + * @param {Number} p + * Start index of the array + * @param {Number} r + * End index of the array */ -exports.encode = function base64VLQ_encode(aValue) { - var encoded = ""; - var digit; - - var vlq = toVLQSigned(aValue); - - do { - digit = vlq & VLQ_BASE_MASK; - vlq >>>= VLQ_BASE_SHIFT; - if (vlq > 0) { - // There are still more digits in this value, so we must make sure the - // continuation bit is marked. - digit |= VLQ_CONTINUATION_BIT; - } - encoded += base64.encode(digit); - } while (vlq > 0); +function doQuickSort(ary, comparator, p, r) { + // If our lower bound is less than our upper bound, we (1) partition the + // array into two pieces and (2) recurse on each half. If it is not, this is + // the empty array and our base case. - return encoded; -}; + if (p < r) { + // (1) Partitioning. + // + // The partitioning chooses a pivot between `p` and `r` and moves all + // elements that are less than or equal to the pivot to the before it, and + // all the elements that are greater than it after it. The effect is that + // once partition is done, the pivot is in the exact place it will be when + // the array is put in sorted order, and it will not need to be moved + // again. This runs in O(n) time. -/** - * Decodes the next base 64 VLQ value from the given string and returns the - * value and the rest of the string via the out parameter. - */ -exports.decode = function base64VLQ_decode(aStr, aIndex, aOutParam) { - var strLen = aStr.length; - var result = 0; - var shift = 0; - var continuation, digit; + // Always choose a random pivot so that an input array which is reverse + // sorted does not cause O(n^2) running time. + var pivotIndex = randomIntInRange(p, r); + var i = p - 1; - do { - if (aIndex >= strLen) { - throw new Error("Expected more digits in base 64 VLQ value."); - } + swap(ary, pivotIndex, r); + var pivot = ary[r]; - digit = base64.decode(aStr.charCodeAt(aIndex++)); - if (digit === -1) { - throw new Error("Invalid base64 digit: " + aStr.charAt(aIndex - 1)); + // Immediately after `j` is incremented in this loop, the following hold + // true: + // + // * Every element in `ary[p .. i]` is less than or equal to the pivot. + // + // * Every element in `ary[i+1 .. j-1]` is greater than the pivot. + for (var j = p; j < r; j++) { + if (comparator(ary[j], pivot) <= 0) { + i += 1; + swap(ary, i, j); + } } - continuation = !!(digit & VLQ_CONTINUATION_BIT); - digit &= VLQ_BASE_MASK; - result = result + (digit << shift); - shift += VLQ_BASE_SHIFT; - } while (continuation); + swap(ary, i + 1, j); + var q = i + 1; - aOutParam.value = fromVLQSigned(result); - aOutParam.rest = aIndex; + // (2) Recurse on each half. + + doQuickSort(ary, comparator, p, q - 1); + doQuickSort(ary, comparator, q + 1, r); + } +} + +/** + * Sort the given array in-place with the given comparator function. + * + * @param {Array} ary + * An array to sort. + * @param {function} comparator + * Function to use to compare two items. + */ +exports.quickSort = function (ary, comparator) { + doQuickSort(ary, comparator, 0, ary.length - 1); }; /***/ }), -/* 666 */ -/***/ (function(module, exports) { +/* 679 */ +/***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ /* @@ -73890,8352 +78028,8156 @@ exports.decode = function base64VLQ_decode(aStr, aIndex, aOutParam) { * http://opensource.org/licenses/BSD-3-Clause */ -var intToCharMap = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split(''); +var SourceMapGenerator = __webpack_require__(670).SourceMapGenerator; +var util = __webpack_require__(673); + +// Matches a Windows-style `\r\n` newline or a `\n` newline used by all other +// operating systems these days (capturing the result). +var REGEX_NEWLINE = /(\r?\n)/; + +// Newline character code for charCodeAt() comparisons +var NEWLINE_CODE = 10; + +// Private symbol for identifying `SourceNode`s when multiple versions of +// the source-map library are loaded. This MUST NOT CHANGE across +// versions! +var isSourceNode = "$$$isSourceNode$$$"; /** - * Encode an integer in the range of 0 to 63 to a single base 64 digit. + * SourceNodes provide a way to abstract over interpolating/concatenating + * snippets of generated JavaScript source code while maintaining the line and + * column information associated with the original source code. + * + * @param aLine The original line number. + * @param aColumn The original column number. + * @param aSource The original source's filename. + * @param aChunks Optional. An array of strings which are snippets of + * generated JS, or other SourceNodes. + * @param aName The original identifier. */ -exports.encode = function (number) { - if (0 <= number && number < intToCharMap.length) { - return intToCharMap[number]; - } - throw new TypeError("Must be between 0 and 63: " + number); -}; +function SourceNode(aLine, aColumn, aSource, aChunks, aName) { + this.children = []; + this.sourceContents = {}; + this.line = aLine == null ? null : aLine; + this.column = aColumn == null ? null : aColumn; + this.source = aSource == null ? null : aSource; + this.name = aName == null ? null : aName; + this[isSourceNode] = true; + if (aChunks != null) this.add(aChunks); +} /** - * Decode a single base 64 character code digit to an integer. Returns -1 on - * failure. + * Creates a SourceNode from generated code and a SourceMapConsumer. + * + * @param aGeneratedCode The generated code + * @param aSourceMapConsumer The SourceMap for the generated code + * @param aRelativePath Optional. The path that relative sources in the + * SourceMapConsumer should be relative to. */ -exports.decode = function (charCode) { - var bigA = 65; // 'A' - var bigZ = 90; // 'Z' +SourceNode.fromStringWithSourceMap = + function SourceNode_fromStringWithSourceMap(aGeneratedCode, aSourceMapConsumer, aRelativePath) { + // The SourceNode we want to fill with the generated code + // and the SourceMap + var node = new SourceNode(); - var littleA = 97; // 'a' - var littleZ = 122; // 'z' + // All even indices of this array are one line of the generated code, + // while all odd indices are the newlines between two adjacent lines + // (since `REGEX_NEWLINE` captures its match). + // Processed fragments are accessed by calling `shiftNextLine`. + var remainingLines = aGeneratedCode.split(REGEX_NEWLINE); + var remainingLinesIndex = 0; + var shiftNextLine = function() { + var lineContents = getNextLine(); + // The last line of a file might not have a newline. + var newLine = getNextLine() || ""; + return lineContents + newLine; - var zero = 48; // '0' - var nine = 57; // '9' + function getNextLine() { + return remainingLinesIndex < remainingLines.length ? + remainingLines[remainingLinesIndex++] : undefined; + } + }; - var plus = 43; // '+' - var slash = 47; // '/' + // We need to remember the position of "remainingLines" + var lastGeneratedLine = 1, lastGeneratedColumn = 0; - var littleOffset = 26; - var numberOffset = 52; + // The generate SourceNodes we need a code range. + // To extract it current and last mapping is used. + // Here we store the last mapping. + var lastMapping = null; - // 0 - 25: ABCDEFGHIJKLMNOPQRSTUVWXYZ - if (bigA <= charCode && charCode <= bigZ) { - return (charCode - bigA); - } + aSourceMapConsumer.eachMapping(function (mapping) { + if (lastMapping !== null) { + // We add the code from "lastMapping" to "mapping": + // First check if there is a new line in between. + if (lastGeneratedLine < mapping.generatedLine) { + // Associate first line with "lastMapping" + addMappingWithCode(lastMapping, shiftNextLine()); + lastGeneratedLine++; + lastGeneratedColumn = 0; + // The remaining code is added without mapping + } else { + // There is no new line in between. + // Associate the code between "lastGeneratedColumn" and + // "mapping.generatedColumn" with "lastMapping" + var nextLine = remainingLines[remainingLinesIndex]; + var code = nextLine.substr(0, mapping.generatedColumn - + lastGeneratedColumn); + remainingLines[remainingLinesIndex] = nextLine.substr(mapping.generatedColumn - + lastGeneratedColumn); + lastGeneratedColumn = mapping.generatedColumn; + addMappingWithCode(lastMapping, code); + // No more remaining code, continue + lastMapping = mapping; + return; + } + } + // We add the generated code until the first mapping + // to the SourceNode without any mapping. + // Each line is added as separate string. + while (lastGeneratedLine < mapping.generatedLine) { + node.add(shiftNextLine()); + lastGeneratedLine++; + } + if (lastGeneratedColumn < mapping.generatedColumn) { + var nextLine = remainingLines[remainingLinesIndex]; + node.add(nextLine.substr(0, mapping.generatedColumn)); + remainingLines[remainingLinesIndex] = nextLine.substr(mapping.generatedColumn); + lastGeneratedColumn = mapping.generatedColumn; + } + lastMapping = mapping; + }, this); + // We have processed all mappings. + if (remainingLinesIndex < remainingLines.length) { + if (lastMapping) { + // Associate the remaining code in the current line with "lastMapping" + addMappingWithCode(lastMapping, shiftNextLine()); + } + // and add the remaining lines without any mapping + node.add(remainingLines.splice(remainingLinesIndex).join("")); + } - // 26 - 51: abcdefghijklmnopqrstuvwxyz - if (littleA <= charCode && charCode <= littleZ) { - return (charCode - littleA + littleOffset); - } + // Copy sourcesContent into SourceNode + aSourceMapConsumer.sources.forEach(function (sourceFile) { + var content = aSourceMapConsumer.sourceContentFor(sourceFile); + if (content != null) { + if (aRelativePath != null) { + sourceFile = util.join(aRelativePath, sourceFile); + } + node.setSourceContent(sourceFile, content); + } + }); - // 52 - 61: 0123456789 - if (zero <= charCode && charCode <= nine) { - return (charCode - zero + numberOffset); - } + return node; - // 62: + - if (charCode == plus) { - return 62; - } + function addMappingWithCode(mapping, code) { + if (mapping === null || mapping.source === undefined) { + node.add(code); + } else { + var source = aRelativePath + ? util.join(aRelativePath, mapping.source) + : mapping.source; + node.add(new SourceNode(mapping.originalLine, + mapping.originalColumn, + source, + code, + mapping.name)); + } + } + }; - // 63: / - if (charCode == slash) { - return 63; +/** + * Add a chunk of generated JS to this source node. + * + * @param aChunk A string snippet of generated JS code, another instance of + * SourceNode, or an array where each member is one of those things. + */ +SourceNode.prototype.add = function SourceNode_add(aChunk) { + if (Array.isArray(aChunk)) { + aChunk.forEach(function (chunk) { + this.add(chunk); + }, this); } - - // Invalid base64 digit. - return -1; + else if (aChunk[isSourceNode] || typeof aChunk === "string") { + if (aChunk) { + this.children.push(aChunk); + } + } + else { + throw new TypeError( + "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk + ); + } + return this; }; - -/***/ }), -/* 667 */ -/***/ (function(module, exports) { - -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause +/** + * Add a chunk of generated JS to the beginning of this source node. + * + * @param aChunk A string snippet of generated JS code, another instance of + * SourceNode, or an array where each member is one of those things. */ +SourceNode.prototype.prepend = function SourceNode_prepend(aChunk) { + if (Array.isArray(aChunk)) { + for (var i = aChunk.length-1; i >= 0; i--) { + this.prepend(aChunk[i]); + } + } + else if (aChunk[isSourceNode] || typeof aChunk === "string") { + this.children.unshift(aChunk); + } + else { + throw new TypeError( + "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk + ); + } + return this; +}; /** - * This is a helper function for getting values from parameter/options - * objects. + * Walk over the tree of JS snippets in this node and its children. The + * walking function is called once for each snippet of JS and is passed that + * snippet and the its original associated source's line/column location. * - * @param args The object we are extracting values from - * @param name The name of the property we are getting. - * @param defaultValue An optional value to return if the property is missing - * from the object. If this is not specified and the property is missing, an - * error will be thrown. + * @param aFn The traversal function. */ -function getArg(aArgs, aName, aDefaultValue) { - if (aName in aArgs) { - return aArgs[aName]; - } else if (arguments.length === 3) { - return aDefaultValue; - } else { - throw new Error('"' + aName + '" is a required argument.'); +SourceNode.prototype.walk = function SourceNode_walk(aFn) { + var chunk; + for (var i = 0, len = this.children.length; i < len; i++) { + chunk = this.children[i]; + if (chunk[isSourceNode]) { + chunk.walk(aFn); + } + else { + if (chunk !== '') { + aFn(chunk, { source: this.source, + line: this.line, + column: this.column, + name: this.name }); + } + } } -} -exports.getArg = getArg; - -var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.]*)(?::(\d+))?(\S*)$/; -var dataUrlRegexp = /^data:.+\,.+$/; +}; -function urlParse(aUrl) { - var match = aUrl.match(urlRegexp); - if (!match) { - return null; +/** + * Like `String.prototype.join` except for SourceNodes. Inserts `aStr` between + * each of `this.children`. + * + * @param aSep The separator. + */ +SourceNode.prototype.join = function SourceNode_join(aSep) { + var newChildren; + var i; + var len = this.children.length; + if (len > 0) { + newChildren = []; + for (i = 0; i < len-1; i++) { + newChildren.push(this.children[i]); + newChildren.push(aSep); + } + newChildren.push(this.children[i]); + this.children = newChildren; } - return { - scheme: match[1], - auth: match[2], - host: match[3], - port: match[4], - path: match[5] - }; -} -exports.urlParse = urlParse; + return this; +}; -function urlGenerate(aParsedUrl) { - var url = ''; - if (aParsedUrl.scheme) { - url += aParsedUrl.scheme + ':'; - } - url += '//'; - if (aParsedUrl.auth) { - url += aParsedUrl.auth + '@'; - } - if (aParsedUrl.host) { - url += aParsedUrl.host; +/** + * Call String.prototype.replace on the very right-most source snippet. Useful + * for trimming whitespace from the end of a source node, etc. + * + * @param aPattern The pattern to replace. + * @param aReplacement The thing to replace the pattern with. + */ +SourceNode.prototype.replaceRight = function SourceNode_replaceRight(aPattern, aReplacement) { + var lastChild = this.children[this.children.length - 1]; + if (lastChild[isSourceNode]) { + lastChild.replaceRight(aPattern, aReplacement); } - if (aParsedUrl.port) { - url += ":" + aParsedUrl.port + else if (typeof lastChild === 'string') { + this.children[this.children.length - 1] = lastChild.replace(aPattern, aReplacement); } - if (aParsedUrl.path) { - url += aParsedUrl.path; + else { + this.children.push(''.replace(aPattern, aReplacement)); } - return url; -} -exports.urlGenerate = urlGenerate; + return this; +}; /** - * Normalizes a path, or the path portion of a URL: - * - * - Replaces consecutive slashes with one slash. - * - Removes unnecessary '.' parts. - * - Removes unnecessary '/..' parts. + * Set the source content for a source file. This will be added to the SourceMapGenerator + * in the sourcesContent field. * - * Based on code in the Node.js 'path' core module. + * @param aSourceFile The filename of the source file + * @param aSourceContent The content of the source file + */ +SourceNode.prototype.setSourceContent = + function SourceNode_setSourceContent(aSourceFile, aSourceContent) { + this.sourceContents[util.toSetString(aSourceFile)] = aSourceContent; + }; + +/** + * Walk over the tree of SourceNodes. The walking function is called for each + * source file content and is passed the filename and source content. * - * @param aPath The path or url to normalize. + * @param aFn The traversal function. */ -function normalize(aPath) { - var path = aPath; - var url = urlParse(aPath); - if (url) { - if (!url.path) { - return aPath; +SourceNode.prototype.walkSourceContents = + function SourceNode_walkSourceContents(aFn) { + for (var i = 0, len = this.children.length; i < len; i++) { + if (this.children[i][isSourceNode]) { + this.children[i].walkSourceContents(aFn); + } } - path = url.path; - } - var isAbsolute = exports.isAbsolute(path); - var parts = path.split(/\/+/); - for (var part, up = 0, i = parts.length - 1; i >= 0; i--) { - part = parts[i]; - if (part === '.') { - parts.splice(i, 1); - } else if (part === '..') { - up++; - } else if (up > 0) { - if (part === '') { - // The first part is blank if the path is absolute. Trying to go - // above the root is a no-op. Therefore we can remove all '..' parts - // directly after the root. - parts.splice(i + 1, up); - up = 0; + var sources = Object.keys(this.sourceContents); + for (var i = 0, len = sources.length; i < len; i++) { + aFn(util.fromSetString(sources[i]), this.sourceContents[sources[i]]); + } + }; + +/** + * Return the string representation of this source node. Walks over the tree + * and concatenates all the various snippets together to one string. + */ +SourceNode.prototype.toString = function SourceNode_toString() { + var str = ""; + this.walk(function (chunk) { + str += chunk; + }); + return str; +}; + +/** + * Returns the string representation of this source node along with a source + * map. + */ +SourceNode.prototype.toStringWithSourceMap = function SourceNode_toStringWithSourceMap(aArgs) { + var generated = { + code: "", + line: 1, + column: 0 + }; + var map = new SourceMapGenerator(aArgs); + var sourceMappingActive = false; + var lastOriginalSource = null; + var lastOriginalLine = null; + var lastOriginalColumn = null; + var lastOriginalName = null; + this.walk(function (chunk, original) { + generated.code += chunk; + if (original.source !== null + && original.line !== null + && original.column !== null) { + if(lastOriginalSource !== original.source + || lastOriginalLine !== original.line + || lastOriginalColumn !== original.column + || lastOriginalName !== original.name) { + map.addMapping({ + source: original.source, + original: { + line: original.line, + column: original.column + }, + generated: { + line: generated.line, + column: generated.column + }, + name: original.name + }); + } + lastOriginalSource = original.source; + lastOriginalLine = original.line; + lastOriginalColumn = original.column; + lastOriginalName = original.name; + sourceMappingActive = true; + } else if (sourceMappingActive) { + map.addMapping({ + generated: { + line: generated.line, + column: generated.column + } + }); + lastOriginalSource = null; + sourceMappingActive = false; + } + for (var idx = 0, length = chunk.length; idx < length; idx++) { + if (chunk.charCodeAt(idx) === NEWLINE_CODE) { + generated.line++; + generated.column = 0; + // Mappings end at eol + if (idx + 1 === length) { + lastOriginalSource = null; + sourceMappingActive = false; + } else if (sourceMappingActive) { + map.addMapping({ + source: original.source, + original: { + line: original.line, + column: original.column + }, + generated: { + line: generated.line, + column: generated.column + }, + name: original.name + }); + } } else { - parts.splice(i, 2); - up--; + generated.column++; } } - } - path = parts.join('/'); + }); + this.walkSourceContents(function (sourceFile, sourceContent) { + map.setSourceContent(sourceFile, sourceContent); + }); - if (path === '') { - path = isAbsolute ? '/' : '.'; - } + return { code: generated.code, map: map }; +}; - if (url) { - url.path = path; - return urlGenerate(url); - } - return path; -} -exports.normalize = normalize; +exports.SourceNode = SourceNode; -/** - * Joins two paths/URLs. - * - * @param aRoot The root path or URL. - * @param aPath The path or URL to be joined with the root. - * - * - If aPath is a URL or a data URI, aPath is returned, unless aPath is a - * scheme-relative URL: Then the scheme of aRoot, if any, is prepended - * first. - * - Otherwise aPath is a path. If aRoot is a URL, then its path portion - * is updated with the result and aRoot is returned. Otherwise the result - * is returned. - * - If aPath is absolute, the result is aPath. - * - Otherwise the two paths are joined with a slash. - * - Joining for example 'http://' and 'www.example.com' is also supported. - */ -function join(aRoot, aPath) { - if (aRoot === "") { - aRoot = "."; - } - if (aPath === "") { - aPath = "."; - } - var aPathUrl = urlParse(aPath); - var aRootUrl = urlParse(aRoot); - if (aRootUrl) { - aRoot = aRootUrl.path || '/'; - } - // `join(foo, '//www.example.org')` - if (aPathUrl && !aPathUrl.scheme) { - if (aRootUrl) { - aPathUrl.scheme = aRootUrl.scheme; - } - return urlGenerate(aPathUrl); - } +/***/ }), +/* 680 */ +/***/ (function(module, exports, __webpack_require__) { - if (aPathUrl || aPath.match(dataUrlRegexp)) { - return aPath; - } +// Copyright 2014, 2015, 2016, 2017 Simon Lydell +// X11 (“MIT”) Licensed. (See LICENSE.) - // `join('http://', 'www.example.com')` - if (aRootUrl && !aRootUrl.host && !aRootUrl.path) { - aRootUrl.host = aPath; - return urlGenerate(aRootUrl); - } +var sourceMappingURL = __webpack_require__(681) +var resolveUrl = __webpack_require__(682) +var decodeUriComponent = __webpack_require__(683) +var urix = __webpack_require__(685) +var atob = __webpack_require__(686) - var joined = aPath.charAt(0) === '/' - ? aPath - : normalize(aRoot.replace(/\/+$/, '') + '/' + aPath); - if (aRootUrl) { - aRootUrl.path = joined; - return urlGenerate(aRootUrl); - } - return joined; -} -exports.join = join; -exports.isAbsolute = function (aPath) { - return aPath.charAt(0) === '/' || !!aPath.match(urlRegexp); -}; +function callbackAsync(callback, error, result) { + setImmediate(function() { callback(error, result) }) +} -/** - * Make a path relative to a URL or another path. - * - * @param aRoot The root path or URL. - * @param aPath The path or URL to be made relative to aRoot. - */ -function relative(aRoot, aPath) { - if (aRoot === "") { - aRoot = "."; +function parseMapToJSON(string, data) { + try { + return JSON.parse(string.replace(/^\)\]\}'/, "")) + } catch (error) { + error.sourceMapData = data + throw error } +} - aRoot = aRoot.replace(/\/$/, ''); - - // It is possible for the path to be above the root. In this case, simply - // checking whether the root is a prefix of the path won't work. Instead, we - // need to remove components from the root one by one, until either we find - // a prefix that fits, or we run out of components to remove. - var level = 0; - while (aPath.indexOf(aRoot + '/') !== 0) { - var index = aRoot.lastIndexOf("/"); - if (index < 0) { - return aPath; - } - - // If the only part of the root that is left is the scheme (i.e. http://, - // file:///, etc.), one or more slashes (/), or simply nothing at all, we - // have exhausted all components, so the path is not relative to the root. - aRoot = aRoot.slice(0, index); - if (aRoot.match(/^([^\/]+:\/)?\/*$/)) { - return aPath; - } - - ++level; +function readSync(read, url, data) { + var readUrl = decodeUriComponent(url) + try { + return String(read(readUrl)) + } catch (error) { + error.sourceMapData = data + throw error } - - // Make sure we add a "../" for each component we removed from the root. - return Array(level + 1).join("../") + aPath.substr(aRoot.length + 1); } -exports.relative = relative; -var supportsNullProto = (function () { - var obj = Object.create(null); - return !('__proto__' in obj); -}()); -function identity (s) { - return s; -} -/** - * Because behavior goes wacky when you set `__proto__` on objects, we - * have to prefix all the strings in our set with an arbitrary character. - * - * See https://github.com/mozilla/source-map/pull/31 and - * https://github.com/mozilla/source-map/issues/30 - * - * @param String aStr - */ -function toSetString(aStr) { - if (isProtoString(aStr)) { - return '$' + aStr; +function resolveSourceMap(code, codeUrl, read, callback) { + var mapData + try { + mapData = resolveSourceMapHelper(code, codeUrl) + } catch (error) { + return callbackAsync(callback, error) } - - return aStr; -} -exports.toSetString = supportsNullProto ? identity : toSetString; - -function fromSetString(aStr) { - if (isProtoString(aStr)) { - return aStr.slice(1); + if (!mapData || mapData.map) { + return callbackAsync(callback, null, mapData) } - - return aStr; + var readUrl = decodeUriComponent(mapData.url) + read(readUrl, function(error, result) { + if (error) { + error.sourceMapData = mapData + return callback(error) + } + mapData.map = String(result) + try { + mapData.map = parseMapToJSON(mapData.map, mapData) + } catch (error) { + return callback(error) + } + callback(null, mapData) + }) } -exports.fromSetString = supportsNullProto ? identity : fromSetString; -function isProtoString(s) { - if (!s) { - return false; +function resolveSourceMapSync(code, codeUrl, read) { + var mapData = resolveSourceMapHelper(code, codeUrl) + if (!mapData || mapData.map) { + return mapData } + mapData.map = readSync(read, mapData.url, mapData) + mapData.map = parseMapToJSON(mapData.map, mapData) + return mapData +} - var length = s.length; +var dataUriRegex = /^data:([^,;]*)(;[^,;]*)*(?:,(.*))?$/ +var jsonMimeTypeRegex = /^(?:application|text)\/json$/ - if (length < 9 /* "__proto__".length */) { - return false; - } +function resolveSourceMapHelper(code, codeUrl) { + codeUrl = urix(codeUrl) - if (s.charCodeAt(length - 1) !== 95 /* '_' */ || - s.charCodeAt(length - 2) !== 95 /* '_' */ || - s.charCodeAt(length - 3) !== 111 /* 'o' */ || - s.charCodeAt(length - 4) !== 116 /* 't' */ || - s.charCodeAt(length - 5) !== 111 /* 'o' */ || - s.charCodeAt(length - 6) !== 114 /* 'r' */ || - s.charCodeAt(length - 7) !== 112 /* 'p' */ || - s.charCodeAt(length - 8) !== 95 /* '_' */ || - s.charCodeAt(length - 9) !== 95 /* '_' */) { - return false; + var url = sourceMappingURL.getFrom(code) + if (!url) { + return null } - for (var i = length - 10; i >= 0; i--) { - if (s.charCodeAt(i) !== 36 /* '$' */) { - return false; + var dataUri = url.match(dataUriRegex) + if (dataUri) { + var mimeType = dataUri[1] + var lastParameter = dataUri[2] || "" + var encoded = dataUri[3] || "" + var data = { + sourceMappingURL: url, + url: null, + sourcesRelativeTo: codeUrl, + map: encoded + } + if (!jsonMimeTypeRegex.test(mimeType)) { + var error = new Error("Unuseful data uri mime type: " + (mimeType || "text/plain")) + error.sourceMapData = data + throw error } + data.map = parseMapToJSON( + lastParameter === ";base64" ? atob(encoded) : decodeURIComponent(encoded), + data + ) + return data } - return true; + var mapUrl = resolveUrl(codeUrl, url) + return { + sourceMappingURL: url, + url: mapUrl, + sourcesRelativeTo: mapUrl, + map: null + } } -/** - * Comparator between two mappings where the original positions are compared. - * - * Optionally pass in `true` as `onlyCompareGenerated` to consider two - * mappings with the same original source/line/column, but different generated - * line and column the same. Useful when searching for a mapping with a - * stubbed out mapping. - */ -function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) { - var cmp = mappingA.source - mappingB.source; - if (cmp !== 0) { - return cmp; - } - cmp = mappingA.originalLine - mappingB.originalLine; - if (cmp !== 0) { - return cmp; - } - cmp = mappingA.originalColumn - mappingB.originalColumn; - if (cmp !== 0 || onlyCompareOriginal) { - return cmp; +function resolveSources(map, mapUrl, read, options, callback) { + if (typeof options === "function") { + callback = options + options = {} } - - cmp = mappingA.generatedColumn - mappingB.generatedColumn; - if (cmp !== 0) { - return cmp; + var pending = map.sources ? map.sources.length : 0 + var result = { + sourcesResolved: [], + sourcesContent: [] } - cmp = mappingA.generatedLine - mappingB.generatedLine; - if (cmp !== 0) { - return cmp; + if (pending === 0) { + callbackAsync(callback, null, result) + return } - return mappingA.name - mappingB.name; -} -exports.compareByOriginalPositions = compareByOriginalPositions; - -/** - * Comparator between two mappings with deflated source and name indices where - * the generated positions are compared. - * - * Optionally pass in `true` as `onlyCompareGenerated` to consider two - * mappings with the same generated line and column, but different - * source/name/original line and column the same. Useful when searching for a - * mapping with a stubbed out mapping. - */ -function compareByGeneratedPositionsDeflated(mappingA, mappingB, onlyCompareGenerated) { - var cmp = mappingA.generatedLine - mappingB.generatedLine; - if (cmp !== 0) { - return cmp; + var done = function() { + pending-- + if (pending === 0) { + callback(null, result) + } } - cmp = mappingA.generatedColumn - mappingB.generatedColumn; - if (cmp !== 0 || onlyCompareGenerated) { - return cmp; - } + resolveSourcesHelper(map, mapUrl, options, function(fullUrl, sourceContent, index) { + result.sourcesResolved[index] = fullUrl + if (typeof sourceContent === "string") { + result.sourcesContent[index] = sourceContent + callbackAsync(done, null) + } else { + var readUrl = decodeUriComponent(fullUrl) + read(readUrl, function(error, source) { + result.sourcesContent[index] = error ? error : String(source) + done() + }) + } + }) +} - cmp = mappingA.source - mappingB.source; - if (cmp !== 0) { - return cmp; +function resolveSourcesSync(map, mapUrl, read, options) { + var result = { + sourcesResolved: [], + sourcesContent: [] } - cmp = mappingA.originalLine - mappingB.originalLine; - if (cmp !== 0) { - return cmp; + if (!map.sources || map.sources.length === 0) { + return result } - cmp = mappingA.originalColumn - mappingB.originalColumn; - if (cmp !== 0) { - return cmp; - } + resolveSourcesHelper(map, mapUrl, options, function(fullUrl, sourceContent, index) { + result.sourcesResolved[index] = fullUrl + if (read !== null) { + if (typeof sourceContent === "string") { + result.sourcesContent[index] = sourceContent + } else { + var readUrl = decodeUriComponent(fullUrl) + try { + result.sourcesContent[index] = String(read(readUrl)) + } catch (error) { + result.sourcesContent[index] = error + } + } + } + }) - return mappingA.name - mappingB.name; + return result } -exports.compareByGeneratedPositionsDeflated = compareByGeneratedPositionsDeflated; -function strcmp(aStr1, aStr2) { - if (aStr1 === aStr2) { - return 0; - } +var endingSlash = /\/?$/ - if (aStr1 > aStr2) { - return 1; +function resolveSourcesHelper(map, mapUrl, options, fn) { + options = options || {} + mapUrl = urix(mapUrl) + var fullUrl + var sourceContent + var sourceRoot + for (var index = 0, len = map.sources.length; index < len; index++) { + sourceRoot = null + if (typeof options.sourceRoot === "string") { + sourceRoot = options.sourceRoot + } else if (typeof map.sourceRoot === "string" && options.sourceRoot !== false) { + sourceRoot = map.sourceRoot + } + // If the sourceRoot is the empty string, it is equivalent to not setting + // the property at all. + if (sourceRoot === null || sourceRoot === '') { + fullUrl = resolveUrl(mapUrl, map.sources[index]) + } else { + // Make sure that the sourceRoot ends with a slash, so that `/scripts/subdir` becomes + // `/scripts/subdir/`, not `/scripts/`. Pointing to a file as source root + // does not make sense. + fullUrl = resolveUrl(mapUrl, sourceRoot.replace(endingSlash, "/"), map.sources[index]) + } + sourceContent = (map.sourcesContent || [])[index] + fn(fullUrl, sourceContent, index) } - - return -1; } -/** - * Comparator between two mappings with inflated source and name strings where - * the generated positions are compared. - */ -function compareByGeneratedPositionsInflated(mappingA, mappingB) { - var cmp = mappingA.generatedLine - mappingB.generatedLine; - if (cmp !== 0) { - return cmp; - } - cmp = mappingA.generatedColumn - mappingB.generatedColumn; - if (cmp !== 0) { - return cmp; + +function resolve(code, codeUrl, read, options, callback) { + if (typeof options === "function") { + callback = options + options = {} + } + if (code === null) { + var mapUrl = codeUrl + var data = { + sourceMappingURL: null, + url: mapUrl, + sourcesRelativeTo: mapUrl, + map: null + } + var readUrl = decodeUriComponent(mapUrl) + read(readUrl, function(error, result) { + if (error) { + error.sourceMapData = data + return callback(error) + } + data.map = String(result) + try { + data.map = parseMapToJSON(data.map, data) + } catch (error) { + return callback(error) + } + _resolveSources(data) + }) + } else { + resolveSourceMap(code, codeUrl, read, function(error, mapData) { + if (error) { + return callback(error) + } + if (!mapData) { + return callback(null, null) + } + _resolveSources(mapData) + }) } - cmp = strcmp(mappingA.source, mappingB.source); - if (cmp !== 0) { - return cmp; + function _resolveSources(mapData) { + resolveSources(mapData.map, mapData.sourcesRelativeTo, read, options, function(error, result) { + if (error) { + return callback(error) + } + mapData.sourcesResolved = result.sourcesResolved + mapData.sourcesContent = result.sourcesContent + callback(null, mapData) + }) } +} - cmp = mappingA.originalLine - mappingB.originalLine; - if (cmp !== 0) { - return cmp; +function resolveSync(code, codeUrl, read, options) { + var mapData + if (code === null) { + var mapUrl = codeUrl + mapData = { + sourceMappingURL: null, + url: mapUrl, + sourcesRelativeTo: mapUrl, + map: null + } + mapData.map = readSync(read, mapUrl, mapData) + mapData.map = parseMapToJSON(mapData.map, mapData) + } else { + mapData = resolveSourceMapSync(code, codeUrl, read) + if (!mapData) { + return null + } } + var result = resolveSourcesSync(mapData.map, mapData.sourcesRelativeTo, read, options) + mapData.sourcesResolved = result.sourcesResolved + mapData.sourcesContent = result.sourcesContent + return mapData +} - cmp = mappingA.originalColumn - mappingB.originalColumn; - if (cmp !== 0) { - return cmp; - } - return strcmp(mappingA.name, mappingB.name); + +module.exports = { + resolveSourceMap: resolveSourceMap, + resolveSourceMapSync: resolveSourceMapSync, + resolveSources: resolveSources, + resolveSourcesSync: resolveSourcesSync, + resolve: resolve, + resolveSync: resolveSync, + parseMapToJSON: parseMapToJSON } -exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflated; /***/ }), -/* 668 */ +/* 681 */ /***/ (function(module, exports, __webpack_require__) { -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ +var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_RESULT__;// Copyright 2014 Simon Lydell +// X11 (“MIT”) Licensed. (See LICENSE.) -var util = __webpack_require__(667); -var has = Object.prototype.hasOwnProperty; -var hasNativeMap = typeof Map !== "undefined"; +void (function(root, factory) { + if (true) { + !(__WEBPACK_AMD_DEFINE_FACTORY__ = (factory), + __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? + (__WEBPACK_AMD_DEFINE_FACTORY__.call(exports, __webpack_require__, exports, module)) : + __WEBPACK_AMD_DEFINE_FACTORY__), + __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)) + } else {} +}(this, function() { -/** - * A data structure which is a combination of an array and a set. Adding a new - * member is O(1), testing for membership is O(1), and finding the index of an - * element is O(1). Removing elements from the set is not supported. Only - * strings are supported for membership. - */ -function ArraySet() { - this._array = []; - this._set = hasNativeMap ? new Map() : Object.create(null); -} + var innerRegex = /[#@] sourceMappingURL=([^\s'"]*)/ -/** - * Static method for creating ArraySet instances from an existing array. - */ -ArraySet.fromArray = function ArraySet_fromArray(aArray, aAllowDuplicates) { - var set = new ArraySet(); - for (var i = 0, len = aArray.length; i < len; i++) { - set.add(aArray[i], aAllowDuplicates); - } - return set; -}; + var regex = RegExp( + "(?:" + + "/\\*" + + "(?:\\s*\r?\n(?://)?)?" + + "(?:" + innerRegex.source + ")" + + "\\s*" + + "\\*/" + + "|" + + "//(?:" + innerRegex.source + ")" + + ")" + + "\\s*" + ) -/** - * Return how many unique items are in this ArraySet. If duplicates have been - * added, than those do not count towards the size. - * - * @returns Number - */ -ArraySet.prototype.size = function ArraySet_size() { - return hasNativeMap ? this._set.size : Object.getOwnPropertyNames(this._set).length; -}; + return { -/** - * Add the given string to this set. - * - * @param String aStr - */ -ArraySet.prototype.add = function ArraySet_add(aStr, aAllowDuplicates) { - var sStr = hasNativeMap ? aStr : util.toSetString(aStr); - var isDuplicate = hasNativeMap ? this.has(aStr) : has.call(this._set, sStr); - var idx = this._array.length; - if (!isDuplicate || aAllowDuplicates) { - this._array.push(aStr); - } - if (!isDuplicate) { - if (hasNativeMap) { - this._set.set(aStr, idx); - } else { - this._set[sStr] = idx; - } - } -}; + regex: regex, + _innerRegex: innerRegex, -/** - * Is the given string a member of this set? - * - * @param String aStr - */ -ArraySet.prototype.has = function ArraySet_has(aStr) { - if (hasNativeMap) { - return this._set.has(aStr); - } else { - var sStr = util.toSetString(aStr); - return has.call(this._set, sStr); - } -}; + getFrom: function(code) { + var match = code.match(regex) + return (match ? match[1] || match[2] || "" : null) + }, -/** - * What is the index of the given string in the array? - * - * @param String aStr - */ -ArraySet.prototype.indexOf = function ArraySet_indexOf(aStr) { - if (hasNativeMap) { - var idx = this._set.get(aStr); - if (idx >= 0) { - return idx; - } - } else { - var sStr = util.toSetString(aStr); - if (has.call(this._set, sStr)) { - return this._set[sStr]; - } - } + existsIn: function(code) { + return regex.test(code) + }, - throw new Error('"' + aStr + '" is not in the set.'); -}; + removeFrom: function(code) { + return code.replace(regex, "") + }, -/** - * What is the element at the given index? - * - * @param Number aIdx - */ -ArraySet.prototype.at = function ArraySet_at(aIdx) { - if (aIdx >= 0 && aIdx < this._array.length) { - return this._array[aIdx]; + insertBefore: function(code, string) { + var match = code.match(regex) + if (match) { + return code.slice(0, match.index) + string + code.slice(match.index) + } else { + return code + string + } + } } - throw new Error('No element indexed by ' + aIdx); -}; - -/** - * Returns the array representation of this set (which has the proper indices - * indicated by indexOf). Note that this is a copy of the internal array used - * for storing the members so that no one can mess with internal state. - */ -ArraySet.prototype.toArray = function ArraySet_toArray() { - return this._array.slice(); -}; -exports.ArraySet = ArraySet; +})); /***/ }), -/* 669 */ +/* 682 */ /***/ (function(module, exports, __webpack_require__) { -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2014 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ +// Copyright 2014 Simon Lydell +// X11 (“MIT”) Licensed. (See LICENSE.) -var util = __webpack_require__(667); +var url = __webpack_require__(203) -/** - * Determine whether mappingB is after mappingA with respect to generated - * position. - */ -function generatedPositionAfter(mappingA, mappingB) { - // Optimized for most common case - var lineA = mappingA.generatedLine; - var lineB = mappingB.generatedLine; - var columnA = mappingA.generatedColumn; - var columnB = mappingB.generatedColumn; - return lineB > lineA || lineB == lineA && columnB >= columnA || - util.compareByGeneratedPositionsInflated(mappingA, mappingB) <= 0; +function resolveUrl(/* ...urls */) { + return Array.prototype.reduce.call(arguments, function(resolved, nextUrl) { + return url.resolve(resolved, nextUrl) + }) } -/** - * A data structure to provide a sorted view of accumulated mappings in a - * performance conscious manner. It trades a neglibable overhead in general - * case for a large speedup in case of mappings being added in order. - */ -function MappingList() { - this._array = []; - this._sorted = true; - // Serves as infimum - this._last = {generatedLine: -1, generatedColumn: 0}; -} +module.exports = resolveUrl -/** - * Iterate through internal items. This method takes the same arguments that - * `Array.prototype.forEach` takes. - * - * NOTE: The order of the mappings is NOT guaranteed. - */ -MappingList.prototype.unsortedForEach = - function MappingList_forEach(aCallback, aThisArg) { - this._array.forEach(aCallback, aThisArg); - }; -/** - * Add the given source mapping. - * - * @param Object aMapping - */ -MappingList.prototype.add = function MappingList_add(aMapping) { - if (generatedPositionAfter(this._last, aMapping)) { - this._last = aMapping; - this._array.push(aMapping); - } else { - this._sorted = false; - this._array.push(aMapping); - } -}; +/***/ }), +/* 683 */ +/***/ (function(module, exports, __webpack_require__) { -/** - * Returns the flat, sorted array of mappings. The mappings are sorted by - * generated position. - * - * WARNING: This method returns internal data without copying, for - * performance. The return value must NOT be mutated, and should be treated as - * an immutable borrow. If you want to take ownership, you must make your own - * copy. - */ -MappingList.prototype.toArray = function MappingList_toArray() { - if (!this._sorted) { - this._array.sort(util.compareByGeneratedPositionsInflated); - this._sorted = true; - } - return this._array; -}; +// Copyright 2017 Simon Lydell +// X11 (“MIT”) Licensed. (See LICENSE.) -exports.MappingList = MappingList; +var decodeUriComponent = __webpack_require__(684) + +function customDecodeUriComponent(string) { + // `decodeUriComponent` turns `+` into ` `, but that's not wanted. + return decodeUriComponent(string.replace(/\+/g, "%2B")) +} + +module.exports = customDecodeUriComponent /***/ }), -/* 670 */ +/* 684 */ /***/ (function(module, exports, __webpack_require__) { -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ +"use strict"; -var util = __webpack_require__(667); -var binarySearch = __webpack_require__(671); -var ArraySet = __webpack_require__(668).ArraySet; -var base64VLQ = __webpack_require__(665); -var quickSort = __webpack_require__(672).quickSort; +var token = '%[a-f0-9]{2}'; +var singleMatcher = new RegExp(token, 'gi'); +var multiMatcher = new RegExp('(' + token + ')+', 'gi'); -function SourceMapConsumer(aSourceMap) { - var sourceMap = aSourceMap; - if (typeof aSourceMap === 'string') { - sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, '')); - } +function decodeComponents(components, split) { + try { + // Try to decode the entire string first + return decodeURIComponent(components.join('')); + } catch (err) { + // Do nothing + } - return sourceMap.sections != null - ? new IndexedSourceMapConsumer(sourceMap) - : new BasicSourceMapConsumer(sourceMap); -} + if (components.length === 1) { + return components; + } -SourceMapConsumer.fromSourceMap = function(aSourceMap) { - return BasicSourceMapConsumer.fromSourceMap(aSourceMap); -} + split = split || 1; -/** - * The version of the source mapping spec that we are consuming. - */ -SourceMapConsumer.prototype._version = 3; + // Split the array in 2 parts + var left = components.slice(0, split); + var right = components.slice(split); -// `__generatedMappings` and `__originalMappings` are arrays that hold the -// parsed mapping coordinates from the source map's "mappings" attribute. They -// are lazily instantiated, accessed via the `_generatedMappings` and -// `_originalMappings` getters respectively, and we only parse the mappings -// and create these arrays once queried for a source location. We jump through -// these hoops because there can be many thousands of mappings, and parsing -// them is expensive, so we only want to do it if we must. -// -// Each object in the arrays is of the form: -// -// { -// generatedLine: The line number in the generated code, -// generatedColumn: The column number in the generated code, -// source: The path to the original source file that generated this -// chunk of code, -// originalLine: The line number in the original source that -// corresponds to this chunk of generated code, -// originalColumn: The column number in the original source that -// corresponds to this chunk of generated code, -// name: The name of the original symbol which generated this chunk of -// code. -// } -// -// All properties except for `generatedLine` and `generatedColumn` can be -// `null`. -// -// `_generatedMappings` is ordered by the generated positions. -// -// `_originalMappings` is ordered by the original positions. + return Array.prototype.concat.call([], decodeComponents(left), decodeComponents(right)); +} -SourceMapConsumer.prototype.__generatedMappings = null; -Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', { - get: function () { - if (!this.__generatedMappings) { - this._parseMappings(this._mappings, this.sourceRoot); - } +function decode(input) { + try { + return decodeURIComponent(input); + } catch (err) { + var tokens = input.match(singleMatcher); - return this.__generatedMappings; - } -}); + for (var i = 1; i < tokens.length; i++) { + input = decodeComponents(tokens, i).join(''); -SourceMapConsumer.prototype.__originalMappings = null; -Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', { - get: function () { - if (!this.__originalMappings) { - this._parseMappings(this._mappings, this.sourceRoot); - } + tokens = input.match(singleMatcher); + } - return this.__originalMappings; - } -}); + return input; + } +} -SourceMapConsumer.prototype._charIsMappingSeparator = - function SourceMapConsumer_charIsMappingSeparator(aStr, index) { - var c = aStr.charAt(index); - return c === ";" || c === ","; - }; +function customDecodeURIComponent(input) { + // Keep track of all the replacements and prefill the map with the `BOM` + var replaceMap = { + '%FE%FF': '\uFFFD\uFFFD', + '%FF%FE': '\uFFFD\uFFFD' + }; -/** - * Parse the mappings in a string in to a data structure which we can easily - * query (the ordered arrays in the `this.__generatedMappings` and - * `this.__originalMappings` properties). - */ -SourceMapConsumer.prototype._parseMappings = - function SourceMapConsumer_parseMappings(aStr, aSourceRoot) { - throw new Error("Subclasses must implement _parseMappings"); - }; + var match = multiMatcher.exec(input); + while (match) { + try { + // Decode as big chunks as possible + replaceMap[match[0]] = decodeURIComponent(match[0]); + } catch (err) { + var result = decode(match[0]); -SourceMapConsumer.GENERATED_ORDER = 1; -SourceMapConsumer.ORIGINAL_ORDER = 2; + if (result !== match[0]) { + replaceMap[match[0]] = result; + } + } -SourceMapConsumer.GREATEST_LOWER_BOUND = 1; -SourceMapConsumer.LEAST_UPPER_BOUND = 2; + match = multiMatcher.exec(input); + } -/** - * Iterate over each mapping between an original source/line/column and a - * generated line/column in this source map. - * - * @param Function aCallback - * The function that is called with each mapping. - * @param Object aContext - * Optional. If specified, this object will be the value of `this` every - * time that `aCallback` is called. - * @param aOrder - * Either `SourceMapConsumer.GENERATED_ORDER` or - * `SourceMapConsumer.ORIGINAL_ORDER`. Specifies whether you want to - * iterate over the mappings sorted by the generated file's line/column - * order or the original's source/line/column order, respectively. Defaults to - * `SourceMapConsumer.GENERATED_ORDER`. - */ -SourceMapConsumer.prototype.eachMapping = - function SourceMapConsumer_eachMapping(aCallback, aContext, aOrder) { - var context = aContext || null; - var order = aOrder || SourceMapConsumer.GENERATED_ORDER; + // Add `%C2` at the end of the map to make sure it does not replace the combinator before everything else + replaceMap['%C2'] = '\uFFFD'; - var mappings; - switch (order) { - case SourceMapConsumer.GENERATED_ORDER: - mappings = this._generatedMappings; - break; - case SourceMapConsumer.ORIGINAL_ORDER: - mappings = this._originalMappings; - break; - default: - throw new Error("Unknown order of iteration."); - } + var entries = Object.keys(replaceMap); - var sourceRoot = this.sourceRoot; - mappings.map(function (mapping) { - var source = mapping.source === null ? null : this._sources.at(mapping.source); - if (source != null && sourceRoot != null) { - source = util.join(sourceRoot, source); - } - return { - source: source, - generatedLine: mapping.generatedLine, - generatedColumn: mapping.generatedColumn, - originalLine: mapping.originalLine, - originalColumn: mapping.originalColumn, - name: mapping.name === null ? null : this._names.at(mapping.name) - }; - }, this).forEach(aCallback, context); - }; + for (var i = 0; i < entries.length; i++) { + // Replace all decoded components + var key = entries[i]; + input = input.replace(new RegExp(key, 'g'), replaceMap[key]); + } -/** - * Returns all generated line and column information for the original source, - * line, and column provided. If no column is provided, returns all mappings - * corresponding to a either the line we are searching for or the next - * closest line that has any mappings. Otherwise, returns all mappings - * corresponding to the given line and either the column we are searching for - * or the next closest column that has any offsets. - * - * The only argument is an object with the following properties: - * - * - source: The filename of the original source. - * - line: The line number in the original source. - * - column: Optional. the column number in the original source. - * - * and an array of objects is returned, each with the following properties: - * - * - line: The line number in the generated source, or null. - * - column: The column number in the generated source, or null. - */ -SourceMapConsumer.prototype.allGeneratedPositionsFor = - function SourceMapConsumer_allGeneratedPositionsFor(aArgs) { - var line = util.getArg(aArgs, 'line'); + return input; +} - // When there is no exact match, BasicSourceMapConsumer.prototype._findMapping - // returns the index of the closest mapping less than the needle. By - // setting needle.originalColumn to 0, we thus find the last mapping for - // the given line, provided such a mapping exists. - var needle = { - source: util.getArg(aArgs, 'source'), - originalLine: line, - originalColumn: util.getArg(aArgs, 'column', 0) - }; +module.exports = function (encodedURI) { + if (typeof encodedURI !== 'string') { + throw new TypeError('Expected `encodedURI` to be of type `string`, got `' + typeof encodedURI + '`'); + } - if (this.sourceRoot != null) { - needle.source = util.relative(this.sourceRoot, needle.source); - } - if (!this._sources.has(needle.source)) { - return []; - } - needle.source = this._sources.indexOf(needle.source); + try { + encodedURI = encodedURI.replace(/\+/g, ' '); + + // Try the built in decoder first + return decodeURIComponent(encodedURI); + } catch (err) { + // Fallback to a more advanced decoder + return customDecodeURIComponent(encodedURI); + } +}; - var mappings = []; - var index = this._findMapping(needle, - this._originalMappings, - "originalLine", - "originalColumn", - util.compareByOriginalPositions, - binarySearch.LEAST_UPPER_BOUND); - if (index >= 0) { - var mapping = this._originalMappings[index]; +/***/ }), +/* 685 */ +/***/ (function(module, exports, __webpack_require__) { - if (aArgs.column === undefined) { - var originalLine = mapping.originalLine; +// Copyright 2014 Simon Lydell +// X11 (“MIT”) Licensed. (See LICENSE.) + +var path = __webpack_require__(4) + +"use strict" + +function urix(aPath) { + if (path.sep === "\\") { + return aPath + .replace(/\\/g, "/") + .replace(/^[a-z]:\/?/i, "/") + } + return aPath +} + +module.exports = urix - // Iterate until either we run out of mappings, or we run into - // a mapping for a different line than the one we found. Since - // mappings are sorted, this is guaranteed to find all mappings for - // the line we found. - while (mapping && mapping.originalLine === originalLine) { - mappings.push({ - line: util.getArg(mapping, 'generatedLine', null), - column: util.getArg(mapping, 'generatedColumn', null), - lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null) - }); - mapping = this._originalMappings[++index]; - } - } else { - var originalColumn = mapping.originalColumn; +/***/ }), +/* 686 */ +/***/ (function(module, exports, __webpack_require__) { - // Iterate until either we run out of mappings, or we run into - // a mapping for a different line than the one we were searching for. - // Since mappings are sorted, this is guaranteed to find all mappings for - // the line we are searching for. - while (mapping && - mapping.originalLine === line && - mapping.originalColumn == originalColumn) { - mappings.push({ - line: util.getArg(mapping, 'generatedLine', null), - column: util.getArg(mapping, 'generatedColumn', null), - lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null) - }); +"use strict"; - mapping = this._originalMappings[++index]; - } - } - } - return mappings; - }; +function atob(str) { + return Buffer.from(str, 'base64').toString('binary'); +} -exports.SourceMapConsumer = SourceMapConsumer; +module.exports = atob.atob = atob; -/** - * A BasicSourceMapConsumer instance represents a parsed source map which we can - * query for information about the original file positions by giving it a file - * position in the generated source. - * - * The only parameter is the raw source map (either as a JSON string, or - * already parsed to an object). According to the spec, source maps have the - * following attributes: - * - * - version: Which version of the source map spec this map is following. - * - sources: An array of URLs to the original source files. - * - names: An array of identifiers which can be referrenced by individual mappings. - * - sourceRoot: Optional. The URL root from which all sources are relative. - * - sourcesContent: Optional. An array of contents of the original source files. - * - mappings: A string of base64 VLQs which contain the actual mappings. - * - file: Optional. The generated file this source map is associated with. - * - * Here is an example source map, taken from the source map spec[0]: - * - * { - * version : 3, - * file: "out.js", - * sourceRoot : "", - * sources: ["foo.js", "bar.js"], - * names: ["src", "maps", "are", "fun"], - * mappings: "AA,AB;;ABCDE;" - * } - * - * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1# - */ -function BasicSourceMapConsumer(aSourceMap) { - var sourceMap = aSourceMap; - if (typeof aSourceMap === 'string') { - sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, '')); - } - var version = util.getArg(sourceMap, 'version'); - var sources = util.getArg(sourceMap, 'sources'); - // Sass 3.3 leaves out the 'names' array, so we deviate from the spec (which - // requires the array) to play nice here. - var names = util.getArg(sourceMap, 'names', []); - var sourceRoot = util.getArg(sourceMap, 'sourceRoot', null); - var sourcesContent = util.getArg(sourceMap, 'sourcesContent', null); - var mappings = util.getArg(sourceMap, 'mappings'); - var file = util.getArg(sourceMap, 'file', null); +/***/ }), +/* 687 */ +/***/ (function(module, exports, __webpack_require__) { - // Once again, Sass deviates from the spec and supplies the version as a - // string rather than a number, so we use loose equality checking here. - if (version != this._version) { - throw new Error('Unsupported version: ' + version); - } +"use strict"; - sources = sources - .map(String) - // Some source maps produce relative source paths like "./foo.js" instead of - // "foo.js". Normalize these first so that future comparisons will succeed. - // See bugzil.la/1090768. - .map(util.normalize) - // Always ensure that absolute sources are internally stored relative to - // the source root, if the source root is absolute. Not doing this would - // be particularly problematic when the source root is a prefix of the - // source (valid, but why??). See github issue #199 and bugzil.la/1188982. - .map(function (source) { - return sourceRoot && util.isAbsolute(sourceRoot) && util.isAbsolute(source) - ? util.relative(sourceRoot, source) - : source; - }); - // Pass `true` below to allow duplicate names and sources. While source maps - // are intended to be compressed and deduplicated, the TypeScript compiler - // sometimes generates source maps with duplicates in them. See Github issue - // #72 and bugzil.la/889492. - this._names = ArraySet.fromArray(names.map(String), true); - this._sources = ArraySet.fromArray(sources, true); +var fs = __webpack_require__(132); +var path = __webpack_require__(4); +var define = __webpack_require__(654); +var utils = __webpack_require__(668); - this.sourceRoot = sourceRoot; - this.sourcesContent = sourcesContent; - this._mappings = mappings; - this.file = file; -} +/** + * Expose `mixin()`. + * This code is based on `source-maps-support.js` in reworkcss/css + * https://github.com/reworkcss/css/blob/master/lib/stringify/source-map-support.js + * Copyright (c) 2012 TJ Holowaychuk + */ -BasicSourceMapConsumer.prototype = Object.create(SourceMapConsumer.prototype); -BasicSourceMapConsumer.prototype.consumer = SourceMapConsumer; +module.exports = mixin; /** - * Create a BasicSourceMapConsumer from a SourceMapGenerator. + * Mixin source map support into `compiler`. * - * @param SourceMapGenerator aSourceMap - * The source map that will be consumed. - * @returns BasicSourceMapConsumer + * @param {Object} `compiler` + * @api public */ -BasicSourceMapConsumer.fromSourceMap = - function SourceMapConsumer_fromSourceMap(aSourceMap) { - var smc = Object.create(BasicSourceMapConsumer.prototype); - var names = smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true); - var sources = smc._sources = ArraySet.fromArray(aSourceMap._sources.toArray(), true); - smc.sourceRoot = aSourceMap._sourceRoot; - smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(), - smc.sourceRoot); - smc.file = aSourceMap._file; +function mixin(compiler) { + define(compiler, '_comment', compiler.comment); + compiler.map = new utils.SourceMap.SourceMapGenerator(); + compiler.position = { line: 1, column: 1 }; + compiler.content = {}; + compiler.files = {}; - // Because we are modifying the entries (by converting string sources and - // names to indices into the sources and names ArraySets), we have to make - // a copy of the entry or else bad things happen. Shared mutable state - // strikes again! See github issue #191. + for (var key in exports) { + define(compiler, key, exports[key]); + } +} - var generatedMappings = aSourceMap._mappings.toArray().slice(); - var destGeneratedMappings = smc.__generatedMappings = []; - var destOriginalMappings = smc.__originalMappings = []; +/** + * Update position. + * + * @param {String} str + */ - for (var i = 0, length = generatedMappings.length; i < length; i++) { - var srcMapping = generatedMappings[i]; - var destMapping = new Mapping; - destMapping.generatedLine = srcMapping.generatedLine; - destMapping.generatedColumn = srcMapping.generatedColumn; +exports.updatePosition = function(str) { + var lines = str.match(/\n/g); + if (lines) this.position.line += lines.length; + var i = str.lastIndexOf('\n'); + this.position.column = ~i ? str.length - i : this.position.column + str.length; +}; - if (srcMapping.source) { - destMapping.source = sources.indexOf(srcMapping.source); - destMapping.originalLine = srcMapping.originalLine; - destMapping.originalColumn = srcMapping.originalColumn; +/** + * Emit `str` with `position`. + * + * @param {String} str + * @param {Object} [pos] + * @return {String} + */ - if (srcMapping.name) { - destMapping.name = names.indexOf(srcMapping.name); - } +exports.emit = function(str, node) { + var position = node.position || {}; + var source = position.source; + if (source) { + if (position.filepath) { + source = utils.unixify(position.filepath); + } - destOriginalMappings.push(destMapping); + this.map.addMapping({ + source: source, + generated: { + line: this.position.line, + column: Math.max(this.position.column - 1, 0) + }, + original: { + line: position.start.line, + column: position.start.column - 1 } + }); - destGeneratedMappings.push(destMapping); + if (position.content) { + this.addContent(source, position); + } + if (position.filepath) { + this.addFile(source, position); } - quickSort(smc.__originalMappings, util.compareByOriginalPositions); - - return smc; - }; + this.updatePosition(str); + this.output += str; + } + return str; +}; /** - * The version of the source mapping spec that we are consuming. + * Adds a file to the source map output if it has not already been added + * @param {String} `file` + * @param {Object} `pos` */ -BasicSourceMapConsumer.prototype._version = 3; -/** - * The list of original sources. - */ -Object.defineProperty(BasicSourceMapConsumer.prototype, 'sources', { - get: function () { - return this._sources.toArray().map(function (s) { - return this.sourceRoot != null ? util.join(this.sourceRoot, s) : s; - }, this); - } -}); +exports.addFile = function(file, position) { + if (typeof position.content !== 'string') return; + if (Object.prototype.hasOwnProperty.call(this.files, file)) return; + this.files[file] = position.content; +}; /** - * Provide the JIT with a nice shape / hidden class. + * Adds a content source to the source map output if it has not already been added + * @param {String} `source` + * @param {Object} `position` */ -function Mapping() { - this.generatedLine = 0; - this.generatedColumn = 0; - this.source = null; - this.originalLine = null; - this.originalColumn = null; - this.name = null; -} + +exports.addContent = function(source, position) { + if (typeof position.content !== 'string') return; + if (Object.prototype.hasOwnProperty.call(this.content, source)) return; + this.map.setSourceContent(source, position.content); +}; /** - * Parse the mappings in a string in to a data structure which we can easily - * query (the ordered arrays in the `this.__generatedMappings` and - * `this.__originalMappings` properties). + * Applies any original source maps to the output and embeds the source file + * contents in the source map. */ -BasicSourceMapConsumer.prototype._parseMappings = - function SourceMapConsumer_parseMappings(aStr, aSourceRoot) { - var generatedLine = 1; - var previousGeneratedColumn = 0; - var previousOriginalLine = 0; - var previousOriginalColumn = 0; - var previousSource = 0; - var previousName = 0; - var length = aStr.length; - var index = 0; - var cachedSegments = {}; - var temp = {}; - var originalMappings = []; - var generatedMappings = []; - var mapping, str, segment, end, value; - while (index < length) { - if (aStr.charAt(index) === ';') { - generatedLine++; - index++; - previousGeneratedColumn = 0; - } - else if (aStr.charAt(index) === ',') { - index++; +exports.applySourceMaps = function() { + Object.keys(this.files).forEach(function(file) { + var content = this.files[file]; + this.map.setSourceContent(file, content); + + if (this.options.inputSourcemaps === true) { + var originalMap = utils.sourceMapResolve.resolveSync(content, file, fs.readFileSync); + if (originalMap) { + var map = new utils.SourceMap.SourceMapConsumer(originalMap.map); + var relativeTo = originalMap.sourcesRelativeTo; + this.map.applySourceMap(map, file, utils.unixify(path.dirname(relativeTo))); } - else { - mapping = new Mapping(); - mapping.generatedLine = generatedLine; + } + }, this); +}; - // Because each offset is encoded relative to the previous one, - // many segments often have the same encoding. We can exploit this - // fact by caching the parsed variable length fields of each segment, - // allowing us to avoid a second parse if we encounter the same - // segment again. - for (end = index; end < length; end++) { - if (this._charIsMappingSeparator(aStr, end)) { - break; - } - } - str = aStr.slice(index, end); +/** + * Process comments, drops sourceMap comments. + * @param {Object} node + */ - segment = cachedSegments[str]; - if (segment) { - index += str.length; - } else { - segment = []; - while (index < end) { - base64VLQ.decode(aStr, index, temp); - value = temp.value; - index = temp.rest; - segment.push(value); - } +exports.comment = function(node) { + if (/^# sourceMappingURL=/.test(node.comment)) { + return this.emit('', node.position); + } + return this._comment(node); +}; - if (segment.length === 2) { - throw new Error('Found a source, but no line and column'); - } - if (segment.length === 3) { - throw new Error('Found a source and line, but no column'); - } +/***/ }), +/* 688 */ +/***/ (function(module, exports, __webpack_require__) { - cachedSegments[str] = segment; - } +"use strict"; - // Generated column. - mapping.generatedColumn = previousGeneratedColumn + segment[0]; - previousGeneratedColumn = mapping.generatedColumn; - if (segment.length > 1) { - // Original source. - mapping.source = previousSource + segment[1]; - previousSource += segment[1]; +var use = __webpack_require__(666); +var util = __webpack_require__(113); +var Cache = __webpack_require__(689); +var define = __webpack_require__(654); +var debug = __webpack_require__(205)('snapdragon:parser'); +var Position = __webpack_require__(690); +var utils = __webpack_require__(668); - // Original line. - mapping.originalLine = previousOriginalLine + segment[2]; - previousOriginalLine = mapping.originalLine; - // Lines are stored 0-based - mapping.originalLine += 1; +/** + * Create a new `Parser` with the given `input` and `options`. + * @param {String} `input` + * @param {Object} `options` + * @api public + */ - // Original column. - mapping.originalColumn = previousOriginalColumn + segment[3]; - previousOriginalColumn = mapping.originalColumn; +function Parser(options) { + debug('initializing', __filename); + this.options = utils.extend({source: 'string'}, options); + this.init(this.options); + use(this); +} - if (segment.length > 4) { - // Original name. - mapping.name = previousName + segment[4]; - previousName += segment[4]; - } - } +/** + * Prototype methods + */ - generatedMappings.push(mapping); - if (typeof mapping.originalLine === 'number') { - originalMappings.push(mapping); - } - } - } +Parser.prototype = { + constructor: Parser, - quickSort(generatedMappings, util.compareByGeneratedPositionsDeflated); - this.__generatedMappings = generatedMappings; + init: function(options) { + this.orig = ''; + this.input = ''; + this.parsed = ''; - quickSort(originalMappings, util.compareByOriginalPositions); - this.__originalMappings = originalMappings; - }; + this.column = 1; + this.line = 1; -/** - * Find the mapping that best matches the hypothetical "needle" mapping that - * we are searching for in the given "haystack" of mappings. - */ -BasicSourceMapConsumer.prototype._findMapping = - function SourceMapConsumer_findMapping(aNeedle, aMappings, aLineName, - aColumnName, aComparator, aBias) { - // To return the position we are searching for, we must first find the - // mapping for the given position and then return the opposite position it - // points to. Because the mappings are sorted, we can use binary search to - // find the best mapping. + this.regex = new Cache(); + this.errors = this.errors || []; + this.parsers = this.parsers || {}; + this.types = this.types || []; + this.sets = this.sets || {}; + this.fns = this.fns || []; + this.currentType = 'root'; - if (aNeedle[aLineName] <= 0) { - throw new TypeError('Line must be greater than or equal to 1, got ' - + aNeedle[aLineName]); - } - if (aNeedle[aColumnName] < 0) { - throw new TypeError('Column must be greater than or equal to 0, got ' - + aNeedle[aColumnName]); - } + var pos = this.position(); + this.bos = pos({type: 'bos', val: ''}); - return binarySearch.search(aNeedle, aMappings, aComparator, aBias); - }; + this.ast = { + type: 'root', + errors: this.errors, + nodes: [this.bos] + }; -/** - * Compute the last column for each generated mapping. The last column is - * inclusive. - */ -BasicSourceMapConsumer.prototype.computeColumnSpans = - function SourceMapConsumer_computeColumnSpans() { - for (var index = 0; index < this._generatedMappings.length; ++index) { - var mapping = this._generatedMappings[index]; + define(this.bos, 'parent', this.ast); + this.nodes = [this.ast]; - // Mappings do not contain a field for the last generated columnt. We - // can come up with an optimistic estimate, however, by assuming that - // mappings are contiguous (i.e. given two consecutive mappings, the - // first mapping ends where the second one starts). - if (index + 1 < this._generatedMappings.length) { - var nextMapping = this._generatedMappings[index + 1]; + this.count = 0; + this.setCount = 0; + this.stack = []; + }, - if (mapping.generatedLine === nextMapping.generatedLine) { - mapping.lastGeneratedColumn = nextMapping.generatedColumn - 1; - continue; - } - } + /** + * Throw a formatted error with the cursor column and `msg`. + * @param {String} `msg` Message to use in the Error. + */ + + error: function(msg, node) { + var pos = node.position || {start: {column: 0, line: 0}}; + var line = pos.start.line; + var column = pos.start.column; + var source = this.options.source; - // The last mapping for each line spans the entire line. - mapping.lastGeneratedColumn = Infinity; + var message = source + ' : ' + msg; + var err = new Error(message); + err.source = source; + err.reason = msg; + err.pos = pos; + + if (this.options.silent) { + this.errors.push(err); + } else { + throw err; } - }; + }, -/** - * Returns the original source, line, and column information for the generated - * source's line and column positions provided. The only argument is an object - * with the following properties: - * - * - line: The line number in the generated source. - * - column: The column number in the generated source. - * - bias: Either 'SourceMapConsumer.GREATEST_LOWER_BOUND' or - * 'SourceMapConsumer.LEAST_UPPER_BOUND'. Specifies whether to return the - * closest element that is smaller than or greater than the one we are - * searching for, respectively, if the exact element cannot be found. - * Defaults to 'SourceMapConsumer.GREATEST_LOWER_BOUND'. - * - * and an object is returned with the following properties: - * - * - source: The original source file, or null. - * - line: The line number in the original source, or null. - * - column: The column number in the original source, or null. - * - name: The original identifier, or null. - */ -BasicSourceMapConsumer.prototype.originalPositionFor = - function SourceMapConsumer_originalPositionFor(aArgs) { - var needle = { - generatedLine: util.getArg(aArgs, 'line'), - generatedColumn: util.getArg(aArgs, 'column') - }; + /** + * Define a non-enumberable property on the `Parser` instance. + * + * ```js + * parser.define('foo', 'bar'); + * ``` + * @name .define + * @param {String} `key` propery name + * @param {any} `val` property value + * @return {Object} Returns the Parser instance for chaining. + * @api public + */ - var index = this._findMapping( - needle, - this._generatedMappings, - "generatedLine", - "generatedColumn", - util.compareByGeneratedPositionsDeflated, - util.getArg(aArgs, 'bias', SourceMapConsumer.GREATEST_LOWER_BOUND) - ); + define: function(key, val) { + define(this, key, val); + return this; + }, - if (index >= 0) { - var mapping = this._generatedMappings[index]; + /** + * Mark position and patch `node.position`. + */ - if (mapping.generatedLine === needle.generatedLine) { - var source = util.getArg(mapping, 'source', null); - if (source !== null) { - source = this._sources.at(source); - if (this.sourceRoot != null) { - source = util.join(this.sourceRoot, source); - } - } - var name = util.getArg(mapping, 'name', null); - if (name !== null) { - name = this._names.at(name); - } - return { - source: source, - line: util.getArg(mapping, 'originalLine', null), - column: util.getArg(mapping, 'originalColumn', null), - name: name - }; - } - } + position: function() { + var start = { line: this.line, column: this.column }; + var self = this; - return { - source: null, - line: null, - column: null, - name: null + return function(node) { + define(node, 'position', new Position(start, self)); + return node; }; - }; + }, -/** - * Return true if we have the source content for every source in the source - * map, false otherwise. - */ -BasicSourceMapConsumer.prototype.hasContentsOfAllSources = - function BasicSourceMapConsumer_hasContentsOfAllSources() { - if (!this.sourcesContent) { - return false; - } - return this.sourcesContent.length >= this._sources.size() && - !this.sourcesContent.some(function (sc) { return sc == null; }); - }; + /** + * Set parser `name` with the given `fn` + * @param {String} `name` + * @param {Function} `fn` + * @api public + */ -/** - * Returns the original source content. The only argument is the url of the - * original source file. Returns null if no original source content is - * available. - */ -BasicSourceMapConsumer.prototype.sourceContentFor = - function SourceMapConsumer_sourceContentFor(aSource, nullOnMissing) { - if (!this.sourcesContent) { - return null; + set: function(type, fn) { + if (this.types.indexOf(type) === -1) { + this.types.push(type); } + this.parsers[type] = fn.bind(this); + return this; + }, - if (this.sourceRoot != null) { - aSource = util.relative(this.sourceRoot, aSource); - } + /** + * Get parser `name` + * @param {String} `name` + * @api public + */ - if (this._sources.has(aSource)) { - return this.sourcesContent[this._sources.indexOf(aSource)]; - } + get: function(name) { + return this.parsers[name]; + }, - var url; - if (this.sourceRoot != null - && (url = util.urlParse(this.sourceRoot))) { - // XXX: file:// URIs and absolute paths lead to unexpected behavior for - // many users. We can help them out when they expect file:// URIs to - // behave like it would if they were running a local HTTP server. See - // https://bugzilla.mozilla.org/show_bug.cgi?id=885597. - var fileUriAbsPath = aSource.replace(/^file:\/\//, ""); - if (url.scheme == "file" - && this._sources.has(fileUriAbsPath)) { - return this.sourcesContent[this._sources.indexOf(fileUriAbsPath)] - } + /** + * Push a `token` onto the `type` stack. + * + * @param {String} `type` + * @return {Object} `token` + * @api public + */ - if ((!url.path || url.path == "/") - && this._sources.has("/" + aSource)) { - return this.sourcesContent[this._sources.indexOf("/" + aSource)]; - } - } + push: function(type, token) { + this.sets[type] = this.sets[type] || []; + this.count++; + this.stack.push(token); + return this.sets[type].push(token); + }, - // This function is used recursively from - // IndexedSourceMapConsumer.prototype.sourceContentFor. In that case, we - // don't want to throw if we can't find the source - we just want to - // return null, so we provide a flag to exit gracefully. - if (nullOnMissing) { - return null; - } - else { - throw new Error('"' + aSource + '" is not in the SourceMap.'); - } - }; + /** + * Pop a token off of the `type` stack + * @param {String} `type` + * @returns {Object} Returns a token + * @api public + */ -/** - * Returns the generated line and column information for the original source, - * line, and column positions provided. The only argument is an object with - * the following properties: - * - * - source: The filename of the original source. - * - line: The line number in the original source. - * - column: The column number in the original source. - * - bias: Either 'SourceMapConsumer.GREATEST_LOWER_BOUND' or - * 'SourceMapConsumer.LEAST_UPPER_BOUND'. Specifies whether to return the - * closest element that is smaller than or greater than the one we are - * searching for, respectively, if the exact element cannot be found. - * Defaults to 'SourceMapConsumer.GREATEST_LOWER_BOUND'. - * - * and an object is returned with the following properties: - * - * - line: The line number in the generated source, or null. - * - column: The column number in the generated source, or null. - */ -BasicSourceMapConsumer.prototype.generatedPositionFor = - function SourceMapConsumer_generatedPositionFor(aArgs) { - var source = util.getArg(aArgs, 'source'); - if (this.sourceRoot != null) { - source = util.relative(this.sourceRoot, source); - } - if (!this._sources.has(source)) { - return { - line: null, - column: null, - lastColumn: null - }; - } - source = this._sources.indexOf(source); + pop: function(type) { + this.sets[type] = this.sets[type] || []; + this.count--; + this.stack.pop(); + return this.sets[type].pop(); + }, - var needle = { - source: source, - originalLine: util.getArg(aArgs, 'line'), - originalColumn: util.getArg(aArgs, 'column') - }; + /** + * Return true if inside a `stack` node. Types are `braces`, `parens` or `brackets`. + * + * @param {String} `type` + * @return {Boolean} + * @api public + */ - var index = this._findMapping( - needle, - this._originalMappings, - "originalLine", - "originalColumn", - util.compareByOriginalPositions, - util.getArg(aArgs, 'bias', SourceMapConsumer.GREATEST_LOWER_BOUND) - ); + isInside: function(type) { + this.sets[type] = this.sets[type] || []; + return this.sets[type].length > 0; + }, - if (index >= 0) { - var mapping = this._originalMappings[index]; + /** + * Return true if `node` is the given `type`. + * + * ```js + * parser.isType(node, 'brace'); + * ``` + * @param {Object} `node` + * @param {String} `type` + * @return {Boolean} + * @api public + */ - if (mapping.source === needle.source) { - return { - line: util.getArg(mapping, 'generatedLine', null), - column: util.getArg(mapping, 'generatedColumn', null), - lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null) - }; - } - } + isType: function(node, type) { + return node && node.type === type; + }, - return { - line: null, - column: null, - lastColumn: null - }; - }; + /** + * Get the previous AST node + * @return {Object} + */ -exports.BasicSourceMapConsumer = BasicSourceMapConsumer; + prev: function(n) { + return this.stack.length > 0 + ? utils.last(this.stack, n) + : utils.last(this.nodes, n); + }, -/** - * An IndexedSourceMapConsumer instance represents a parsed source map which - * we can query for information. It differs from BasicSourceMapConsumer in - * that it takes "indexed" source maps (i.e. ones with a "sections" field) as - * input. - * - * The only parameter is a raw source map (either as a JSON string, or already - * parsed to an object). According to the spec for indexed source maps, they - * have the following attributes: - * - * - version: Which version of the source map spec this map is following. - * - file: Optional. The generated file this source map is associated with. - * - sections: A list of section definitions. - * - * Each value under the "sections" field has two fields: - * - offset: The offset into the original specified at which this section - * begins to apply, defined as an object with a "line" and "column" - * field. - * - map: A source map definition. This source map could also be indexed, - * but doesn't have to be. - * - * Instead of the "map" field, it's also possible to have a "url" field - * specifying a URL to retrieve a source map from, but that's currently - * unsupported. - * - * Here's an example source map, taken from the source map spec[0], but - * modified to omit a section which uses the "url" field. - * - * { - * version : 3, - * file: "app.js", - * sections: [{ - * offset: {line:100, column:10}, - * map: { - * version : 3, - * file: "section.js", - * sources: ["foo.js", "bar.js"], - * names: ["src", "maps", "are", "fun"], - * mappings: "AAAA,E;;ABCDE;" - * } - * }], - * } - * - * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#heading=h.535es3xeprgt - */ -function IndexedSourceMapConsumer(aSourceMap) { - var sourceMap = aSourceMap; - if (typeof aSourceMap === 'string') { - sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, '')); - } + /** + * Update line and column based on `str`. + */ - var version = util.getArg(sourceMap, 'version'); - var sections = util.getArg(sourceMap, 'sections'); + consume: function(len) { + this.input = this.input.substr(len); + }, - if (version != this._version) { - throw new Error('Unsupported version: ' + version); - } + /** + * Update column based on `str`. + */ - this._sources = new ArraySet(); - this._names = new ArraySet(); + updatePosition: function(str, len) { + var lines = str.match(/\n/g); + if (lines) this.line += lines.length; + var i = str.lastIndexOf('\n'); + this.column = ~i ? len - i : this.column + len; + this.parsed += str; + this.consume(len); + }, - var lastOffset = { - line: -1, - column: 0 - }; - this._sections = sections.map(function (s) { - if (s.url) { - // The url field will require support for asynchronicity. - // See https://github.com/mozilla/source-map/issues/16 - throw new Error('Support for url field in sections not implemented.'); - } - var offset = util.getArg(s, 'offset'); - var offsetLine = util.getArg(offset, 'line'); - var offsetColumn = util.getArg(offset, 'column'); + /** + * Match `regex`, return captures, and update the cursor position by `match[0]` length. + * @param {RegExp} `regex` + * @return {Object} + */ - if (offsetLine < lastOffset.line || - (offsetLine === lastOffset.line && offsetColumn < lastOffset.column)) { - throw new Error('Section offsets must be ordered and non-overlapping.'); + match: function(regex) { + var m = regex.exec(this.input); + if (m) { + this.updatePosition(m[0], m[0].length); + return m; } - lastOffset = offset; + }, - return { - generatedOffset: { - // The offset fields are 0-based, but we use 1-based indices when - // encoding/decoding from VLQ. - generatedLine: offsetLine + 1, - generatedColumn: offsetColumn + 1 - }, - consumer: new SourceMapConsumer(util.getArg(s, 'map')) + /** + * Capture `type` with the given regex. + * @param {String} `type` + * @param {RegExp} `regex` + * @return {Function} + */ + + capture: function(type, regex) { + if (typeof regex === 'function') { + return this.set.apply(this, arguments); } - }); -} -IndexedSourceMapConsumer.prototype = Object.create(SourceMapConsumer.prototype); -IndexedSourceMapConsumer.prototype.constructor = SourceMapConsumer; + this.regex.set(type, regex); + this.set(type, function() { + var parsed = this.parsed; + var pos = this.position(); + var m = this.match(regex); + if (!m || !m[0]) return; -/** - * The version of the source mapping spec that we are consuming. - */ -IndexedSourceMapConsumer.prototype._version = 3; + var prev = this.prev(); + var node = pos({ + type: type, + val: m[0], + parsed: parsed, + rest: this.input + }); -/** - * The list of original sources. - */ -Object.defineProperty(IndexedSourceMapConsumer.prototype, 'sources', { - get: function () { - var sources = []; - for (var i = 0; i < this._sections.length; i++) { - for (var j = 0; j < this._sections[i].consumer.sources.length; j++) { - sources.push(this._sections[i].consumer.sources[j]); + if (m[1]) { + node.inner = m[1]; } - } - return sources; - } -}); -/** - * Returns the original source, line, and column information for the generated - * source's line and column positions provided. The only argument is an object - * with the following properties: - * - * - line: The line number in the generated source. - * - column: The column number in the generated source. - * - * and an object is returned with the following properties: - * - * - source: The original source file, or null. - * - line: The line number in the original source, or null. - * - column: The column number in the original source, or null. - * - name: The original identifier, or null. - */ -IndexedSourceMapConsumer.prototype.originalPositionFor = - function IndexedSourceMapConsumer_originalPositionFor(aArgs) { - var needle = { - generatedLine: util.getArg(aArgs, 'line'), - generatedColumn: util.getArg(aArgs, 'column') - }; + define(node, 'inside', this.stack.length > 0); + define(node, 'parent', prev); + prev.nodes.push(node); + }.bind(this)); + return this; + }, - // Find the section containing the generated position we're trying to map - // to an original position. - var sectionIndex = binarySearch.search(needle, this._sections, - function(needle, section) { - var cmp = needle.generatedLine - section.generatedOffset.generatedLine; - if (cmp) { - return cmp; - } + /** + * Create a parser with open and close for parens, + * brackets or braces + */ + + capturePair: function(type, openRegex, closeRegex, fn) { + this.sets[type] = this.sets[type] || []; + + /** + * Open + */ + + this.set(type + '.open', function() { + var parsed = this.parsed; + var pos = this.position(); + var m = this.match(openRegex); + if (!m || !m[0]) return; + + var val = m[0]; + this.setCount++; + this.specialChars = true; + var open = pos({ + type: type + '.open', + val: val, + rest: this.input + }); - return (needle.generatedColumn - - section.generatedOffset.generatedColumn); + if (typeof m[1] !== 'undefined') { + open.inner = m[1]; + } + + var prev = this.prev(); + var node = pos({ + type: type, + nodes: [open] }); - var section = this._sections[sectionIndex]; - if (!section) { - return { - source: null, - line: null, - column: null, - name: null - }; - } + define(node, 'rest', this.input); + define(node, 'parsed', parsed); + define(node, 'prefix', m[1]); + define(node, 'parent', prev); + define(open, 'parent', node); - return section.consumer.originalPositionFor({ - line: needle.generatedLine - - (section.generatedOffset.generatedLine - 1), - column: needle.generatedColumn - - (section.generatedOffset.generatedLine === needle.generatedLine - ? section.generatedOffset.generatedColumn - 1 - : 0), - bias: aArgs.bias - }); - }; + if (typeof fn === 'function') { + fn.call(this, open, node); + } -/** - * Return true if we have the source content for every source in the source - * map, false otherwise. - */ -IndexedSourceMapConsumer.prototype.hasContentsOfAllSources = - function IndexedSourceMapConsumer_hasContentsOfAllSources() { - return this._sections.every(function (s) { - return s.consumer.hasContentsOfAllSources(); + this.push(type, node); + prev.nodes.push(node); }); - }; -/** - * Returns the original source content. The only argument is the url of the - * original source file. Returns null if no original source content is - * available. - */ -IndexedSourceMapConsumer.prototype.sourceContentFor = - function IndexedSourceMapConsumer_sourceContentFor(aSource, nullOnMissing) { - for (var i = 0; i < this._sections.length; i++) { - var section = this._sections[i]; + /** + * Close + */ - var content = section.consumer.sourceContentFor(aSource, true); - if (content) { - return content; - } - } - if (nullOnMissing) { - return null; - } - else { - throw new Error('"' + aSource + '" is not in the SourceMap.'); - } - }; + this.set(type + '.close', function() { + var pos = this.position(); + var m = this.match(closeRegex); + if (!m || !m[0]) return; -/** - * Returns the generated line and column information for the original source, - * line, and column positions provided. The only argument is an object with - * the following properties: - * - * - source: The filename of the original source. - * - line: The line number in the original source. - * - column: The column number in the original source. - * - * and an object is returned with the following properties: - * - * - line: The line number in the generated source, or null. - * - column: The column number in the generated source, or null. - */ -IndexedSourceMapConsumer.prototype.generatedPositionFor = - function IndexedSourceMapConsumer_generatedPositionFor(aArgs) { - for (var i = 0; i < this._sections.length; i++) { - var section = this._sections[i]; + var parent = this.pop(type); + var node = pos({ + type: type + '.close', + rest: this.input, + suffix: m[1], + val: m[0] + }); - // Only consider this section if the requested source is in the list of - // sources of the consumer. - if (section.consumer.sources.indexOf(util.getArg(aArgs, 'source')) === -1) { - continue; + if (!this.isType(parent, type)) { + if (this.options.strict) { + throw new Error('missing opening "' + type + '"'); + } + + this.setCount--; + node.escaped = true; + return node; } - var generatedPosition = section.consumer.generatedPositionFor(aArgs); - if (generatedPosition) { - var ret = { - line: generatedPosition.line + - (section.generatedOffset.generatedLine - 1), - column: generatedPosition.column + - (section.generatedOffset.generatedLine === generatedPosition.line - ? section.generatedOffset.generatedColumn - 1 - : 0) - }; - return ret; + + if (node.suffix === '\\') { + parent.escaped = true; + node.escaped = true; } - } - return { - line: null, - column: null - }; - }; + parent.nodes.push(node); + define(node, 'parent', parent); + }); -/** - * Parse the mappings in a string in to a data structure which we can easily - * query (the ordered arrays in the `this.__generatedMappings` and - * `this.__originalMappings` properties). - */ -IndexedSourceMapConsumer.prototype._parseMappings = - function IndexedSourceMapConsumer_parseMappings(aStr, aSourceRoot) { - this.__generatedMappings = []; - this.__originalMappings = []; - for (var i = 0; i < this._sections.length; i++) { - var section = this._sections[i]; - var sectionMappings = section.consumer._generatedMappings; - for (var j = 0; j < sectionMappings.length; j++) { - var mapping = sectionMappings[j]; + return this; + }, - var source = section.consumer._sources.at(mapping.source); - if (section.consumer.sourceRoot !== null) { - source = util.join(section.consumer.sourceRoot, source); - } - this._sources.add(source); - source = this._sources.indexOf(source); + /** + * Capture end-of-string + */ - var name = section.consumer._names.at(mapping.name); - this._names.add(name); - name = this._names.indexOf(name); + eos: function() { + var pos = this.position(); + if (this.input) return; + var prev = this.prev(); - // The mappings coming from the consumer for the section have - // generated positions relative to the start of the section, so we - // need to offset them to be relative to the start of the concatenated - // generated file. - var adjustedMapping = { - source: source, - generatedLine: mapping.generatedLine + - (section.generatedOffset.generatedLine - 1), - generatedColumn: mapping.generatedColumn + - (section.generatedOffset.generatedLine === mapping.generatedLine - ? section.generatedOffset.generatedColumn - 1 - : 0), - originalLine: mapping.originalLine, - originalColumn: mapping.originalColumn, - name: name - }; + while (prev.type !== 'root' && !prev.visited) { + if (this.options.strict === true) { + throw new SyntaxError('invalid syntax:' + util.inspect(prev, null, 2)); + } - this.__generatedMappings.push(adjustedMapping); - if (typeof adjustedMapping.originalLine === 'number') { - this.__originalMappings.push(adjustedMapping); - } + if (!hasDelims(prev)) { + prev.parent.escaped = true; + prev.escaped = true; } - } - quickSort(this.__generatedMappings, util.compareByGeneratedPositionsDeflated); - quickSort(this.__originalMappings, util.compareByOriginalPositions); - }; + visit(prev, function(node) { + if (!hasDelims(node.parent)) { + node.parent.escaped = true; + node.escaped = true; + } + }); -exports.IndexedSourceMapConsumer = IndexedSourceMapConsumer; + prev = prev.parent; + } + var tok = pos({ + type: 'eos', + val: this.append || '' + }); -/***/ }), -/* 671 */ -/***/ (function(module, exports) { + define(tok, 'parent', this.ast); + return tok; + }, -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ + /** + * Run parsers to advance the cursor position + */ -exports.GREATEST_LOWER_BOUND = 1; -exports.LEAST_UPPER_BOUND = 2; + next: function() { + var parsed = this.parsed; + var len = this.types.length; + var idx = -1; + var tok; -/** - * Recursive implementation of binary search. - * - * @param aLow Indices here and lower do not contain the needle. - * @param aHigh Indices here and higher do not contain the needle. - * @param aNeedle The element being searched for. - * @param aHaystack The non-empty array being searched. - * @param aCompare Function which takes two elements and returns -1, 0, or 1. - * @param aBias Either 'binarySearch.GREATEST_LOWER_BOUND' or - * 'binarySearch.LEAST_UPPER_BOUND'. Specifies whether to return the - * closest element that is smaller than or greater than the one we are - * searching for, respectively, if the exact element cannot be found. - */ -function recursiveSearch(aLow, aHigh, aNeedle, aHaystack, aCompare, aBias) { - // This function terminates when one of the following is true: - // - // 1. We find the exact element we are looking for. - // - // 2. We did not find the exact element, but we can return the index of - // the next-closest element. - // - // 3. We did not find the exact element, and there is no next-closest - // element than the one we are searching for, so we return -1. - var mid = Math.floor((aHigh - aLow) / 2) + aLow; - var cmp = aCompare(aNeedle, aHaystack[mid], true); - if (cmp === 0) { - // Found the element we are looking for. - return mid; - } - else if (cmp > 0) { - // Our needle is greater than aHaystack[mid]. - if (aHigh - mid > 1) { - // The element is in the upper half. - return recursiveSearch(mid, aHigh, aNeedle, aHaystack, aCompare, aBias); + while (++idx < len) { + if ((tok = this.parsers[this.types[idx]].call(this))) { + define(tok, 'rest', this.input); + define(tok, 'parsed', parsed); + this.last = tok; + return tok; + } } + }, - // The exact needle element was not found in this haystack. Determine if - // we are in termination case (3) or (2) and return the appropriate thing. - if (aBias == exports.LEAST_UPPER_BOUND) { - return aHigh < aHaystack.length ? aHigh : -1; - } else { - return mid; - } - } - else { - // Our needle is less than aHaystack[mid]. - if (mid - aLow > 1) { - // The element is in the lower half. - return recursiveSearch(aLow, mid, aNeedle, aHaystack, aCompare, aBias); - } + /** + * Parse the given string. + * @return {Array} + */ - // we are in termination case (3) or (2) and return the appropriate thing. - if (aBias == exports.LEAST_UPPER_BOUND) { - return mid; - } else { - return aLow < 0 ? -1 : aLow; + parse: function(input) { + if (typeof input !== 'string') { + throw new TypeError('expected a string'); } - } -} -/** - * This is an implementation of binary search which will always try and return - * the index of the closest element if there is no exact hit. This is because - * mappings between original and generated line/col pairs are single points, - * and there is an implicit region between each of them, so a miss just means - * that you aren't on the very start of a region. - * - * @param aNeedle The element you are looking for. - * @param aHaystack The array that is being searched. - * @param aCompare A function which takes the needle and an element in the - * array and returns -1, 0, or 1 depending on whether the needle is less - * than, equal to, or greater than the element, respectively. - * @param aBias Either 'binarySearch.GREATEST_LOWER_BOUND' or - * 'binarySearch.LEAST_UPPER_BOUND'. Specifies whether to return the - * closest element that is smaller than or greater than the one we are - * searching for, respectively, if the exact element cannot be found. - * Defaults to 'binarySearch.GREATEST_LOWER_BOUND'. - */ -exports.search = function search(aNeedle, aHaystack, aCompare, aBias) { - if (aHaystack.length === 0) { - return -1; - } + this.init(this.options); + this.orig = input; + this.input = input; + var self = this; - var index = recursiveSearch(-1, aHaystack.length, aNeedle, aHaystack, - aCompare, aBias || exports.GREATEST_LOWER_BOUND); - if (index < 0) { - return -1; - } + function parse() { + // check input before calling `.next()` + input = self.input; - // We have found either the exact element, or the next-closest element than - // the one we are searching for. However, there may be more than one such - // element. Make sure we always return the smallest of these. - while (index - 1 >= 0) { - if (aCompare(aHaystack[index], aHaystack[index - 1], true) !== 0) { - break; - } - --index; - } + // get the next AST ndoe + var node = self.next(); + if (node) { + var prev = self.prev(); + if (prev) { + define(node, 'parent', prev); + if (prev.nodes) { + prev.nodes.push(node); + } + } - return index; -}; + if (self.sets.hasOwnProperty(prev.type)) { + self.currentType = prev.type; + } + } + // if we got here but input is not changed, throw an error + if (self.input && input === self.input) { + throw new Error('no parsers registered for: "' + self.input.slice(0, 5) + '"'); + } + } -/***/ }), -/* 672 */ -/***/ (function(module, exports) { + while (this.input) parse(); + if (this.stack.length && this.options.strict) { + var node = this.stack.pop(); + throw this.error('missing opening ' + node.type + ': "' + this.orig + '"'); + } -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause - */ + var eos = this.eos(); + var tok = this.prev(); + if (tok.type !== 'eos') { + this.ast.nodes.push(eos); + } -// It turns out that some (most?) JavaScript engines don't self-host -// `Array.prototype.sort`. This makes sense because C++ will likely remain -// faster than JS when doing raw CPU-intensive sorting. However, when using a -// custom comparator function, calling back and forth between the VM's C++ and -// JIT'd JS is rather slow *and* loses JIT type information, resulting in -// worse generated code for the comparator function than would be optimal. In -// fact, when sorting with a comparator, these costs outweigh the benefits of -// sorting in C++. By using our own JS-implemented Quick Sort (below), we get -// a ~3500ms mean speed-up in `bench/bench.html`. + return this.ast; + } +}; /** - * Swap the elements indexed by `x` and `y` in the array `ary`. - * - * @param {Array} ary - * The array. - * @param {Number} x - * The index of the first item. - * @param {Number} y - * The index of the second item. + * Visit `node` with the given `fn` */ -function swap(ary, x, y) { - var temp = ary[x]; - ary[x] = ary[y]; - ary[y] = temp; -} -/** - * Returns a random integer within the range `low .. high` inclusive. - * - * @param {Number} low - * The lower bound on the range. - * @param {Number} high - * The upper bound on the range. - */ -function randomIntInRange(low, high) { - return Math.round(low + (Math.random() * (high - low))); +function visit(node, fn) { + if (!node.visited) { + define(node, 'visited', true); + return node.nodes ? mapVisit(node.nodes, fn) : fn(node); + } + return node; } /** - * The Quick Sort algorithm. - * - * @param {Array} ary - * An array to sort. - * @param {function} comparator - * Function to use to compare two items. - * @param {Number} p - * Start index of the array - * @param {Number} r - * End index of the array + * Map visit over array of `nodes`. */ -function doQuickSort(ary, comparator, p, r) { - // If our lower bound is less than our upper bound, we (1) partition the - // array into two pieces and (2) recurse on each half. If it is not, this is - // the empty array and our base case. - - if (p < r) { - // (1) Partitioning. - // - // The partitioning chooses a pivot between `p` and `r` and moves all - // elements that are less than or equal to the pivot to the before it, and - // all the elements that are greater than it after it. The effect is that - // once partition is done, the pivot is in the exact place it will be when - // the array is put in sorted order, and it will not need to be moved - // again. This runs in O(n) time. - - // Always choose a random pivot so that an input array which is reverse - // sorted does not cause O(n^2) running time. - var pivotIndex = randomIntInRange(p, r); - var i = p - 1; - - swap(ary, pivotIndex, r); - var pivot = ary[r]; - // Immediately after `j` is incremented in this loop, the following hold - // true: - // - // * Every element in `ary[p .. i]` is less than or equal to the pivot. - // - // * Every element in `ary[i+1 .. j-1]` is greater than the pivot. - for (var j = p; j < r; j++) { - if (comparator(ary[j], pivot) <= 0) { - i += 1; - swap(ary, i, j); - } - } +function mapVisit(nodes, fn) { + var len = nodes.length; + var idx = -1; + while (++idx < len) { + visit(nodes[idx], fn); + } +} - swap(ary, i + 1, j); - var q = i + 1; +function hasOpen(node) { + return node.nodes && node.nodes[0].type === (node.type + '.open'); +} - // (2) Recurse on each half. +function hasClose(node) { + return node.nodes && utils.last(node.nodes).type === (node.type + '.close'); +} - doQuickSort(ary, comparator, p, q - 1); - doQuickSort(ary, comparator, q + 1, r); - } +function hasDelims(node) { + return hasOpen(node) && hasClose(node); } /** - * Sort the given array in-place with the given comparator function. - * - * @param {Array} ary - * An array to sort. - * @param {function} comparator - * Function to use to compare two items. + * Expose `Parser` */ -exports.quickSort = function (ary, comparator) { - doQuickSort(ary, comparator, 0, ary.length - 1); -}; + +module.exports = Parser; /***/ }), -/* 673 */ +/* 689 */ /***/ (function(module, exports, __webpack_require__) { -/* -*- Mode: js; js-indent-level: 2; -*- */ -/* - * Copyright 2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE or: - * http://opensource.org/licenses/BSD-3-Clause +"use strict"; +/*! + * map-cache + * + * Copyright (c) 2015, Jon Schlinkert. + * Licensed under the MIT License. */ -var SourceMapGenerator = __webpack_require__(664).SourceMapGenerator; -var util = __webpack_require__(667); - -// Matches a Windows-style `\r\n` newline or a `\n` newline used by all other -// operating systems these days (capturing the result). -var REGEX_NEWLINE = /(\r?\n)/; -// Newline character code for charCodeAt() comparisons -var NEWLINE_CODE = 10; -// Private symbol for identifying `SourceNode`s when multiple versions of -// the source-map library are loaded. This MUST NOT CHANGE across -// versions! -var isSourceNode = "$$$isSourceNode$$$"; +var hasOwn = Object.prototype.hasOwnProperty; /** - * SourceNodes provide a way to abstract over interpolating/concatenating - * snippets of generated JavaScript source code while maintaining the line and - * column information associated with the original source code. - * - * @param aLine The original line number. - * @param aColumn The original column number. - * @param aSource The original source's filename. - * @param aChunks Optional. An array of strings which are snippets of - * generated JS, or other SourceNodes. - * @param aName The original identifier. + * Expose `MapCache` */ -function SourceNode(aLine, aColumn, aSource, aChunks, aName) { - this.children = []; - this.sourceContents = {}; - this.line = aLine == null ? null : aLine; - this.column = aColumn == null ? null : aColumn; - this.source = aSource == null ? null : aSource; - this.name = aName == null ? null : aName; - this[isSourceNode] = true; - if (aChunks != null) this.add(aChunks); -} + +module.exports = MapCache; /** - * Creates a SourceNode from generated code and a SourceMapConsumer. + * Creates a cache object to store key/value pairs. * - * @param aGeneratedCode The generated code - * @param aSourceMapConsumer The SourceMap for the generated code - * @param aRelativePath Optional. The path that relative sources in the - * SourceMapConsumer should be relative to. + * ```js + * var cache = new MapCache(); + * ``` + * + * @api public */ -SourceNode.fromStringWithSourceMap = - function SourceNode_fromStringWithSourceMap(aGeneratedCode, aSourceMapConsumer, aRelativePath) { - // The SourceNode we want to fill with the generated code - // and the SourceMap - var node = new SourceNode(); - - // All even indices of this array are one line of the generated code, - // while all odd indices are the newlines between two adjacent lines - // (since `REGEX_NEWLINE` captures its match). - // Processed fragments are accessed by calling `shiftNextLine`. - var remainingLines = aGeneratedCode.split(REGEX_NEWLINE); - var remainingLinesIndex = 0; - var shiftNextLine = function() { - var lineContents = getNextLine(); - // The last line of a file might not have a newline. - var newLine = getNextLine() || ""; - return lineContents + newLine; - - function getNextLine() { - return remainingLinesIndex < remainingLines.length ? - remainingLines[remainingLinesIndex++] : undefined; - } - }; - - // We need to remember the position of "remainingLines" - var lastGeneratedLine = 1, lastGeneratedColumn = 0; - - // The generate SourceNodes we need a code range. - // To extract it current and last mapping is used. - // Here we store the last mapping. - var lastMapping = null; - - aSourceMapConsumer.eachMapping(function (mapping) { - if (lastMapping !== null) { - // We add the code from "lastMapping" to "mapping": - // First check if there is a new line in between. - if (lastGeneratedLine < mapping.generatedLine) { - // Associate first line with "lastMapping" - addMappingWithCode(lastMapping, shiftNextLine()); - lastGeneratedLine++; - lastGeneratedColumn = 0; - // The remaining code is added without mapping - } else { - // There is no new line in between. - // Associate the code between "lastGeneratedColumn" and - // "mapping.generatedColumn" with "lastMapping" - var nextLine = remainingLines[remainingLinesIndex]; - var code = nextLine.substr(0, mapping.generatedColumn - - lastGeneratedColumn); - remainingLines[remainingLinesIndex] = nextLine.substr(mapping.generatedColumn - - lastGeneratedColumn); - lastGeneratedColumn = mapping.generatedColumn; - addMappingWithCode(lastMapping, code); - // No more remaining code, continue - lastMapping = mapping; - return; - } - } - // We add the generated code until the first mapping - // to the SourceNode without any mapping. - // Each line is added as separate string. - while (lastGeneratedLine < mapping.generatedLine) { - node.add(shiftNextLine()); - lastGeneratedLine++; - } - if (lastGeneratedColumn < mapping.generatedColumn) { - var nextLine = remainingLines[remainingLinesIndex]; - node.add(nextLine.substr(0, mapping.generatedColumn)); - remainingLines[remainingLinesIndex] = nextLine.substr(mapping.generatedColumn); - lastGeneratedColumn = mapping.generatedColumn; - } - lastMapping = mapping; - }, this); - // We have processed all mappings. - if (remainingLinesIndex < remainingLines.length) { - if (lastMapping) { - // Associate the remaining code in the current line with "lastMapping" - addMappingWithCode(lastMapping, shiftNextLine()); - } - // and add the remaining lines without any mapping - node.add(remainingLines.splice(remainingLinesIndex).join("")); - } - - // Copy sourcesContent into SourceNode - aSourceMapConsumer.sources.forEach(function (sourceFile) { - var content = aSourceMapConsumer.sourceContentFor(sourceFile); - if (content != null) { - if (aRelativePath != null) { - sourceFile = util.join(aRelativePath, sourceFile); - } - node.setSourceContent(sourceFile, content); - } - }); - - return node; - function addMappingWithCode(mapping, code) { - if (mapping === null || mapping.source === undefined) { - node.add(code); - } else { - var source = aRelativePath - ? util.join(aRelativePath, mapping.source) - : mapping.source; - node.add(new SourceNode(mapping.originalLine, - mapping.originalColumn, - source, - code, - mapping.name)); - } - } - }; +function MapCache(data) { + this.__data__ = data || {}; +} /** - * Add a chunk of generated JS to this source node. + * Adds `value` to `key` on the cache. * - * @param aChunk A string snippet of generated JS code, another instance of - * SourceNode, or an array where each member is one of those things. + * ```js + * cache.set('foo', 'bar'); + * ``` + * + * @param {String} `key` The key of the value to cache. + * @param {*} `value` The value to cache. + * @returns {Object} Returns the `Cache` object for chaining. + * @api public */ -SourceNode.prototype.add = function SourceNode_add(aChunk) { - if (Array.isArray(aChunk)) { - aChunk.forEach(function (chunk) { - this.add(chunk); - }, this); - } - else if (aChunk[isSourceNode] || typeof aChunk === "string") { - if (aChunk) { - this.children.push(aChunk); - } - } - else { - throw new TypeError( - "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk - ); + +MapCache.prototype.set = function mapSet(key, value) { + if (key !== '__proto__') { + this.__data__[key] = value; } return this; }; /** - * Add a chunk of generated JS to the beginning of this source node. + * Gets the cached value for `key`. * - * @param aChunk A string snippet of generated JS code, another instance of - * SourceNode, or an array where each member is one of those things. + * ```js + * cache.get('foo'); + * //=> 'bar' + * ``` + * + * @param {String} `key` The key of the value to get. + * @returns {*} Returns the cached value. + * @api public */ -SourceNode.prototype.prepend = function SourceNode_prepend(aChunk) { - if (Array.isArray(aChunk)) { - for (var i = aChunk.length-1; i >= 0; i--) { - this.prepend(aChunk[i]); - } - } - else if (aChunk[isSourceNode] || typeof aChunk === "string") { - this.children.unshift(aChunk); - } - else { - throw new TypeError( - "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk - ); - } - return this; + +MapCache.prototype.get = function mapGet(key) { + return key === '__proto__' ? undefined : this.__data__[key]; }; /** - * Walk over the tree of JS snippets in this node and its children. The - * walking function is called once for each snippet of JS and is passed that - * snippet and the its original associated source's line/column location. + * Checks if a cached value for `key` exists. * - * @param aFn The traversal function. + * ```js + * cache.has('foo'); + * //=> true + * ``` + * + * @param {String} `key` The key of the entry to check. + * @returns {Boolean} Returns `true` if an entry for `key` exists, else `false`. + * @api public */ -SourceNode.prototype.walk = function SourceNode_walk(aFn) { - var chunk; - for (var i = 0, len = this.children.length; i < len; i++) { - chunk = this.children[i]; - if (chunk[isSourceNode]) { - chunk.walk(aFn); - } - else { - if (chunk !== '') { - aFn(chunk, { source: this.source, - line: this.line, - column: this.column, - name: this.name }); - } - } - } + +MapCache.prototype.has = function mapHas(key) { + return key !== '__proto__' && hasOwn.call(this.__data__, key); }; /** - * Like `String.prototype.join` except for SourceNodes. Inserts `aStr` between - * each of `this.children`. + * Removes `key` and its value from the cache. * - * @param aSep The separator. + * ```js + * cache.del('foo'); + * ``` + * @title .del + * @param {String} `key` The key of the value to remove. + * @returns {Boolean} Returns `true` if the entry was removed successfully, else `false`. + * @api public */ -SourceNode.prototype.join = function SourceNode_join(aSep) { - var newChildren; - var i; - var len = this.children.length; - if (len > 0) { - newChildren = []; - for (i = 0; i < len-1; i++) { - newChildren.push(this.children[i]); - newChildren.push(aSep); - } - newChildren.push(this.children[i]); - this.children = newChildren; - } - return this; + +MapCache.prototype.del = function mapDelete(key) { + return this.has(key) && delete this.__data__[key]; }; + +/***/ }), +/* 690 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var define = __webpack_require__(654); + /** - * Call String.prototype.replace on the very right-most source snippet. Useful - * for trimming whitespace from the end of a source node, etc. - * - * @param aPattern The pattern to replace. - * @param aReplacement The thing to replace the pattern with. + * Store position for a node */ -SourceNode.prototype.replaceRight = function SourceNode_replaceRight(aPattern, aReplacement) { - var lastChild = this.children[this.children.length - 1]; - if (lastChild[isSourceNode]) { - lastChild.replaceRight(aPattern, aReplacement); + +module.exports = function Position(start, parser) { + this.start = start; + this.end = { line: parser.line, column: parser.column }; + define(this, 'content', parser.orig); + define(this, 'source', parser.options.source); +}; + + +/***/ }), +/* 691 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var isExtendable = __webpack_require__(692); +var assignSymbols = __webpack_require__(595); + +module.exports = Object.assign || function(obj/*, objects*/) { + if (obj === null || typeof obj === 'undefined') { + throw new TypeError('Cannot convert undefined or null to object'); } - else if (typeof lastChild === 'string') { - this.children[this.children.length - 1] = lastChild.replace(aPattern, aReplacement); + if (!isObject(obj)) { + obj = {}; } - else { - this.children.push(''.replace(aPattern, aReplacement)); + for (var i = 1; i < arguments.length; i++) { + var val = arguments[i]; + if (isString(val)) { + val = toObject(val); + } + if (isObject(val)) { + assign(obj, val); + assignSymbols(obj, val); + } } - return this; + return obj; }; +function assign(a, b) { + for (var key in b) { + if (hasOwn(b, key)) { + a[key] = b[key]; + } + } +} + +function isString(val) { + return (val && typeof val === 'string'); +} + +function toObject(str) { + var obj = {}; + for (var i in str) { + obj[i] = str[i]; + } + return obj; +} + +function isObject(val) { + return (val && typeof val === 'object') || isExtendable(val); +} + /** - * Set the source content for a source file. This will be added to the SourceMapGenerator - * in the sourcesContent field. - * - * @param aSourceFile The filename of the source file - * @param aSourceContent The content of the source file + * Returns true if the given `key` is an own property of `obj`. */ -SourceNode.prototype.setSourceContent = - function SourceNode_setSourceContent(aSourceFile, aSourceContent) { - this.sourceContents[util.toSetString(aSourceFile)] = aSourceContent; - }; -/** - * Walk over the tree of SourceNodes. The walking function is called for each - * source file content and is passed the filename and source content. +function hasOwn(obj, key) { + return Object.prototype.hasOwnProperty.call(obj, key); +} + +function isEnum(obj, key) { + return Object.prototype.propertyIsEnumerable.call(obj, key); +} + + +/***/ }), +/* 692 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +/*! + * is-extendable * - * @param aFn The traversal function. + * Copyright (c) 2015-2017, Jon Schlinkert. + * Released under the MIT License. */ -SourceNode.prototype.walkSourceContents = - function SourceNode_walkSourceContents(aFn) { - for (var i = 0, len = this.children.length; i < len; i++) { - if (this.children[i][isSourceNode]) { - this.children[i].walkSourceContents(aFn); - } - } - var sources = Object.keys(this.sourceContents); - for (var i = 0, len = sources.length; i < len; i++) { - aFn(util.fromSetString(sources[i]), this.sourceContents[sources[i]]); - } - }; -/** - * Return the string representation of this source node. Walks over the tree - * and concatenates all the various snippets together to one string. - */ -SourceNode.prototype.toString = function SourceNode_toString() { - var str = ""; - this.walk(function (chunk) { - str += chunk; - }); - return str; -}; -/** - * Returns the string representation of this source node along with a source - * map. - */ -SourceNode.prototype.toStringWithSourceMap = function SourceNode_toStringWithSourceMap(aArgs) { - var generated = { - code: "", - line: 1, - column: 0 - }; - var map = new SourceMapGenerator(aArgs); - var sourceMappingActive = false; - var lastOriginalSource = null; - var lastOriginalLine = null; - var lastOriginalColumn = null; - var lastOriginalName = null; - this.walk(function (chunk, original) { - generated.code += chunk; - if (original.source !== null - && original.line !== null - && original.column !== null) { - if(lastOriginalSource !== original.source - || lastOriginalLine !== original.line - || lastOriginalColumn !== original.column - || lastOriginalName !== original.name) { - map.addMapping({ - source: original.source, - original: { - line: original.line, - column: original.column - }, - generated: { - line: generated.line, - column: generated.column - }, - name: original.name - }); - } - lastOriginalSource = original.source; - lastOriginalLine = original.line; - lastOriginalColumn = original.column; - lastOriginalName = original.name; - sourceMappingActive = true; - } else if (sourceMappingActive) { - map.addMapping({ - generated: { - line: generated.line, - column: generated.column - } - }); - lastOriginalSource = null; - sourceMappingActive = false; - } - for (var idx = 0, length = chunk.length; idx < length; idx++) { - if (chunk.charCodeAt(idx) === NEWLINE_CODE) { - generated.line++; - generated.column = 0; - // Mappings end at eol - if (idx + 1 === length) { - lastOriginalSource = null; - sourceMappingActive = false; - } else if (sourceMappingActive) { - map.addMapping({ - source: original.source, - original: { - line: original.line, - column: original.column - }, - generated: { - line: generated.line, - column: generated.column - }, - name: original.name - }); - } - } else { - generated.column++; - } - } - }); - this.walkSourceContents(function (sourceFile, sourceContent) { - map.setSourceContent(sourceFile, sourceContent); - }); +var isPlainObject = __webpack_require__(594); - return { code: generated.code, map: map }; +module.exports = function isExtendable(val) { + return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); }; -exports.SourceNode = SourceNode; - /***/ }), -/* 674 */ +/* 693 */ /***/ (function(module, exports, __webpack_require__) { -// Copyright 2014, 2015, 2016, 2017 Simon Lydell -// X11 (“MIT”) Licensed. (See LICENSE.) +"use strict"; -var sourceMappingURL = __webpack_require__(675) -var resolveUrl = __webpack_require__(676) -var decodeUriComponent = __webpack_require__(677) -var urix = __webpack_require__(679) -var atob = __webpack_require__(680) +var nanomatch = __webpack_require__(694); +var extglob = __webpack_require__(708); +module.exports = function(snapdragon) { + var compilers = snapdragon.compiler.compilers; + var opts = snapdragon.options; -function callbackAsync(callback, error, result) { - setImmediate(function() { callback(error, result) }) -} + // register nanomatch compilers + snapdragon.use(nanomatch.compilers); -function parseMapToJSON(string, data) { - try { - return JSON.parse(string.replace(/^\)\]\}'/, "")) - } catch (error) { - error.sourceMapData = data - throw error - } -} + // get references to some specific nanomatch compilers before they + // are overridden by the extglob and/or custom compilers + var escape = compilers.escape; + var qmark = compilers.qmark; + var slash = compilers.slash; + var star = compilers.star; + var text = compilers.text; + var plus = compilers.plus; + var dot = compilers.dot; -function readSync(read, url, data) { - var readUrl = decodeUriComponent(url) - try { - return String(read(readUrl)) - } catch (error) { - error.sourceMapData = data - throw error + // register extglob compilers or escape exglobs if disabled + if (opts.extglob === false || opts.noext === true) { + snapdragon.compiler.use(escapeExtglobs); + } else { + snapdragon.use(extglob.compilers); } -} + snapdragon.use(function() { + this.options.star = this.options.star || function(/*node*/) { + return '[^\\\\/]*?'; + }; + }); + // custom micromatch compilers + snapdragon.compiler -function resolveSourceMap(code, codeUrl, read, callback) { - var mapData - try { - mapData = resolveSourceMapHelper(code, codeUrl) - } catch (error) { - return callbackAsync(callback, error) - } - if (!mapData || mapData.map) { - return callbackAsync(callback, null, mapData) + // reset referenced compiler + .set('dot', dot) + .set('escape', escape) + .set('plus', plus) + .set('slash', slash) + .set('qmark', qmark) + .set('star', star) + .set('text', text); +}; + +function escapeExtglobs(compiler) { + compiler.set('paren', function(node) { + var val = ''; + visit(node, function(tok) { + if (tok.val) val += (/^\W/.test(tok.val) ? '\\' : '') + tok.val; + }); + return this.emit(val, node); + }); + + /** + * Visit `node` with the given `fn` + */ + + function visit(node, fn) { + return node.nodes ? mapVisit(node.nodes, fn) : fn(node); } - var readUrl = decodeUriComponent(mapData.url) - read(readUrl, function(error, result) { - if (error) { - error.sourceMapData = mapData - return callback(error) - } - mapData.map = String(result) - try { - mapData.map = parseMapToJSON(mapData.map, mapData) - } catch (error) { - return callback(error) - } - callback(null, mapData) - }) -} -function resolveSourceMapSync(code, codeUrl, read) { - var mapData = resolveSourceMapHelper(code, codeUrl) - if (!mapData || mapData.map) { - return mapData + /** + * Map visit over array of `nodes`. + */ + + function mapVisit(nodes, fn) { + var len = nodes.length; + var idx = -1; + while (++idx < len) { + visit(nodes[idx], fn); + } } - mapData.map = readSync(read, mapData.url, mapData) - mapData.map = parseMapToJSON(mapData.map, mapData) - return mapData } -var dataUriRegex = /^data:([^,;]*)(;[^,;]*)*(?:,(.*))?$/ -var jsonMimeTypeRegex = /^(?:application|text)\/json$/ -function resolveSourceMapHelper(code, codeUrl) { - codeUrl = urix(codeUrl) +/***/ }), +/* 694 */ +/***/ (function(module, exports, __webpack_require__) { - var url = sourceMappingURL.getFrom(code) - if (!url) { - return null - } +"use strict"; - var dataUri = url.match(dataUriRegex) - if (dataUri) { - var mimeType = dataUri[1] - var lastParameter = dataUri[2] || "" - var encoded = dataUri[3] || "" - var data = { - sourceMappingURL: url, - url: null, - sourcesRelativeTo: codeUrl, - map: encoded - } - if (!jsonMimeTypeRegex.test(mimeType)) { - var error = new Error("Unuseful data uri mime type: " + (mimeType || "text/plain")) - error.sourceMapData = data - throw error - } - data.map = parseMapToJSON( - lastParameter === ";base64" ? atob(encoded) : decodeURIComponent(encoded), - data - ) - return data - } - var mapUrl = resolveUrl(codeUrl, url) - return { - sourceMappingURL: url, - url: mapUrl, - sourcesRelativeTo: mapUrl, - map: null - } -} +/** + * Module dependencies + */ +var util = __webpack_require__(113); +var toRegex = __webpack_require__(579); +var extend = __webpack_require__(695); +/** + * Local dependencies + */ -function resolveSources(map, mapUrl, read, options, callback) { - if (typeof options === "function") { - callback = options - options = {} - } - var pending = map.sources ? map.sources.length : 0 - var result = { - sourcesResolved: [], - sourcesContent: [] +var compilers = __webpack_require__(697); +var parsers = __webpack_require__(698); +var cache = __webpack_require__(701); +var utils = __webpack_require__(703); +var MAX_LENGTH = 1024 * 64; + +/** + * The main function takes a list of strings and one or more + * glob patterns to use for matching. + * + * ```js + * var nm = require('nanomatch'); + * nm(list, patterns[, options]); + * + * console.log(nm(['a.js', 'a.txt'], ['*.js'])); + * //=> [ 'a.js' ] + * ``` + * @param {Array} `list` A list of strings to match + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Array} Returns an array of matches + * @summary false + * @api public + */ + +function nanomatch(list, patterns, options) { + patterns = utils.arrayify(patterns); + list = utils.arrayify(list); + + var len = patterns.length; + if (list.length === 0 || len === 0) { + return []; } - if (pending === 0) { - callbackAsync(callback, null, result) - return + if (len === 1) { + return nanomatch.match(list, patterns[0], options); } - var done = function() { - pending-- - if (pending === 0) { - callback(null, result) + var negated = false; + var omit = []; + var keep = []; + var idx = -1; + + while (++idx < len) { + var pattern = patterns[idx]; + + if (typeof pattern === 'string' && pattern.charCodeAt(0) === 33 /* ! */) { + omit.push.apply(omit, nanomatch.match(list, pattern.slice(1), options)); + negated = true; + } else { + keep.push.apply(keep, nanomatch.match(list, pattern, options)); } } - resolveSourcesHelper(map, mapUrl, options, function(fullUrl, sourceContent, index) { - result.sourcesResolved[index] = fullUrl - if (typeof sourceContent === "string") { - result.sourcesContent[index] = sourceContent - callbackAsync(done, null) + // minimatch.match parity + if (negated && keep.length === 0) { + if (options && options.unixify === false) { + keep = list.slice(); } else { - var readUrl = decodeUriComponent(fullUrl) - read(readUrl, function(error, source) { - result.sourcesContent[index] = error ? error : String(source) - done() - }) + var unixify = utils.unixify(options); + for (var i = 0; i < list.length; i++) { + keep.push(unixify(list[i])); + } } - }) -} - -function resolveSourcesSync(map, mapUrl, read, options) { - var result = { - sourcesResolved: [], - sourcesContent: [] } - if (!map.sources || map.sources.length === 0) { - return result + var matches = utils.diff(keep, omit); + if (!options || options.nodupes !== false) { + return utils.unique(matches); } - resolveSourcesHelper(map, mapUrl, options, function(fullUrl, sourceContent, index) { - result.sourcesResolved[index] = fullUrl - if (read !== null) { - if (typeof sourceContent === "string") { - result.sourcesContent[index] = sourceContent - } else { - var readUrl = decodeUriComponent(fullUrl) - try { - result.sourcesContent[index] = String(read(readUrl)) - } catch (error) { - result.sourcesContent[index] = error - } - } - } - }) - - return result + return matches; } -var endingSlash = /\/?$/ +/** + * Similar to the main function, but `pattern` must be a string. + * + * ```js + * var nm = require('nanomatch'); + * nm.match(list, pattern[, options]); + * + * console.log(nm.match(['a.a', 'a.aa', 'a.b', 'a.c'], '*.a')); + * //=> ['a.a', 'a.aa'] + * ``` + * @param {Array} `list` Array of strings to match + * @param {String} `pattern` Glob pattern to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Array} Returns an array of matches + * @api public + */ -function resolveSourcesHelper(map, mapUrl, options, fn) { - options = options || {} - mapUrl = urix(mapUrl) - var fullUrl - var sourceContent - var sourceRoot - for (var index = 0, len = map.sources.length; index < len; index++) { - sourceRoot = null - if (typeof options.sourceRoot === "string") { - sourceRoot = options.sourceRoot - } else if (typeof map.sourceRoot === "string" && options.sourceRoot !== false) { - sourceRoot = map.sourceRoot - } - // If the sourceRoot is the empty string, it is equivalent to not setting - // the property at all. - if (sourceRoot === null || sourceRoot === '') { - fullUrl = resolveUrl(mapUrl, map.sources[index]) - } else { - // Make sure that the sourceRoot ends with a slash, so that `/scripts/subdir` becomes - // `/scripts/subdir/`, not `/scripts/`. Pointing to a file as source root - // does not make sense. - fullUrl = resolveUrl(mapUrl, sourceRoot.replace(endingSlash, "/"), map.sources[index]) - } - sourceContent = (map.sourcesContent || [])[index] - fn(fullUrl, sourceContent, index) +nanomatch.match = function(list, pattern, options) { + if (Array.isArray(pattern)) { + throw new TypeError('expected pattern to be a string'); } -} + var unixify = utils.unixify(options); + var isMatch = memoize('match', pattern, options, nanomatch.matcher); + var matches = []; + list = utils.arrayify(list); + var len = list.length; + var idx = -1; -function resolve(code, codeUrl, read, options, callback) { - if (typeof options === "function") { - callback = options - options = {} - } - if (code === null) { - var mapUrl = codeUrl - var data = { - sourceMappingURL: null, - url: mapUrl, - sourcesRelativeTo: mapUrl, - map: null + while (++idx < len) { + var ele = list[idx]; + if (ele === pattern || isMatch(ele)) { + matches.push(utils.value(ele, unixify, options)); } - var readUrl = decodeUriComponent(mapUrl) - read(readUrl, function(error, result) { - if (error) { - error.sourceMapData = data - return callback(error) - } - data.map = String(result) - try { - data.map = parseMapToJSON(data.map, data) - } catch (error) { - return callback(error) - } - _resolveSources(data) - }) - } else { - resolveSourceMap(code, codeUrl, read, function(error, mapData) { - if (error) { - return callback(error) - } - if (!mapData) { - return callback(null, null) - } - _resolveSources(mapData) - }) } - function _resolveSources(mapData) { - resolveSources(mapData.map, mapData.sourcesRelativeTo, read, options, function(error, result) { - if (error) { - return callback(error) - } - mapData.sourcesResolved = result.sourcesResolved - mapData.sourcesContent = result.sourcesContent - callback(null, mapData) - }) + // if no options were passed, uniquify results and return + if (typeof options === 'undefined') { + return utils.unique(matches); } -} -function resolveSync(code, codeUrl, read, options) { - var mapData - if (code === null) { - var mapUrl = codeUrl - mapData = { - sourceMappingURL: null, - url: mapUrl, - sourcesRelativeTo: mapUrl, - map: null + if (matches.length === 0) { + if (options.failglob === true) { + throw new Error('no matches found for "' + pattern + '"'); } - mapData.map = readSync(read, mapUrl, mapData) - mapData.map = parseMapToJSON(mapData.map, mapData) - } else { - mapData = resolveSourceMapSync(code, codeUrl, read) - if (!mapData) { - return null + if (options.nonull === true || options.nullglob === true) { + return [options.unescape ? utils.unescape(pattern) : pattern]; } } - var result = resolveSourcesSync(mapData.map, mapData.sourcesRelativeTo, read, options) - mapData.sourcesResolved = result.sourcesResolved - mapData.sourcesContent = result.sourcesContent - return mapData -} - - - -module.exports = { - resolveSourceMap: resolveSourceMap, - resolveSourceMapSync: resolveSourceMapSync, - resolveSources: resolveSources, - resolveSourcesSync: resolveSourcesSync, - resolve: resolve, - resolveSync: resolveSync, - parseMapToJSON: parseMapToJSON -} - - -/***/ }), -/* 675 */ -/***/ (function(module, exports, __webpack_require__) { -var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_RESULT__;// Copyright 2014 Simon Lydell -// X11 (“MIT”) Licensed. (See LICENSE.) + // if `opts.ignore` was defined, diff ignored list + if (options.ignore) { + matches = nanomatch.not(matches, options.ignore, options); + } -void (function(root, factory) { - if (true) { - !(__WEBPACK_AMD_DEFINE_FACTORY__ = (factory), - __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? - (__WEBPACK_AMD_DEFINE_FACTORY__.call(exports, __webpack_require__, exports, module)) : - __WEBPACK_AMD_DEFINE_FACTORY__), - __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)) - } else {} -}(this, function() { + return options.nodupes !== false ? utils.unique(matches) : matches; +}; - var innerRegex = /[#@] sourceMappingURL=([^\s'"]*)/ +/** + * Returns true if the specified `string` matches the given glob `pattern`. + * + * ```js + * var nm = require('nanomatch'); + * nm.isMatch(string, pattern[, options]); + * + * console.log(nm.isMatch('a.a', '*.a')); + * //=> true + * console.log(nm.isMatch('a.b', '*.a')); + * //=> false + * ``` + * @param {String} `string` String to match + * @param {String} `pattern` Glob pattern to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Boolean} Returns true if the string matches the glob pattern. + * @api public + */ - var regex = RegExp( - "(?:" + - "/\\*" + - "(?:\\s*\r?\n(?://)?)?" + - "(?:" + innerRegex.source + ")" + - "\\s*" + - "\\*/" + - "|" + - "//(?:" + innerRegex.source + ")" + - ")" + - "\\s*" - ) +nanomatch.isMatch = function(str, pattern, options) { + if (typeof str !== 'string') { + throw new TypeError('expected a string: "' + util.inspect(str) + '"'); + } - return { + if (utils.isEmptyString(str) || utils.isEmptyString(pattern)) { + return false; + } - regex: regex, - _innerRegex: innerRegex, + var equals = utils.equalsPattern(options); + if (equals(str)) { + return true; + } - getFrom: function(code) { - var match = code.match(regex) - return (match ? match[1] || match[2] || "" : null) - }, + var isMatch = memoize('isMatch', pattern, options, nanomatch.matcher); + return isMatch(str); +}; - existsIn: function(code) { - return regex.test(code) - }, +/** + * Returns true if some of the elements in the given `list` match any of the + * given glob `patterns`. + * + * ```js + * var nm = require('nanomatch'); + * nm.some(list, patterns[, options]); + * + * console.log(nm.some(['foo.js', 'bar.js'], ['*.js', '!foo.js'])); + * // true + * console.log(nm.some(['foo.js'], ['*.js', '!foo.js'])); + * // false + * ``` + * @param {String|Array} `list` The string or array of strings to test. Returns as soon as the first match is found. + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Boolean} Returns true if any patterns match `str` + * @api public + */ - removeFrom: function(code) { - return code.replace(regex, "") - }, +nanomatch.some = function(list, patterns, options) { + if (typeof list === 'string') { + list = [list]; + } - insertBefore: function(code, string) { - var match = code.match(regex) - if (match) { - return code.slice(0, match.index) + string + code.slice(match.index) - } else { - return code + string - } + for (var i = 0; i < list.length; i++) { + if (nanomatch(list[i], patterns, options).length === 1) { + return true; } } -})); - - -/***/ }), -/* 676 */ -/***/ (function(module, exports, __webpack_require__) { + return false; +}; -// Copyright 2014 Simon Lydell -// X11 (“MIT”) Licensed. (See LICENSE.) +/** + * Returns true if every element in the given `list` matches + * at least one of the given glob `patterns`. + * + * ```js + * var nm = require('nanomatch'); + * nm.every(list, patterns[, options]); + * + * console.log(nm.every('foo.js', ['foo.js'])); + * // true + * console.log(nm.every(['foo.js', 'bar.js'], ['*.js'])); + * // true + * console.log(nm.every(['foo.js', 'bar.js'], ['*.js', '!foo.js'])); + * // false + * console.log(nm.every(['foo.js'], ['*.js', '!foo.js'])); + * // false + * ``` + * @param {String|Array} `list` The string or array of strings to test. + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Boolean} Returns true if any patterns match `str` + * @api public + */ -var url = __webpack_require__(203) +nanomatch.every = function(list, patterns, options) { + if (typeof list === 'string') { + list = [list]; + } -function resolveUrl(/* ...urls */) { - return Array.prototype.reduce.call(arguments, function(resolved, nextUrl) { - return url.resolve(resolved, nextUrl) - }) -} + for (var i = 0; i < list.length; i++) { + if (nanomatch(list[i], patterns, options).length !== 1) { + return false; + } + } -module.exports = resolveUrl + return true; +}; +/** + * Returns true if **any** of the given glob `patterns` + * match the specified `string`. + * + * ```js + * var nm = require('nanomatch'); + * nm.any(string, patterns[, options]); + * + * console.log(nm.any('a.a', ['b.*', '*.a'])); + * //=> true + * console.log(nm.any('a.a', 'b.*')); + * //=> false + * ``` + * @param {String|Array} `str` The string to test. + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Boolean} Returns true if any patterns match `str` + * @api public + */ -/***/ }), -/* 677 */ -/***/ (function(module, exports, __webpack_require__) { +nanomatch.any = function(str, patterns, options) { + if (typeof str !== 'string') { + throw new TypeError('expected a string: "' + util.inspect(str) + '"'); + } -// Copyright 2017 Simon Lydell -// X11 (“MIT”) Licensed. (See LICENSE.) + if (utils.isEmptyString(str) || utils.isEmptyString(patterns)) { + return false; + } -var decodeUriComponent = __webpack_require__(678) + if (typeof patterns === 'string') { + patterns = [patterns]; + } -function customDecodeUriComponent(string) { - // `decodeUriComponent` turns `+` into ` `, but that's not wanted. - return decodeUriComponent(string.replace(/\+/g, "%2B")) -} + for (var i = 0; i < patterns.length; i++) { + if (nanomatch.isMatch(str, patterns[i], options)) { + return true; + } + } + return false; +}; -module.exports = customDecodeUriComponent +/** + * Returns true if **all** of the given `patterns` + * match the specified string. + * + * ```js + * var nm = require('nanomatch'); + * nm.all(string, patterns[, options]); + * + * console.log(nm.all('foo.js', ['foo.js'])); + * // true + * + * console.log(nm.all('foo.js', ['*.js', '!foo.js'])); + * // false + * + * console.log(nm.all('foo.js', ['*.js', 'foo.js'])); + * // true + * + * console.log(nm.all('foo.js', ['*.js', 'f*', '*o*', '*o.js'])); + * // true + * ``` + * @param {String|Array} `str` The string to test. + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Boolean} Returns true if any patterns match `str` + * @api public + */ +nanomatch.all = function(str, patterns, options) { + if (typeof str !== 'string') { + throw new TypeError('expected a string: "' + util.inspect(str) + '"'); + } -/***/ }), -/* 678 */ -/***/ (function(module, exports, __webpack_require__) { + if (typeof patterns === 'string') { + patterns = [patterns]; + } -"use strict"; + for (var i = 0; i < patterns.length; i++) { + if (!nanomatch.isMatch(str, patterns[i], options)) { + return false; + } + } + return true; +}; -var token = '%[a-f0-9]{2}'; -var singleMatcher = new RegExp(token, 'gi'); -var multiMatcher = new RegExp('(' + token + ')+', 'gi'); +/** + * Returns a list of strings that _**do not match any**_ of the given `patterns`. + * + * ```js + * var nm = require('nanomatch'); + * nm.not(list, patterns[, options]); + * + * console.log(nm.not(['a.a', 'b.b', 'c.c'], '*.a')); + * //=> ['b.b', 'c.c'] + * ``` + * @param {Array} `list` Array of strings to match. + * @param {String|Array} `patterns` One or more glob pattern to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Array} Returns an array of strings that **do not match** the given patterns. + * @api public + */ -function decodeComponents(components, split) { - try { - // Try to decode the entire string first - return decodeURIComponent(components.join('')); - } catch (err) { - // Do nothing - } +nanomatch.not = function(list, patterns, options) { + var opts = extend({}, options); + var ignore = opts.ignore; + delete opts.ignore; - if (components.length === 1) { - return components; - } + list = utils.arrayify(list); - split = split || 1; + var matches = utils.diff(list, nanomatch(list, patterns, opts)); + if (ignore) { + matches = utils.diff(matches, nanomatch(list, ignore)); + } - // Split the array in 2 parts - var left = components.slice(0, split); - var right = components.slice(split); + return opts.nodupes !== false ? utils.unique(matches) : matches; +}; - return Array.prototype.concat.call([], decodeComponents(left), decodeComponents(right)); -} +/** + * Returns true if the given `string` contains the given pattern. Similar + * to [.isMatch](#isMatch) but the pattern can match any part of the string. + * + * ```js + * var nm = require('nanomatch'); + * nm.contains(string, pattern[, options]); + * + * console.log(nm.contains('aa/bb/cc', '*b')); + * //=> true + * console.log(nm.contains('aa/bb/cc', '*d')); + * //=> false + * ``` + * @param {String} `str` The string to match. + * @param {String|Array} `patterns` Glob pattern to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Boolean} Returns true if the patter matches any part of `str`. + * @api public + */ -function decode(input) { - try { - return decodeURIComponent(input); - } catch (err) { - var tokens = input.match(singleMatcher); +nanomatch.contains = function(str, patterns, options) { + if (typeof str !== 'string') { + throw new TypeError('expected a string: "' + util.inspect(str) + '"'); + } - for (var i = 1; i < tokens.length; i++) { - input = decodeComponents(tokens, i).join(''); + if (typeof patterns === 'string') { + if (utils.isEmptyString(str) || utils.isEmptyString(patterns)) { + return false; + } - tokens = input.match(singleMatcher); - } + var equals = utils.equalsPattern(patterns, options); + if (equals(str)) { + return true; + } + var contains = utils.containsPattern(patterns, options); + if (contains(str)) { + return true; + } + } - return input; - } -} + var opts = extend({}, options, {contains: true}); + return nanomatch.any(str, patterns, opts); +}; -function customDecodeURIComponent(input) { - // Keep track of all the replacements and prefill the map with the `BOM` - var replaceMap = { - '%FE%FF': '\uFFFD\uFFFD', - '%FF%FE': '\uFFFD\uFFFD' - }; +/** + * Returns true if the given pattern and options should enable + * the `matchBase` option. + * @return {Boolean} + * @api private + */ - var match = multiMatcher.exec(input); - while (match) { - try { - // Decode as big chunks as possible - replaceMap[match[0]] = decodeURIComponent(match[0]); - } catch (err) { - var result = decode(match[0]); +nanomatch.matchBase = function(pattern, options) { + if (pattern && pattern.indexOf('/') !== -1 || !options) return false; + return options.basename === true || options.matchBase === true; +}; - if (result !== match[0]) { - replaceMap[match[0]] = result; - } - } +/** + * Filter the keys of the given object with the given `glob` pattern + * and `options`. Does not attempt to match nested keys. If you need this feature, + * use [glob-object][] instead. + * + * ```js + * var nm = require('nanomatch'); + * nm.matchKeys(object, patterns[, options]); + * + * var obj = { aa: 'a', ab: 'b', ac: 'c' }; + * console.log(nm.matchKeys(obj, '*b')); + * //=> { ab: 'b' } + * ``` + * @param {Object} `object` The object with keys to filter. + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Object} Returns an object with only keys that match the given patterns. + * @api public + */ - match = multiMatcher.exec(input); - } +nanomatch.matchKeys = function(obj, patterns, options) { + if (!utils.isObject(obj)) { + throw new TypeError('expected the first argument to be an object'); + } + var keys = nanomatch(Object.keys(obj), patterns, options); + return utils.pick(obj, keys); +}; - // Add `%C2` at the end of the map to make sure it does not replace the combinator before everything else - replaceMap['%C2'] = '\uFFFD'; +/** + * Returns a memoized matcher function from the given glob `pattern` and `options`. + * The returned function takes a string to match as its only argument and returns + * true if the string is a match. + * + * ```js + * var nm = require('nanomatch'); + * nm.matcher(pattern[, options]); + * + * var isMatch = nm.matcher('*.!(*a)'); + * console.log(isMatch('a.a')); + * //=> false + * console.log(isMatch('a.b')); + * //=> true + * ``` + * @param {String} `pattern` Glob pattern + * @param {Object} `options` See available [options](#options) for changing how matches are performed. + * @return {Function} Returns a matcher function. + * @api public + */ - var entries = Object.keys(replaceMap); +nanomatch.matcher = function matcher(pattern, options) { + if (utils.isEmptyString(pattern)) { + return function() { + return false; + }; + } - for (var i = 0; i < entries.length; i++) { - // Replace all decoded components - var key = entries[i]; - input = input.replace(new RegExp(key, 'g'), replaceMap[key]); - } + if (Array.isArray(pattern)) { + return compose(pattern, options, matcher); + } - return input; -} + // if pattern is a regex + if (pattern instanceof RegExp) { + return test(pattern); + } -module.exports = function (encodedURI) { - if (typeof encodedURI !== 'string') { - throw new TypeError('Expected `encodedURI` to be of type `string`, got `' + typeof encodedURI + '`'); - } + // if pattern is invalid + if (!utils.isString(pattern)) { + throw new TypeError('expected pattern to be an array, string or regex'); + } - try { - encodedURI = encodedURI.replace(/\+/g, ' '); + // if pattern is a non-glob string + if (!utils.hasSpecialChars(pattern)) { + if (options && options.nocase === true) { + pattern = pattern.toLowerCase(); + } + return utils.matchPath(pattern, options); + } - // Try the built in decoder first - return decodeURIComponent(encodedURI); - } catch (err) { - // Fallback to a more advanced decoder - return customDecodeURIComponent(encodedURI); - } -}; + // if pattern is a glob string + var re = nanomatch.makeRe(pattern, options); + // if `options.matchBase` or `options.basename` is defined + if (nanomatch.matchBase(pattern, options)) { + return utils.matchBasename(re, options); + } -/***/ }), -/* 679 */ -/***/ (function(module, exports, __webpack_require__) { + function test(regex) { + var equals = utils.equalsPattern(options); + var unixify = utils.unixify(options); -// Copyright 2014 Simon Lydell -// X11 (“MIT”) Licensed. (See LICENSE.) - -var path = __webpack_require__(4) - -"use strict" - -function urix(aPath) { - if (path.sep === "\\") { - return aPath - .replace(/\\/g, "/") - .replace(/^[a-z]:\/?/i, "/") - } - return aPath -} - -module.exports = urix + return function(str) { + if (equals(str)) { + return true; + } + if (regex.test(unixify(str))) { + return true; + } + return false; + }; + } -/***/ }), -/* 680 */ -/***/ (function(module, exports, __webpack_require__) { + // create matcher function + var matcherFn = test(re); + // set result object from compiler on matcher function, + // as a non-enumerable property. useful for debugging + utils.define(matcherFn, 'result', re.result); + return matcherFn; +}; -"use strict"; +/** + * Returns an array of matches captured by `pattern` in `string, or + * `null` if the pattern did not match. + * + * ```js + * var nm = require('nanomatch'); + * nm.capture(pattern, string[, options]); + * + * console.log(nm.capture('test/*.js', 'test/foo.js')); + * //=> ['foo'] + * console.log(nm.capture('test/*.js', 'foo/bar.css')); + * //=> null + * ``` + * @param {String} `pattern` Glob pattern to use for matching. + * @param {String} `string` String to match + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Boolean} Returns an array of captures if the string matches the glob pattern, otherwise `null`. + * @api public + */ +nanomatch.capture = function(pattern, str, options) { + var re = nanomatch.makeRe(pattern, extend({capture: true}, options)); + var unixify = utils.unixify(options); -function atob(str) { - return Buffer.from(str, 'base64').toString('binary'); -} + function match() { + return function(string) { + var match = re.exec(unixify(string)); + if (!match) { + return null; + } -module.exports = atob.atob = atob; + return match.slice(1); + }; + } + var capture = memoize('capture', pattern, options, match); + return capture(str); +}; -/***/ }), -/* 681 */ -/***/ (function(module, exports, __webpack_require__) { +/** + * Create a regular expression from the given glob `pattern`. + * + * ```js + * var nm = require('nanomatch'); + * nm.makeRe(pattern[, options]); + * + * console.log(nm.makeRe('*.js')); + * //=> /^(?:(\.[\\\/])?(?!\.)(?=.)[^\/]*?\.js)$/ + * ``` + * @param {String} `pattern` A glob pattern to convert to regex. + * @param {Object} `options` See available [options](#options) for changing how matches are performed. + * @return {RegExp} Returns a regex created from the given pattern. + * @api public + */ -"use strict"; +nanomatch.makeRe = function(pattern, options) { + if (pattern instanceof RegExp) { + return pattern; + } + if (typeof pattern !== 'string') { + throw new TypeError('expected pattern to be a string'); + } -var fs = __webpack_require__(132); -var path = __webpack_require__(4); -var define = __webpack_require__(648); -var utils = __webpack_require__(662); + if (pattern.length > MAX_LENGTH) { + throw new Error('expected pattern to be less than ' + MAX_LENGTH + ' characters'); + } -/** - * Expose `mixin()`. - * This code is based on `source-maps-support.js` in reworkcss/css - * https://github.com/reworkcss/css/blob/master/lib/stringify/source-map-support.js - * Copyright (c) 2012 TJ Holowaychuk - */ + function makeRe() { + var opts = utils.extend({wrap: false}, options); + var result = nanomatch.create(pattern, opts); + var regex = toRegex(result.output, opts); + utils.define(regex, 'result', result); + return regex; + } -module.exports = mixin; + return memoize('makeRe', pattern, options, makeRe); +}; /** - * Mixin source map support into `compiler`. + * Parses the given glob `pattern` and returns an object with the compiled `output` + * and optional source `map`. * - * @param {Object} `compiler` + * ```js + * var nm = require('nanomatch'); + * nm.create(pattern[, options]); + * + * console.log(nm.create('abc/*.js')); + * // { options: { source: 'string', sourcemap: true }, + * // state: {}, + * // compilers: + * // { ... }, + * // output: '(\\.[\\\\\\/])?abc\\/(?!\\.)(?=.)[^\\/]*?\\.js', + * // ast: + * // { type: 'root', + * // errors: [], + * // nodes: + * // [ ... ], + * // dot: false, + * // input: 'abc/*.js' }, + * // parsingErrors: [], + * // map: + * // { version: 3, + * // sources: [ 'string' ], + * // names: [], + * // mappings: 'AAAA,GAAG,EAAC,kBAAC,EAAC,EAAE', + * // sourcesContent: [ 'abc/*.js' ] }, + * // position: { line: 1, column: 28 }, + * // content: {}, + * // files: {}, + * // idx: 6 } + * ``` + * @param {String} `pattern` Glob pattern to parse and compile. + * @param {Object} `options` Any [options](#options) to change how parsing and compiling is performed. + * @return {Object} Returns an object with the parsed AST, compiled string and optional source map. * @api public */ -function mixin(compiler) { - define(compiler, '_comment', compiler.comment); - compiler.map = new utils.SourceMap.SourceMapGenerator(); - compiler.position = { line: 1, column: 1 }; - compiler.content = {}; - compiler.files = {}; - - for (var key in exports) { - define(compiler, key, exports[key]); +nanomatch.create = function(pattern, options) { + if (typeof pattern !== 'string') { + throw new TypeError('expected a string'); } -} - -/** - * Update position. - * - * @param {String} str - */ - -exports.updatePosition = function(str) { - var lines = str.match(/\n/g); - if (lines) this.position.line += lines.length; - var i = str.lastIndexOf('\n'); - this.position.column = ~i ? str.length - i : this.position.column + str.length; + function create() { + return nanomatch.compile(nanomatch.parse(pattern, options), options); + } + return memoize('create', pattern, options, create); }; /** - * Emit `str` with `position`. + * Parse the given `str` with the given `options`. * - * @param {String} str - * @param {Object} [pos] - * @return {String} + * ```js + * var nm = require('nanomatch'); + * nm.parse(pattern[, options]); + * + * var ast = nm.parse('a/{b,c}/d'); + * console.log(ast); + * // { type: 'root', + * // errors: [], + * // input: 'a/{b,c}/d', + * // nodes: + * // [ { type: 'bos', val: '' }, + * // { type: 'text', val: 'a/' }, + * // { type: 'brace', + * // nodes: + * // [ { type: 'brace.open', val: '{' }, + * // { type: 'text', val: 'b,c' }, + * // { type: 'brace.close', val: '}' } ] }, + * // { type: 'text', val: '/d' }, + * // { type: 'eos', val: '' } ] } + * ``` + * @param {String} `str` + * @param {Object} `options` + * @return {Object} Returns an AST + * @api public */ -exports.emit = function(str, node) { - var position = node.position || {}; - var source = position.source; - if (source) { - if (position.filepath) { - source = utils.unixify(position.filepath); - } - - this.map.addMapping({ - source: source, - generated: { - line: this.position.line, - column: Math.max(this.position.column - 1, 0) - }, - original: { - line: position.start.line, - column: position.start.column - 1 - } - }); +nanomatch.parse = function(pattern, options) { + if (typeof pattern !== 'string') { + throw new TypeError('expected a string'); + } - if (position.content) { - this.addContent(source, position); - } - if (position.filepath) { - this.addFile(source, position); - } + function parse() { + var snapdragon = utils.instantiate(null, options); + parsers(snapdragon, options); - this.updatePosition(str); - this.output += str; + var ast = snapdragon.parse(pattern, options); + utils.define(ast, 'snapdragon', snapdragon); + ast.input = pattern; + return ast; } - return str; + + return memoize('parse', pattern, options, parse); }; /** - * Adds a file to the source map output if it has not already been added - * @param {String} `file` - * @param {Object} `pos` + * Compile the given `ast` or string with the given `options`. + * + * ```js + * var nm = require('nanomatch'); + * nm.compile(ast[, options]); + * + * var ast = nm.parse('a/{b,c}/d'); + * console.log(nm.compile(ast)); + * // { options: { source: 'string' }, + * // state: {}, + * // compilers: + * // { eos: [Function], + * // noop: [Function], + * // bos: [Function], + * // brace: [Function], + * // 'brace.open': [Function], + * // text: [Function], + * // 'brace.close': [Function] }, + * // output: [ 'a/(b|c)/d' ], + * // ast: + * // { ... }, + * // parsingErrors: [] } + * ``` + * @param {Object|String} `ast` + * @param {Object} `options` + * @return {Object} Returns an object that has an `output` property with the compiled string. + * @api public */ -exports.addFile = function(file, position) { - if (typeof position.content !== 'string') return; - if (Object.prototype.hasOwnProperty.call(this.files, file)) return; - this.files[file] = position.content; +nanomatch.compile = function(ast, options) { + if (typeof ast === 'string') { + ast = nanomatch.parse(ast, options); + } + + function compile() { + var snapdragon = utils.instantiate(ast, options); + compilers(snapdragon, options); + return snapdragon.compile(ast, options); + } + + return memoize('compile', ast.input, options, compile); }; /** - * Adds a content source to the source map output if it has not already been added - * @param {String} `source` - * @param {Object} `position` + * Clear the regex cache. + * + * ```js + * nm.clearCache(); + * ``` + * @api public */ -exports.addContent = function(source, position) { - if (typeof position.content !== 'string') return; - if (Object.prototype.hasOwnProperty.call(this.content, source)) return; - this.map.setSourceContent(source, position.content); +nanomatch.clearCache = function() { + nanomatch.cache.__data__ = {}; }; /** - * Applies any original source maps to the output and embeds the source file - * contents in the source map. + * Compose a matcher function with the given patterns. + * This allows matcher functions to be compiled once and + * called multiple times. */ -exports.applySourceMaps = function() { - Object.keys(this.files).forEach(function(file) { - var content = this.files[file]; - this.map.setSourceContent(file, content); +function compose(patterns, options, matcher) { + var matchers; - if (this.options.inputSourcemaps === true) { - var originalMap = utils.sourceMapResolve.resolveSync(content, file, fs.readFileSync); - if (originalMap) { - var map = new utils.SourceMap.SourceMapConsumer(originalMap.map); - var relativeTo = originalMap.sourcesRelativeTo; - this.map.applySourceMap(map, file, utils.unixify(path.dirname(relativeTo))); + return memoize('compose', String(patterns), options, function() { + return function(file) { + // delay composition until it's invoked the first time, + // after that it won't be called again + if (!matchers) { + matchers = []; + for (var i = 0; i < patterns.length; i++) { + matchers.push(matcher(patterns[i], options)); + } } - } - }, this); -}; + + var len = matchers.length; + while (len--) { + if (matchers[len](file) === true) { + return true; + } + } + return false; + }; + }); +} /** - * Process comments, drops sourceMap comments. - * @param {Object} node + * Memoize a generated regex or function. A unique key is generated + * from the `type` (usually method name), the `pattern`, and + * user-defined options. */ -exports.comment = function(node) { - if (/^# sourceMappingURL=/.test(node.comment)) { - return this.emit('', node.position); - } - return this._comment(node); -}; - - -/***/ }), -/* 682 */ -/***/ (function(module, exports, __webpack_require__) { +function memoize(type, pattern, options, fn) { + var key = utils.createKey(type + '=' + pattern, options); -"use strict"; + if (options && options.cache === false) { + return fn(pattern, options); + } + if (cache.has(type, key)) { + return cache.get(type, key); + } -var use = __webpack_require__(660); -var util = __webpack_require__(113); -var Cache = __webpack_require__(683); -var define = __webpack_require__(648); -var debug = __webpack_require__(205)('snapdragon:parser'); -var Position = __webpack_require__(684); -var utils = __webpack_require__(662); + var val = fn(pattern, options); + cache.set(type, key, val); + return val; +} /** - * Create a new `Parser` with the given `input` and `options`. - * @param {String} `input` - * @param {Object} `options` - * @api public + * Expose compiler, parser and cache on `nanomatch` */ -function Parser(options) { - debug('initializing', __filename); - this.options = utils.extend({source: 'string'}, options); - this.init(this.options); - use(this); -} +nanomatch.compilers = compilers; +nanomatch.parsers = parsers; +nanomatch.cache = cache; /** - * Prototype methods + * Expose `nanomatch` + * @type {Function} */ -Parser.prototype = { - constructor: Parser, - - init: function(options) { - this.orig = ''; - this.input = ''; - this.parsed = ''; - - this.column = 1; - this.line = 1; - - this.regex = new Cache(); - this.errors = this.errors || []; - this.parsers = this.parsers || {}; - this.types = this.types || []; - this.sets = this.sets || {}; - this.fns = this.fns || []; - this.currentType = 'root'; - - var pos = this.position(); - this.bos = pos({type: 'bos', val: ''}); +module.exports = nanomatch; - this.ast = { - type: 'root', - errors: this.errors, - nodes: [this.bos] - }; - define(this.bos, 'parent', this.ast); - this.nodes = [this.ast]; +/***/ }), +/* 695 */ +/***/ (function(module, exports, __webpack_require__) { - this.count = 0; - this.setCount = 0; - this.stack = []; - }, +"use strict"; - /** - * Throw a formatted error with the cursor column and `msg`. - * @param {String} `msg` Message to use in the Error. - */ - error: function(msg, node) { - var pos = node.position || {start: {column: 0, line: 0}}; - var line = pos.start.line; - var column = pos.start.column; - var source = this.options.source; +var isExtendable = __webpack_require__(696); +var assignSymbols = __webpack_require__(595); - var message = source + ' : ' + msg; - var err = new Error(message); - err.source = source; - err.reason = msg; - err.pos = pos; +module.exports = Object.assign || function(obj/*, objects*/) { + if (obj === null || typeof obj === 'undefined') { + throw new TypeError('Cannot convert undefined or null to object'); + } + if (!isObject(obj)) { + obj = {}; + } + for (var i = 1; i < arguments.length; i++) { + var val = arguments[i]; + if (isString(val)) { + val = toObject(val); + } + if (isObject(val)) { + assign(obj, val); + assignSymbols(obj, val); + } + } + return obj; +}; - if (this.options.silent) { - this.errors.push(err); - } else { - throw err; +function assign(a, b) { + for (var key in b) { + if (hasOwn(b, key)) { + a[key] = b[key]; } - }, + } +} - /** - * Define a non-enumberable property on the `Parser` instance. - * - * ```js - * parser.define('foo', 'bar'); - * ``` - * @name .define - * @param {String} `key` propery name - * @param {any} `val` property value - * @return {Object} Returns the Parser instance for chaining. - * @api public - */ +function isString(val) { + return (val && typeof val === 'string'); +} - define: function(key, val) { - define(this, key, val); - return this; - }, +function toObject(str) { + var obj = {}; + for (var i in str) { + obj[i] = str[i]; + } + return obj; +} - /** - * Mark position and patch `node.position`. - */ +function isObject(val) { + return (val && typeof val === 'object') || isExtendable(val); +} - position: function() { - var start = { line: this.line, column: this.column }; - var self = this; +/** + * Returns true if the given `key` is an own property of `obj`. + */ - return function(node) { - define(node, 'position', new Position(start, self)); - return node; - }; - }, +function hasOwn(obj, key) { + return Object.prototype.hasOwnProperty.call(obj, key); +} - /** - * Set parser `name` with the given `fn` - * @param {String} `name` - * @param {Function} `fn` - * @api public - */ +function isEnum(obj, key) { + return Object.prototype.propertyIsEnumerable.call(obj, key); +} - set: function(type, fn) { - if (this.types.indexOf(type) === -1) { - this.types.push(type); - } - this.parsers[type] = fn.bind(this); - return this; - }, - /** - * Get parser `name` - * @param {String} `name` - * @api public - */ +/***/ }), +/* 696 */ +/***/ (function(module, exports, __webpack_require__) { - get: function(name) { - return this.parsers[name]; - }, +"use strict"; +/*! + * is-extendable + * + * Copyright (c) 2015-2017, Jon Schlinkert. + * Released under the MIT License. + */ - /** - * Push a `token` onto the `type` stack. - * - * @param {String} `type` - * @return {Object} `token` - * @api public - */ - push: function(type, token) { - this.sets[type] = this.sets[type] || []; - this.count++; - this.stack.push(token); - return this.sets[type].push(token); - }, - /** - * Pop a token off of the `type` stack - * @param {String} `type` - * @returns {Object} Returns a token - * @api public - */ +var isPlainObject = __webpack_require__(594); - pop: function(type) { - this.sets[type] = this.sets[type] || []; - this.count--; - this.stack.pop(); - return this.sets[type].pop(); - }, +module.exports = function isExtendable(val) { + return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); +}; - /** - * Return true if inside a `stack` node. Types are `braces`, `parens` or `brackets`. - * - * @param {String} `type` - * @return {Boolean} - * @api public - */ - isInside: function(type) { - this.sets[type] = this.sets[type] || []; - return this.sets[type].length > 0; - }, +/***/ }), +/* 697 */ +/***/ (function(module, exports, __webpack_require__) { - /** - * Return true if `node` is the given `type`. - * - * ```js - * parser.isType(node, 'brace'); - * ``` - * @param {Object} `node` - * @param {String} `type` - * @return {Boolean} - * @api public - */ +"use strict"; - isType: function(node, type) { - return node && node.type === type; - }, - /** - * Get the previous AST node - * @return {Object} - */ +/** +* Nanomatch compilers +*/ - prev: function(n) { - return this.stack.length > 0 - ? utils.last(this.stack, n) - : utils.last(this.nodes, n); - }, +module.exports = function(nanomatch, options) { + function slash() { + if (options && typeof options.slash === 'string') { + return options.slash; + } + if (options && typeof options.slash === 'function') { + return options.slash.call(nanomatch); + } + return '\\\\/'; + } - /** - * Update line and column based on `str`. - */ + function star() { + if (options && typeof options.star === 'string') { + return options.star; + } + if (options && typeof options.star === 'function') { + return options.star.call(nanomatch); + } + return '[^' + slash() + ']*?'; + } - consume: function(len) { - this.input = this.input.substr(len); - }, + var ast = nanomatch.ast = nanomatch.parser.ast; + ast.state = nanomatch.parser.state; + nanomatch.compiler.state = ast.state; + nanomatch.compiler - /** - * Update column based on `str`. - */ + /** + * Negation / escaping + */ - updatePosition: function(str, len) { - var lines = str.match(/\n/g); - if (lines) this.line += lines.length; - var i = str.lastIndexOf('\n'); - this.column = ~i ? len - i : this.column + len; - this.parsed += str; - this.consume(len); - }, + .set('not', function(node) { + var prev = this.prev(); + if (this.options.nonegate === true || prev.type !== 'bos') { + return this.emit('\\' + node.val, node); + } + return this.emit(node.val, node); + }) + .set('escape', function(node) { + if (this.options.unescape && /^[-\w_.]/.test(node.val)) { + return this.emit(node.val, node); + } + return this.emit('\\' + node.val, node); + }) + .set('quoted', function(node) { + return this.emit(node.val, node); + }) - /** - * Match `regex`, return captures, and update the cursor position by `match[0]` length. - * @param {RegExp} `regex` - * @return {Object} - */ + /** + * Regex + */ - match: function(regex) { - var m = regex.exec(this.input); - if (m) { - this.updatePosition(m[0], m[0].length); - return m; - } - }, + .set('dollar', function(node) { + if (node.parent.type === 'bracket') { + return this.emit(node.val, node); + } + return this.emit('\\' + node.val, node); + }) - /** - * Capture `type` with the given regex. - * @param {String} `type` - * @param {RegExp} `regex` - * @return {Function} - */ + /** + * Dot: "." + */ - capture: function(type, regex) { - if (typeof regex === 'function') { - return this.set.apply(this, arguments); - } + .set('dot', function(node) { + if (node.dotfiles === true) this.dotfiles = true; + return this.emit('\\' + node.val, node); + }) - this.regex.set(type, regex); - this.set(type, function() { - var parsed = this.parsed; - var pos = this.position(); - var m = this.match(regex); - if (!m || !m[0]) return; + /** + * Slashes: "/" and "\" + */ + .set('backslash', function(node) { + return this.emit(node.val, node); + }) + .set('slash', function(node, nodes, i) { + var val = '[' + slash() + ']'; + var parent = node.parent; var prev = this.prev(); - var node = pos({ - type: type, - val: m[0], - parsed: parsed, - rest: this.input - }); - if (m[1]) { - node.inner = m[1]; + // set "node.hasSlash" to true on all ancestor parens nodes + while (parent.type === 'paren' && !parent.hasSlash) { + parent.hasSlash = true; + parent = parent.parent; } - define(node, 'inside', this.stack.length > 0); - define(node, 'parent', prev); - prev.nodes.push(node); - }.bind(this)); - return this; - }, + if (prev.addQmark) { + val += '?'; + } - /** - * Create a parser with open and close for parens, - * brackets or braces - */ + // word boundary + if (node.rest.slice(0, 2) === '\\b') { + return this.emit(val, node); + } - capturePair: function(type, openRegex, closeRegex, fn) { - this.sets[type] = this.sets[type] || []; + // globstars + if (node.parsed === '**' || node.parsed === './**') { + this.output = '(?:' + this.output; + return this.emit(val + ')?', node); + } + + // negation + if (node.parsed === '!**' && this.options.nonegate !== true) { + return this.emit(val + '?\\b', node); + } + return this.emit(val, node); + }) /** - * Open + * Square brackets */ - this.set(type + '.open', function() { - var parsed = this.parsed; - var pos = this.position(); - var m = this.match(openRegex); - if (!m || !m[0]) return; - - var val = m[0]; - this.setCount++; - this.specialChars = true; - var open = pos({ - type: type + '.open', - val: val, - rest: this.input - }); + .set('bracket', function(node) { + var close = node.close; + var open = !node.escaped ? '[' : '\\['; + var negated = node.negated; + var inner = node.inner; + var val = node.val; - if (typeof m[1] !== 'undefined') { - open.inner = m[1]; + if (node.escaped === true) { + inner = inner.replace(/\\?(\W)/g, '\\$1'); + negated = ''; } - var prev = this.prev(); - var node = pos({ - type: type, - nodes: [open] - }); - - define(node, 'rest', this.input); - define(node, 'parsed', parsed); - define(node, 'prefix', m[1]); - define(node, 'parent', prev); - define(open, 'parent', node); + if (inner === ']-') { + inner = '\\]\\-'; + } - if (typeof fn === 'function') { - fn.call(this, open, node); + if (negated && inner.indexOf('.') === -1) { + inner += '.'; + } + if (negated && inner.indexOf('/') === -1) { + inner += '/'; } - this.push(type, node); - prev.nodes.push(node); - }); + val = open + negated + inner + close; + return this.emit(val, node); + }) /** - * Close + * Square: "[.]" (only matches a single character in brackets) */ - this.set(type + '.close', function() { - var pos = this.position(); - var m = this.match(closeRegex); - if (!m || !m[0]) return; - - var parent = this.pop(type); - var node = pos({ - type: type + '.close', - rest: this.input, - suffix: m[1], - val: m[0] - }); + .set('square', function(node) { + var val = (/^\W/.test(node.val) ? '\\' : '') + node.val; + return this.emit(val, node); + }) - if (!this.isType(parent, type)) { - if (this.options.strict) { - throw new Error('missing opening "' + type + '"'); - } + /** + * Question mark: "?" + */ - this.setCount--; - node.escaped = true; - return node; + .set('qmark', function(node) { + var prev = this.prev(); + // don't use "slash" variable so that we always avoid + // matching backslashes and slashes with a qmark + var val = '[^.\\\\/]'; + if (this.options.dot || (prev.type !== 'bos' && prev.type !== 'slash')) { + val = '[^\\\\/]'; } - if (node.suffix === '\\') { - parent.escaped = true; - node.escaped = true; + if (node.parsed.slice(-1) === '(') { + var ch = node.rest.charAt(0); + if (ch === '!' || ch === '=' || ch === ':') { + return this.emit(node.val, node); + } } - parent.nodes.push(node); - define(node, 'parent', parent); - }); + if (node.val.length > 1) { + val += '{' + node.val.length + '}'; + } + return this.emit(val, node); + }) - return this; - }, + /** + * Plus + */ - /** - * Capture end-of-string - */ + .set('plus', function(node) { + var prev = node.parsed.slice(-1); + if (prev === ']' || prev === ')') { + return this.emit(node.val, node); + } + if (!this.output || (/[?*+]/.test(ch) && node.parent.type !== 'bracket')) { + return this.emit('\\+', node); + } + var ch = this.output.slice(-1); + if (/\w/.test(ch) && !node.inside) { + return this.emit('+\\+?', node); + } + return this.emit('+', node); + }) - eos: function() { - var pos = this.position(); - if (this.input) return; - var prev = this.prev(); + /** + * globstar: '**' + */ - while (prev.type !== 'root' && !prev.visited) { - if (this.options.strict === true) { - throw new SyntaxError('invalid syntax:' + util.inspect(prev, null, 2)); + .set('globstar', function(node, nodes, i) { + if (!this.output) { + this.state.leadingGlobstar = true; } - if (!hasDelims(prev)) { - prev.parent.escaped = true; - prev.escaped = true; - } + var prev = this.prev(); + var before = this.prev(2); + var next = this.next(); + var after = this.next(2); + var type = prev.type; + var val = node.val; - visit(prev, function(node) { - if (!hasDelims(node.parent)) { - node.parent.escaped = true; - node.escaped = true; - } - }); + if (prev.type === 'slash' && next.type === 'slash') { + if (before.type === 'text') { + this.output += '?'; - prev = prev.parent; - } + if (after.type !== 'text') { + this.output += '\\b'; + } + } + } - var tok = pos({ - type: 'eos', - val: this.append || '' - }); + var parsed = node.parsed; + if (parsed.charAt(0) === '!') { + parsed = parsed.slice(1); + } - define(tok, 'parent', this.ast); - return tok; - }, + var isInside = node.isInside.paren || node.isInside.brace; + if (parsed && type !== 'slash' && type !== 'bos' && !isInside) { + val = star(); + } else { + val = this.options.dot !== true + ? '(?:(?!(?:[' + slash() + ']|^)\\.).)*?' + : '(?:(?!(?:[' + slash() + ']|^)(?:\\.{1,2})($|[' + slash() + ']))(?!\\.{2}).)*?'; + } - /** - * Run parsers to advance the cursor position - */ + if ((type === 'slash' || type === 'bos') && this.options.dot !== true) { + val = '(?!\\.)' + val; + } - next: function() { - var parsed = this.parsed; - var len = this.types.length; - var idx = -1; - var tok; + if (prev.type === 'slash' && next.type === 'slash' && before.type !== 'text') { + if (after.type === 'text' || after.type === 'star') { + node.addQmark = true; + } + } - while (++idx < len) { - if ((tok = this.parsers[this.types[idx]].call(this))) { - define(tok, 'rest', this.input); - define(tok, 'parsed', parsed); - this.last = tok; - return tok; + if (this.options.capture) { + val = '(' + val + ')'; } - } - }, - /** - * Parse the given string. - * @return {Array} - */ + return this.emit(val, node); + }) - parse: function(input) { - if (typeof input !== 'string') { - throw new TypeError('expected a string'); - } + /** + * Star: "*" + */ - this.init(this.options); - this.orig = input; - this.input = input; - var self = this; + .set('star', function(node, nodes, i) { + var prior = nodes[i - 2] || {}; + var prev = this.prev(); + var next = this.next(); + var type = prev.type; - function parse() { - // check input before calling `.next()` - input = self.input; + function isStart(n) { + return n.type === 'bos' || n.type === 'slash'; + } - // get the next AST ndoe - var node = self.next(); - if (node) { - var prev = self.prev(); - if (prev) { - define(node, 'parent', prev); - if (prev.nodes) { - prev.nodes.push(node); - } - } + if (this.output === '' && this.options.contains !== true) { + this.output = '(?![' + slash() + '])'; + } - if (self.sets.hasOwnProperty(prev.type)) { - self.currentType = prev.type; + if (type === 'bracket' && this.options.bash === false) { + var str = next && next.type === 'bracket' ? star() : '*?'; + if (!prev.nodes || prev.nodes[1].type !== 'posix') { + return this.emit(str, node); } } - // if we got here but input is not changed, throw an error - if (self.input && input === self.input) { - throw new Error('no parsers registered for: "' + self.input.slice(0, 5) + '"'); + var prefix = !this.dotfiles && type !== 'text' && type !== 'escape' + ? (this.options.dot ? '(?!(?:^|[' + slash() + '])\\.{1,2}(?:$|[' + slash() + ']))' : '(?!\\.)') + : ''; + + if (isStart(prev) || (isStart(prior) && type === 'not')) { + if (prefix !== '(?!\\.)') { + prefix += '(?!(\\.{2}|\\.[' + slash() + ']))(?=.)'; + } else { + prefix += '(?=.)'; + } + } else if (prefix === '(?!\\.)') { + prefix = ''; } - } - while (this.input) parse(); - if (this.stack.length && this.options.strict) { - var node = this.stack.pop(); - throw this.error('missing opening ' + node.type + ': "' + this.orig + '"'); - } + if (prev.type === 'not' && prior.type === 'bos' && this.options.dot === true) { + this.output = '(?!\\.)' + this.output; + } - var eos = this.eos(); - var tok = this.prev(); - if (tok.type !== 'eos') { - this.ast.nodes.push(eos); - } + var output = prefix + star(); + if (this.options.capture) { + output = '(' + output + ')'; + } - return this.ast; - } -}; + return this.emit(output, node); + }) -/** - * Visit `node` with the given `fn` - */ + /** + * Text + */ -function visit(node, fn) { - if (!node.visited) { - define(node, 'visited', true); - return node.nodes ? mapVisit(node.nodes, fn) : fn(node); - } - return node; -} + .set('text', function(node) { + return this.emit(node.val, node); + }) -/** - * Map visit over array of `nodes`. - */ + /** + * End-of-string + */ -function mapVisit(nodes, fn) { - var len = nodes.length; - var idx = -1; - while (++idx < len) { - visit(nodes[idx], fn); - } -} + .set('eos', function(node) { + var prev = this.prev(); + var val = node.val; -function hasOpen(node) { - return node.nodes && node.nodes[0].type === (node.type + '.open'); -} + this.output = '(?:\\.[' + slash() + '](?=.))?' + this.output; + if (this.state.metachar && prev.type !== 'qmark' && prev.type !== 'slash') { + val += (this.options.contains ? '[' + slash() + ']?' : '(?:[' + slash() + ']|$)'); + } -function hasClose(node) { - return node.nodes && utils.last(node.nodes).type === (node.type + '.close'); -} + return this.emit(val, node); + }); -function hasDelims(node) { - return hasOpen(node) && hasClose(node); -} + /** + * Allow custom compilers to be passed on options + */ -/** - * Expose `Parser` - */ + if (options && typeof options.compilers === 'function') { + options.compilers(nanomatch.compiler); + } +}; -module.exports = Parser; /***/ }), -/* 683 */ +/* 698 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -/*! - * map-cache - * - * Copyright (c) 2015, Jon Schlinkert. - * Licensed under the MIT License. - */ - - -var hasOwn = Object.prototype.hasOwnProperty; - -/** - * Expose `MapCache` - */ -module.exports = MapCache; +var regexNot = __webpack_require__(596); +var toRegex = __webpack_require__(579); +var isOdd = __webpack_require__(699); /** - * Creates a cache object to store key/value pairs. - * - * ```js - * var cache = new MapCache(); - * ``` - * - * @api public + * Characters to use in negation regex (we want to "not" match + * characters that are matched by other parsers) */ -function MapCache(data) { - this.__data__ = data || {}; -} +var cached; +var NOT_REGEX = '[\\[!*+?$^"\'.\\\\/]+'; +var not = createTextRegex(NOT_REGEX); /** - * Adds `value` to `key` on the cache. - * - * ```js - * cache.set('foo', 'bar'); - * ``` - * - * @param {String} `key` The key of the value to cache. - * @param {*} `value` The value to cache. - * @returns {Object} Returns the `Cache` object for chaining. - * @api public + * Nanomatch parsers */ -MapCache.prototype.set = function mapSet(key, value) { - if (key !== '__proto__') { - this.__data__[key] = value; - } - return this; -}; - -/** - * Gets the cached value for `key`. - * - * ```js - * cache.get('foo'); - * //=> 'bar' - * ``` - * - * @param {String} `key` The key of the value to get. - * @returns {*} Returns the cached value. - * @api public - */ +module.exports = function(nanomatch, options) { + var parser = nanomatch.parser; + var opts = parser.options; -MapCache.prototype.get = function mapGet(key) { - return key === '__proto__' ? undefined : this.__data__[key]; -}; + parser.state = { + slashes: 0, + paths: [] + }; -/** - * Checks if a cached value for `key` exists. - * - * ```js - * cache.has('foo'); - * //=> true - * ``` - * - * @param {String} `key` The key of the entry to check. - * @returns {Boolean} Returns `true` if an entry for `key` exists, else `false`. - * @api public - */ + parser.ast.state = parser.state; + parser -MapCache.prototype.has = function mapHas(key) { - return key !== '__proto__' && hasOwn.call(this.__data__, key); -}; + /** + * Beginning-of-string + */ -/** - * Removes `key` and its value from the cache. - * - * ```js - * cache.del('foo'); - * ``` - * @title .del - * @param {String} `key` The key of the value to remove. - * @returns {Boolean} Returns `true` if the entry was removed successfully, else `false`. - * @api public - */ + .capture('prefix', function() { + if (this.parsed) return; + var m = this.match(/^\.[\\/]/); + if (!m) return; + this.state.strictOpen = !!this.options.strictOpen; + this.state.addPrefix = true; + }) -MapCache.prototype.del = function mapDelete(key) { - return this.has(key) && delete this.__data__[key]; -}; + /** + * Escape: "\\." + */ + .capture('escape', function() { + if (this.isInside('bracket')) return; + var pos = this.position(); + var m = this.match(/^(?:\\(.)|([$^]))/); + if (!m) return; -/***/ }), -/* 684 */ -/***/ (function(module, exports, __webpack_require__) { + return pos({ + type: 'escape', + val: m[2] || m[1] + }); + }) -"use strict"; + /** + * Quoted strings + */ + .capture('quoted', function() { + var pos = this.position(); + var m = this.match(/^["']/); + if (!m) return; -var define = __webpack_require__(648); + var quote = m[0]; + if (this.input.indexOf(quote) === -1) { + return pos({ + type: 'escape', + val: quote + }); + } -/** - * Store position for a node - */ + var tok = advanceTo(this.input, quote); + this.consume(tok.len); -module.exports = function Position(start, parser) { - this.start = start; - this.end = { line: parser.line, column: parser.column }; - define(this, 'content', parser.orig); - define(this, 'source', parser.options.source); -}; + return pos({ + type: 'quoted', + val: tok.esc + }); + }) + /** + * Negations: "!" + */ -/***/ }), -/* 685 */ -/***/ (function(module, exports, __webpack_require__) { + .capture('not', function() { + var parsed = this.parsed; + var pos = this.position(); + var m = this.match(this.notRegex || /^!+/); + if (!m) return; + var val = m[0]; -"use strict"; + var isNegated = isOdd(val.length); + if (parsed === '' && !isNegated) { + val = ''; + } + // if nothing has been parsed, we know `!` is at the start, + // so we need to wrap the result in a negation regex + if (parsed === '' && isNegated && this.options.nonegate !== true) { + this.bos.val = '(?!^(?:'; + this.append = ')$).*'; + val = ''; + } + return pos({ + type: 'not', + val: val + }); + }) -var isExtendable = __webpack_require__(686); -var assignSymbols = __webpack_require__(589); + /** + * Dot: "." + */ -module.exports = Object.assign || function(obj/*, objects*/) { - if (obj === null || typeof obj === 'undefined') { - throw new TypeError('Cannot convert undefined or null to object'); - } - if (!isObject(obj)) { - obj = {}; - } - for (var i = 1; i < arguments.length; i++) { - var val = arguments[i]; - if (isString(val)) { - val = toObject(val); - } - if (isObject(val)) { - assign(obj, val); - assignSymbols(obj, val); - } - } - return obj; -}; + .capture('dot', function() { + var parsed = this.parsed; + var pos = this.position(); + var m = this.match(/^\.+/); + if (!m) return; -function assign(a, b) { - for (var key in b) { - if (hasOwn(b, key)) { - a[key] = b[key]; - } - } -} + var val = m[0]; + this.state.dot = val === '.' && (parsed === '' || parsed.slice(-1) === '/'); -function isString(val) { - return (val && typeof val === 'string'); -} + return pos({ + type: 'dot', + dotfiles: this.state.dot, + val: val + }); + }) -function toObject(str) { - var obj = {}; - for (var i in str) { - obj[i] = str[i]; - } - return obj; -} + /** + * Plus: "+" + */ -function isObject(val) { - return (val && typeof val === 'object') || isExtendable(val); -} + .capture('plus', /^\+(?!\()/) -/** - * Returns true if the given `key` is an own property of `obj`. - */ + /** + * Question mark: "?" + */ -function hasOwn(obj, key) { - return Object.prototype.hasOwnProperty.call(obj, key); -} + .capture('qmark', function() { + var parsed = this.parsed; + var pos = this.position(); + var m = this.match(/^\?+(?!\()/); + if (!m) return; -function isEnum(obj, key) { - return Object.prototype.propertyIsEnumerable.call(obj, key); -} + this.state.metachar = true; + this.state.qmark = true; + return pos({ + type: 'qmark', + parsed: parsed, + val: m[0] + }); + }) -/***/ }), -/* 686 */ -/***/ (function(module, exports, __webpack_require__) { + /** + * Globstar: "**" + */ -"use strict"; -/*! - * is-extendable - * - * Copyright (c) 2015-2017, Jon Schlinkert. - * Released under the MIT License. - */ + .capture('globstar', function() { + var parsed = this.parsed; + var pos = this.position(); + var m = this.match(/^\*{2}(?![*(])(?=[,)/]|$)/); + if (!m) return; + var type = opts.noglobstar !== true ? 'globstar' : 'star'; + var node = pos({type: type, parsed: parsed}); + this.state.metachar = true; + while (this.input.slice(0, 4) === '/**/') { + this.input = this.input.slice(3); + } -var isPlainObject = __webpack_require__(588); + node.isInside = { + brace: this.isInside('brace'), + paren: this.isInside('paren') + }; -module.exports = function isExtendable(val) { - return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); -}; + if (type === 'globstar') { + this.state.globstar = true; + node.val = '**'; + } else { + this.state.star = true; + node.val = '*'; + } -/***/ }), -/* 687 */ -/***/ (function(module, exports, __webpack_require__) { + return node; + }) -"use strict"; + /** + * Star: "*" + */ + .capture('star', function() { + var pos = this.position(); + var starRe = /^(?:\*(?![*(])|[*]{3,}(?!\()|[*]{2}(?![(/]|$)|\*(?=\*\())/; + var m = this.match(starRe); + if (!m) return; -var nanomatch = __webpack_require__(688); -var extglob = __webpack_require__(702); + this.state.metachar = true; + this.state.star = true; + return pos({ + type: 'star', + val: m[0] + }); + }) -module.exports = function(snapdragon) { - var compilers = snapdragon.compiler.compilers; - var opts = snapdragon.options; + /** + * Slash: "/" + */ - // register nanomatch compilers - snapdragon.use(nanomatch.compilers); + .capture('slash', function() { + var pos = this.position(); + var m = this.match(/^\//); + if (!m) return; - // get references to some specific nanomatch compilers before they - // are overridden by the extglob and/or custom compilers - var escape = compilers.escape; - var qmark = compilers.qmark; - var slash = compilers.slash; - var star = compilers.star; - var text = compilers.text; - var plus = compilers.plus; - var dot = compilers.dot; + this.state.slashes++; + return pos({ + type: 'slash', + val: m[0] + }); + }) - // register extglob compilers or escape exglobs if disabled - if (opts.extglob === false || opts.noext === true) { - snapdragon.compiler.use(escapeExtglobs); - } else { - snapdragon.use(extglob.compilers); - } + /** + * Backslash: "\\" + */ - snapdragon.use(function() { - this.options.star = this.options.star || function(/*node*/) { - return '[^\\\\/]*?'; - }; - }); + .capture('backslash', function() { + var pos = this.position(); + var m = this.match(/^\\(?![*+?(){}[\]'"])/); + if (!m) return; - // custom micromatch compilers - snapdragon.compiler + var val = m[0]; - // reset referenced compiler - .set('dot', dot) - .set('escape', escape) - .set('plus', plus) - .set('slash', slash) - .set('qmark', qmark) - .set('star', star) - .set('text', text); -}; + if (this.isInside('bracket')) { + val = '\\'; + } else if (val.length > 1) { + val = '\\\\'; + } -function escapeExtglobs(compiler) { - compiler.set('paren', function(node) { - var val = ''; - visit(node, function(tok) { - if (tok.val) val += (/^\W/.test(tok.val) ? '\\' : '') + tok.val; - }); - return this.emit(val, node); - }); + return pos({ + type: 'backslash', + val: val + }); + }) - /** - * Visit `node` with the given `fn` - */ + /** + * Square: "[.]" + */ - function visit(node, fn) { - return node.nodes ? mapVisit(node.nodes, fn) : fn(node); - } + .capture('square', function() { + if (this.isInside('bracket')) return; + var pos = this.position(); + var m = this.match(/^\[([^!^\\])\]/); + if (!m) return; - /** - * Map visit over array of `nodes`. - */ + return pos({ + type: 'square', + val: m[1] + }); + }) - function mapVisit(nodes, fn) { - var len = nodes.length; - var idx = -1; - while (++idx < len) { - visit(nodes[idx], fn); - } - } -} + /** + * Brackets: "[...]" (basic, this can be overridden by other parsers) + */ + .capture('bracket', function() { + var pos = this.position(); + var m = this.match(/^(?:\[([!^]?)([^\]]+|\]-)(\]|[^*+?]+)|\[)/); + if (!m) return; -/***/ }), -/* 688 */ -/***/ (function(module, exports, __webpack_require__) { + var val = m[0]; + var negated = m[1] ? '^' : ''; + var inner = (m[2] || '').replace(/\\\\+/, '\\\\'); + var close = m[3] || ''; -"use strict"; + if (m[2] && inner.length < m[2].length) { + val = val.replace(/\\\\+/, '\\\\'); + } + var esc = this.input.slice(0, 2); + if (inner === '' && esc === '\\]') { + inner += esc; + this.consume(2); -/** - * Module dependencies - */ + var str = this.input; + var idx = -1; + var ch; -var util = __webpack_require__(113); -var toRegex = __webpack_require__(573); -var extend = __webpack_require__(689); + while ((ch = str[++idx])) { + this.consume(1); + if (ch === ']') { + close = ch; + break; + } + inner += ch; + } + } -/** - * Local dependencies - */ + return pos({ + type: 'bracket', + val: val, + escaped: close !== ']', + negated: negated, + inner: inner, + close: close + }); + }) -var compilers = __webpack_require__(691); -var parsers = __webpack_require__(692); -var cache = __webpack_require__(695); -var utils = __webpack_require__(697); -var MAX_LENGTH = 1024 * 64; + /** + * Text + */ -/** - * The main function takes a list of strings and one or more - * glob patterns to use for matching. - * - * ```js - * var nm = require('nanomatch'); - * nm(list, patterns[, options]); - * - * console.log(nm(['a.js', 'a.txt'], ['*.js'])); - * //=> [ 'a.js' ] - * ``` - * @param {Array} `list` A list of strings to match - * @param {String|Array} `patterns` One or more glob patterns to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Array} Returns an array of matches - * @summary false - * @api public - */ + .capture('text', function() { + if (this.isInside('bracket')) return; + var pos = this.position(); + var m = this.match(not); + if (!m || !m[0]) return; -function nanomatch(list, patterns, options) { - patterns = utils.arrayify(patterns); - list = utils.arrayify(list); + return pos({ + type: 'text', + val: m[0] + }); + }); - var len = patterns.length; - if (list.length === 0 || len === 0) { - return []; - } + /** + * Allow custom parsers to be passed on options + */ - if (len === 1) { - return nanomatch.match(list, patterns[0], options); + if (options && typeof options.parsers === 'function') { + options.parsers(nanomatch.parser); } +}; - var negated = false; - var omit = []; - var keep = []; - var idx = -1; +/** + * Advance to the next non-escaped character + */ - while (++idx < len) { - var pattern = patterns[idx]; +function advanceTo(input, endChar) { + var ch = input.charAt(0); + var tok = { len: 1, val: '', esc: '' }; + var idx = 0; - if (typeof pattern === 'string' && pattern.charCodeAt(0) === 33 /* ! */) { - omit.push.apply(omit, nanomatch.match(list, pattern.slice(1), options)); - negated = true; - } else { - keep.push.apply(keep, nanomatch.match(list, pattern, options)); + function advance() { + if (ch !== '\\') { + tok.esc += '\\' + ch; + tok.val += ch; } - } - // minimatch.match parity - if (negated && keep.length === 0) { - if (options && options.unixify === false) { - keep = list.slice(); - } else { - var unixify = utils.unixify(options); - for (var i = 0; i < list.length; i++) { - keep.push(unixify(list[i])); - } + ch = input.charAt(++idx); + tok.len++; + + if (ch === '\\') { + advance(); + advance(); } } - var matches = utils.diff(keep, omit); - if (!options || options.nodupes !== false) { - return utils.unique(matches); + while (ch && ch !== endChar) { + advance(); } - - return matches; + return tok; } /** - * Similar to the main function, but `pattern` must be a string. - * - * ```js - * var nm = require('nanomatch'); - * nm.match(list, pattern[, options]); - * - * console.log(nm.match(['a.a', 'a.aa', 'a.b', 'a.c'], '*.a')); - * //=> ['a.a', 'a.aa'] - * ``` - * @param {Array} `list` Array of strings to match - * @param {String} `pattern` Glob pattern to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Array} Returns an array of matches - * @api public + * Create text regex */ -nanomatch.match = function(list, pattern, options) { - if (Array.isArray(pattern)) { - throw new TypeError('expected pattern to be a string'); - } +function createTextRegex(pattern) { + if (cached) return cached; + var opts = {contains: true, strictClose: false}; + var not = regexNot.create(pattern, opts); + var re = toRegex('^(?:[*]\\((?=.)|' + not + ')', opts); + return (cached = re); +} - var unixify = utils.unixify(options); - var isMatch = memoize('match', pattern, options, nanomatch.matcher); - var matches = []; +/** + * Expose negation string + */ - list = utils.arrayify(list); - var len = list.length; - var idx = -1; +module.exports.not = NOT_REGEX; - while (++idx < len) { - var ele = list[idx]; - if (ele === pattern || isMatch(ele)) { - matches.push(utils.value(ele, unixify, options)); - } - } - // if no options were passed, uniquify results and return - if (typeof options === 'undefined') { - return utils.unique(matches); - } +/***/ }), +/* 699 */ +/***/ (function(module, exports, __webpack_require__) { - if (matches.length === 0) { - if (options.failglob === true) { - throw new Error('no matches found for "' + pattern + '"'); - } - if (options.nonull === true || options.nullglob === true) { - return [options.unescape ? utils.unescape(pattern) : pattern]; - } - } +"use strict"; +/*! + * is-odd + * + * Copyright (c) 2015-2017, Jon Schlinkert. + * Released under the MIT License. + */ - // if `opts.ignore` was defined, diff ignored list - if (options.ignore) { - matches = nanomatch.not(matches, options.ignore, options); - } - return options.nodupes !== false ? utils.unique(matches) : matches; -}; -/** - * Returns true if the specified `string` matches the given glob `pattern`. - * - * ```js - * var nm = require('nanomatch'); - * nm.isMatch(string, pattern[, options]); - * - * console.log(nm.isMatch('a.a', '*.a')); - * //=> true - * console.log(nm.isMatch('a.b', '*.a')); - * //=> false - * ``` - * @param {String} `string` String to match - * @param {String} `pattern` Glob pattern to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Boolean} Returns true if the string matches the glob pattern. - * @api public - */ +var isNumber = __webpack_require__(700); -nanomatch.isMatch = function(str, pattern, options) { - if (typeof str !== 'string') { - throw new TypeError('expected a string: "' + util.inspect(str) + '"'); +module.exports = function isOdd(i) { + if (!isNumber(i)) { + throw new TypeError('is-odd expects a number.'); } - - if (utils.isEmptyString(str) || utils.isEmptyString(pattern)) { - return false; + if (Number(i) !== Math.floor(i)) { + throw new RangeError('is-odd expects an integer.'); } + return !!(~~i & 1); +}; - var equals = utils.equalsPattern(options); - if (equals(str)) { - return true; - } - var isMatch = memoize('isMatch', pattern, options, nanomatch.matcher); - return isMatch(str); -}; +/***/ }), +/* 700 */ +/***/ (function(module, exports, __webpack_require__) { -/** - * Returns true if some of the elements in the given `list` match any of the - * given glob `patterns`. - * - * ```js - * var nm = require('nanomatch'); - * nm.some(list, patterns[, options]); +"use strict"; +/*! + * is-number * - * console.log(nm.some(['foo.js', 'bar.js'], ['*.js', '!foo.js'])); - * // true - * console.log(nm.some(['foo.js'], ['*.js', '!foo.js'])); - * // false - * ``` - * @param {String|Array} `list` The string or array of strings to test. Returns as soon as the first match is found. - * @param {String|Array} `patterns` One or more glob patterns to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Boolean} Returns true if any patterns match `str` - * @api public + * Copyright (c) 2014-2017, Jon Schlinkert. + * Released under the MIT License. */ -nanomatch.some = function(list, patterns, options) { - if (typeof list === 'string') { - list = [list]; - } - - for (var i = 0; i < list.length; i++) { - if (nanomatch(list[i], patterns, options).length === 1) { - return true; - } - } - - return false; -}; -/** - * Returns true if every element in the given `list` matches - * at least one of the given glob `patterns`. - * - * ```js - * var nm = require('nanomatch'); - * nm.every(list, patterns[, options]); - * - * console.log(nm.every('foo.js', ['foo.js'])); - * // true - * console.log(nm.every(['foo.js', 'bar.js'], ['*.js'])); - * // true - * console.log(nm.every(['foo.js', 'bar.js'], ['*.js', '!foo.js'])); - * // false - * console.log(nm.every(['foo.js'], ['*.js', '!foo.js'])); - * // false - * ``` - * @param {String|Array} `list` The string or array of strings to test. - * @param {String|Array} `patterns` One or more glob patterns to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Boolean} Returns true if any patterns match `str` - * @api public - */ -nanomatch.every = function(list, patterns, options) { - if (typeof list === 'string') { - list = [list]; - } +module.exports = function isNumber(num) { + var type = typeof num; - for (var i = 0; i < list.length; i++) { - if (nanomatch(list[i], patterns, options).length !== 1) { - return false; - } + if (type === 'string' || num instanceof String) { + // an empty string would be coerced to true with the below logic + if (!num.trim()) return false; + } else if (type !== 'number' && !(num instanceof Number)) { + return false; } - return true; + return (num - num + 1) >= 0; }; -/** - * Returns true if **any** of the given glob `patterns` - * match the specified `string`. - * - * ```js - * var nm = require('nanomatch'); - * nm.any(string, patterns[, options]); - * - * console.log(nm.any('a.a', ['b.*', '*.a'])); - * //=> true - * console.log(nm.any('a.a', 'b.*')); - * //=> false - * ``` - * @param {String|Array} `str` The string to test. - * @param {String|Array} `patterns` One or more glob patterns to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Boolean} Returns true if any patterns match `str` - * @api public - */ -nanomatch.any = function(str, patterns, options) { - if (typeof str !== 'string') { - throw new TypeError('expected a string: "' + util.inspect(str) + '"'); - } +/***/ }), +/* 701 */ +/***/ (function(module, exports, __webpack_require__) { - if (utils.isEmptyString(str) || utils.isEmptyString(patterns)) { - return false; - } +module.exports = new (__webpack_require__(702))(); - if (typeof patterns === 'string') { - patterns = [patterns]; - } - for (var i = 0; i < patterns.length; i++) { - if (nanomatch.isMatch(str, patterns[i], options)) { - return true; - } - } - return false; -}; +/***/ }), +/* 702 */ +/***/ (function(module, exports, __webpack_require__) { -/** - * Returns true if **all** of the given `patterns` - * match the specified string. - * - * ```js - * var nm = require('nanomatch'); - * nm.all(string, patterns[, options]); - * - * console.log(nm.all('foo.js', ['foo.js'])); - * // true - * - * console.log(nm.all('foo.js', ['*.js', '!foo.js'])); - * // false - * - * console.log(nm.all('foo.js', ['*.js', 'foo.js'])); - * // true +"use strict"; +/*! + * fragment-cache * - * console.log(nm.all('foo.js', ['*.js', 'f*', '*o*', '*o.js'])); - * // true - * ``` - * @param {String|Array} `str` The string to test. - * @param {String|Array} `patterns` One or more glob patterns to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Boolean} Returns true if any patterns match `str` - * @api public + * Copyright (c) 2016-2017, Jon Schlinkert. + * Released under the MIT License. */ -nanomatch.all = function(str, patterns, options) { - if (typeof str !== 'string') { - throw new TypeError('expected a string: "' + util.inspect(str) + '"'); - } - if (typeof patterns === 'string') { - patterns = [patterns]; - } - for (var i = 0; i < patterns.length; i++) { - if (!nanomatch.isMatch(str, patterns[i], options)) { - return false; - } - } - return true; -}; +var MapCache = __webpack_require__(689); /** - * Returns a list of strings that _**do not match any**_ of the given `patterns`. + * Create a new `FragmentCache` with an optional object to use for `caches`. * * ```js - * var nm = require('nanomatch'); - * nm.not(list, patterns[, options]); - * - * console.log(nm.not(['a.a', 'b.b', 'c.c'], '*.a')); - * //=> ['b.b', 'c.c'] + * var fragment = new FragmentCache(); * ``` - * @param {Array} `list` Array of strings to match. - * @param {String|Array} `patterns` One or more glob pattern to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Array} Returns an array of strings that **do not match** the given patterns. + * @name FragmentCache + * @param {String} `cacheName` + * @return {Object} Returns the [map-cache][] instance. * @api public */ -nanomatch.not = function(list, patterns, options) { - var opts = extend({}, options); - var ignore = opts.ignore; - delete opts.ignore; - - list = utils.arrayify(list); - - var matches = utils.diff(list, nanomatch(list, patterns, opts)); - if (ignore) { - matches = utils.diff(matches, nanomatch(list, ignore)); - } - - return opts.nodupes !== false ? utils.unique(matches) : matches; -}; +function FragmentCache(caches) { + this.caches = caches || {}; +} /** - * Returns true if the given `string` contains the given pattern. Similar - * to [.isMatch](#isMatch) but the pattern can match any part of the string. - * - * ```js - * var nm = require('nanomatch'); - * nm.contains(string, pattern[, options]); - * - * console.log(nm.contains('aa/bb/cc', '*b')); - * //=> true - * console.log(nm.contains('aa/bb/cc', '*d')); - * //=> false - * ``` - * @param {String} `str` The string to match. - * @param {String|Array} `patterns` Glob pattern to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Boolean} Returns true if the patter matches any part of `str`. - * @api public + * Prototype */ -nanomatch.contains = function(str, patterns, options) { - if (typeof str !== 'string') { - throw new TypeError('expected a string: "' + util.inspect(str) + '"'); - } +FragmentCache.prototype = { - if (typeof patterns === 'string') { - if (utils.isEmptyString(str) || utils.isEmptyString(patterns)) { - return false; - } + /** + * Get cache `name` from the `fragment.caches` object. Creates a new + * `MapCache` if it doesn't already exist. + * + * ```js + * var cache = fragment.cache('files'); + * console.log(fragment.caches.hasOwnProperty('files')); + * //=> true + * ``` + * @name .cache + * @param {String} `cacheName` + * @return {Object} Returns the [map-cache][] instance. + * @api public + */ - var equals = utils.equalsPattern(patterns, options); - if (equals(str)) { - return true; - } - var contains = utils.containsPattern(patterns, options); - if (contains(str)) { - return true; - } - } + cache: function(cacheName) { + return this.caches[cacheName] || (this.caches[cacheName] = new MapCache()); + }, - var opts = extend({}, options, {contains: true}); - return nanomatch.any(str, patterns, opts); -}; + /** + * Set a value for property `key` on cache `name` + * + * ```js + * fragment.set('files', 'somefile.js', new File({path: 'somefile.js'})); + * ``` + * @name .set + * @param {String} `name` + * @param {String} `key` Property name to set + * @param {any} `val` The value of `key` + * @return {Object} The cache instance for chaining + * @api public + */ -/** - * Returns true if the given pattern and options should enable - * the `matchBase` option. - * @return {Boolean} - * @api private - */ + set: function(cacheName, key, val) { + var cache = this.cache(cacheName); + cache.set(key, val); + return cache; + }, -nanomatch.matchBase = function(pattern, options) { - if (pattern && pattern.indexOf('/') !== -1 || !options) return false; - return options.basename === true || options.matchBase === true; -}; + /** + * Returns true if a non-undefined value is set for `key` on fragment cache `name`. + * + * ```js + * var cache = fragment.cache('files'); + * cache.set('somefile.js'); + * + * console.log(cache.has('somefile.js')); + * //=> true + * + * console.log(cache.has('some-other-file.js')); + * //=> false + * ``` + * @name .has + * @param {String} `name` Cache name + * @param {String} `key` Optionally specify a property to check for on cache `name` + * @return {Boolean} + * @api public + */ -/** - * Filter the keys of the given object with the given `glob` pattern - * and `options`. Does not attempt to match nested keys. If you need this feature, - * use [glob-object][] instead. - * - * ```js - * var nm = require('nanomatch'); - * nm.matchKeys(object, patterns[, options]); - * - * var obj = { aa: 'a', ab: 'b', ac: 'c' }; - * console.log(nm.matchKeys(obj, '*b')); - * //=> { ab: 'b' } - * ``` - * @param {Object} `object` The object with keys to filter. - * @param {String|Array} `patterns` One or more glob patterns to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Object} Returns an object with only keys that match the given patterns. - * @api public - */ + has: function(cacheName, key) { + return typeof this.get(cacheName, key) !== 'undefined'; + }, + + /** + * Get `name`, or if specified, the value of `key`. Invokes the [cache]() method, + * so that cache `name` will be created it doesn't already exist. If `key` is not passed, + * the entire cache (`name`) is returned. + * + * ```js + * var Vinyl = require('vinyl'); + * var cache = fragment.cache('files'); + * cache.set('somefile.js', new Vinyl({path: 'somefile.js'})); + * console.log(cache.get('somefile.js')); + * //=> + * ``` + * @name .get + * @param {String} `name` + * @return {Object} Returns cache `name`, or the value of `key` if specified + * @api public + */ -nanomatch.matchKeys = function(obj, patterns, options) { - if (!utils.isObject(obj)) { - throw new TypeError('expected the first argument to be an object'); + get: function(name, key) { + var cache = this.cache(name); + if (typeof key === 'string') { + return cache.get(key); + } + return cache; } - var keys = nanomatch(Object.keys(obj), patterns, options); - return utils.pick(obj, keys); }; /** - * Returns a memoized matcher function from the given glob `pattern` and `options`. - * The returned function takes a string to match as its only argument and returns - * true if the string is a match. - * - * ```js - * var nm = require('nanomatch'); - * nm.matcher(pattern[, options]); - * - * var isMatch = nm.matcher('*.!(*a)'); - * console.log(isMatch('a.a')); - * //=> false - * console.log(isMatch('a.b')); - * //=> true - * ``` - * @param {String} `pattern` Glob pattern - * @param {Object} `options` See available [options](#options) for changing how matches are performed. - * @return {Function} Returns a matcher function. - * @api public + * Expose `FragmentCache` */ -nanomatch.matcher = function matcher(pattern, options) { - if (utils.isEmptyString(pattern)) { - return function() { - return false; - }; - } - - if (Array.isArray(pattern)) { - return compose(pattern, options, matcher); - } +exports = module.exports = FragmentCache; - // if pattern is a regex - if (pattern instanceof RegExp) { - return test(pattern); - } - // if pattern is invalid - if (!utils.isString(pattern)) { - throw new TypeError('expected pattern to be an array, string or regex'); - } +/***/ }), +/* 703 */ +/***/ (function(module, exports, __webpack_require__) { - // if pattern is a non-glob string - if (!utils.hasSpecialChars(pattern)) { - if (options && options.nocase === true) { - pattern = pattern.toLowerCase(); - } - return utils.matchPath(pattern, options); - } +"use strict"; - // if pattern is a glob string - var re = nanomatch.makeRe(pattern, options); - // if `options.matchBase` or `options.basename` is defined - if (nanomatch.matchBase(pattern, options)) { - return utils.matchBasename(re, options); - } +var utils = module.exports; +var path = __webpack_require__(4); - function test(regex) { - var equals = utils.equalsPattern(options); - var unixify = utils.unixify(options); +/** + * Module dependencies + */ - return function(str) { - if (equals(str)) { - return true; - } +var isWindows = __webpack_require__(704)(); +var Snapdragon = __webpack_require__(623); +utils.define = __webpack_require__(705); +utils.diff = __webpack_require__(706); +utils.extend = __webpack_require__(695); +utils.pick = __webpack_require__(707); +utils.typeOf = __webpack_require__(589); +utils.unique = __webpack_require__(599); - if (regex.test(unixify(str))) { - return true; - } - return false; - }; - } +/** + * Returns true if the given value is effectively an empty string + */ - // create matcher function - var matcherFn = test(re); - // set result object from compiler on matcher function, - // as a non-enumerable property. useful for debugging - utils.define(matcherFn, 'result', re.result); - return matcherFn; +utils.isEmptyString = function(val) { + return String(val) === '' || String(val) === './'; }; /** - * Returns an array of matches captured by `pattern` in `string, or - * `null` if the pattern did not match. - * - * ```js - * var nm = require('nanomatch'); - * nm.capture(pattern, string[, options]); - * - * console.log(nm.capture('test/*.js', 'test/foo.js')); - * //=> ['foo'] - * console.log(nm.capture('test/*.js', 'foo/bar.css')); - * //=> null - * ``` - * @param {String} `pattern` Glob pattern to use for matching. - * @param {String} `string` String to match - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Boolean} Returns an array of captures if the string matches the glob pattern, otherwise `null`. - * @api public + * Returns true if the platform is windows, or `path.sep` is `\\`. + * This is defined as a function to allow `path.sep` to be set in unit tests, + * or by the user, if there is a reason to do so. + * @return {Boolean} */ -nanomatch.capture = function(pattern, str, options) { - var re = nanomatch.makeRe(pattern, extend({capture: true}, options)); - var unixify = utils.unixify(options); - - function match() { - return function(string) { - var match = re.exec(unixify(string)); - if (!match) { - return null; - } +utils.isWindows = function() { + return path.sep === '\\' || isWindows === true; +}; - return match.slice(1); - }; - } +/** + * Return the last element from an array + */ - var capture = memoize('capture', pattern, options, match); - return capture(str); +utils.last = function(arr, n) { + return arr[arr.length - (n || 1)]; }; /** - * Create a regular expression from the given glob `pattern`. - * - * ```js - * var nm = require('nanomatch'); - * nm.makeRe(pattern[, options]); - * - * console.log(nm.makeRe('*.js')); - * //=> /^(?:(\.[\\\/])?(?!\.)(?=.)[^\/]*?\.js)$/ - * ``` - * @param {String} `pattern` A glob pattern to convert to regex. - * @param {Object} `options` See available [options](#options) for changing how matches are performed. - * @return {RegExp} Returns a regex created from the given pattern. - * @api public + * Get the `Snapdragon` instance to use */ -nanomatch.makeRe = function(pattern, options) { - if (pattern instanceof RegExp) { - return pattern; +utils.instantiate = function(ast, options) { + var snapdragon; + // if an instance was created by `.parse`, use that instance + if (utils.typeOf(ast) === 'object' && ast.snapdragon) { + snapdragon = ast.snapdragon; + // if the user supplies an instance on options, use that instance + } else if (utils.typeOf(options) === 'object' && options.snapdragon) { + snapdragon = options.snapdragon; + // create a new instance + } else { + snapdragon = new Snapdragon(options); } - if (typeof pattern !== 'string') { - throw new TypeError('expected pattern to be a string'); - } + utils.define(snapdragon, 'parse', function(str, options) { + var parsed = Snapdragon.prototype.parse.call(this, str, options); + parsed.input = str; - if (pattern.length > MAX_LENGTH) { - throw new Error('expected pattern to be less than ' + MAX_LENGTH + ' characters'); - } + // escape unmatched brace/bracket/parens + var last = this.parser.stack.pop(); + if (last && this.options.strictErrors !== true) { + var open = last.nodes[0]; + var inner = last.nodes[1]; + if (last.type === 'bracket') { + if (inner.val.charAt(0) === '[') { + inner.val = '\\' + inner.val; + } - function makeRe() { - var opts = utils.extend({wrap: false}, options); - var result = nanomatch.create(pattern, opts); - var regex = toRegex(result.output, opts); - utils.define(regex, 'result', result); - return regex; - } + } else { + open.val = '\\' + open.val; + var sibling = open.parent.nodes[1]; + if (sibling.type === 'star') { + sibling.loose = true; + } + } + } - return memoize('makeRe', pattern, options, makeRe); + // add non-enumerable parser reference + utils.define(parsed, 'parser', this.parser); + return parsed; + }); + + return snapdragon; }; /** - * Parses the given glob `pattern` and returns an object with the compiled `output` - * and optional source `map`. - * - * ```js - * var nm = require('nanomatch'); - * nm.create(pattern[, options]); - * - * console.log(nm.create('abc/*.js')); - * // { options: { source: 'string', sourcemap: true }, - * // state: {}, - * // compilers: - * // { ... }, - * // output: '(\\.[\\\\\\/])?abc\\/(?!\\.)(?=.)[^\\/]*?\\.js', - * // ast: - * // { type: 'root', - * // errors: [], - * // nodes: - * // [ ... ], - * // dot: false, - * // input: 'abc/*.js' }, - * // parsingErrors: [], - * // map: - * // { version: 3, - * // sources: [ 'string' ], - * // names: [], - * // mappings: 'AAAA,GAAG,EAAC,kBAAC,EAAC,EAAE', - * // sourcesContent: [ 'abc/*.js' ] }, - * // position: { line: 1, column: 28 }, - * // content: {}, - * // files: {}, - * // idx: 6 } - * ``` - * @param {String} `pattern` Glob pattern to parse and compile. - * @param {Object} `options` Any [options](#options) to change how parsing and compiling is performed. - * @return {Object} Returns an object with the parsed AST, compiled string and optional source map. - * @api public + * Create the key to use for memoization. The key is generated + * by iterating over the options and concatenating key-value pairs + * to the pattern string. */ -nanomatch.create = function(pattern, options) { - if (typeof pattern !== 'string') { - throw new TypeError('expected a string'); +utils.createKey = function(pattern, options) { + if (typeof options === 'undefined') { + return pattern; } - function create() { - return nanomatch.compile(nanomatch.parse(pattern, options), options); + var key = pattern; + for (var prop in options) { + if (options.hasOwnProperty(prop)) { + key += ';' + prop + '=' + String(options[prop]); + } } - return memoize('create', pattern, options, create); + return key; }; /** - * Parse the given `str` with the given `options`. - * - * ```js - * var nm = require('nanomatch'); - * nm.parse(pattern[, options]); - * - * var ast = nm.parse('a/{b,c}/d'); - * console.log(ast); - * // { type: 'root', - * // errors: [], - * // input: 'a/{b,c}/d', - * // nodes: - * // [ { type: 'bos', val: '' }, - * // { type: 'text', val: 'a/' }, - * // { type: 'brace', - * // nodes: - * // [ { type: 'brace.open', val: '{' }, - * // { type: 'text', val: 'b,c' }, - * // { type: 'brace.close', val: '}' } ] }, - * // { type: 'text', val: '/d' }, - * // { type: 'eos', val: '' } ] } - * ``` - * @param {String} `str` - * @param {Object} `options` - * @return {Object} Returns an AST - * @api public + * Cast `val` to an array + * @return {Array} */ -nanomatch.parse = function(pattern, options) { - if (typeof pattern !== 'string') { - throw new TypeError('expected a string'); - } - - function parse() { - var snapdragon = utils.instantiate(null, options); - parsers(snapdragon, options); - - var ast = snapdragon.parse(pattern, options); - utils.define(ast, 'snapdragon', snapdragon); - ast.input = pattern; - return ast; - } - - return memoize('parse', pattern, options, parse); +utils.arrayify = function(val) { + if (typeof val === 'string') return [val]; + return val ? (Array.isArray(val) ? val : [val]) : []; }; /** - * Compile the given `ast` or string with the given `options`. - * - * ```js - * var nm = require('nanomatch'); - * nm.compile(ast[, options]); - * - * var ast = nm.parse('a/{b,c}/d'); - * console.log(nm.compile(ast)); - * // { options: { source: 'string' }, - * // state: {}, - * // compilers: - * // { eos: [Function], - * // noop: [Function], - * // bos: [Function], - * // brace: [Function], - * // 'brace.open': [Function], - * // text: [Function], - * // 'brace.close': [Function] }, - * // output: [ 'a/(b|c)/d' ], - * // ast: - * // { ... }, - * // parsingErrors: [] } - * ``` - * @param {Object|String} `ast` - * @param {Object} `options` - * @return {Object} Returns an object that has an `output` property with the compiled string. - * @api public + * Return true if `val` is a non-empty string */ -nanomatch.compile = function(ast, options) { - if (typeof ast === 'string') { - ast = nanomatch.parse(ast, options); - } +utils.isString = function(val) { + return typeof val === 'string'; +}; - function compile() { - var snapdragon = utils.instantiate(ast, options); - compilers(snapdragon, options); - return snapdragon.compile(ast, options); - } +/** + * Return true if `val` is a non-empty string + */ - return memoize('compile', ast.input, options, compile); +utils.isRegex = function(val) { + return utils.typeOf(val) === 'regexp'; }; /** - * Clear the regex cache. - * - * ```js - * nm.clearCache(); - * ``` - * @api public + * Return true if `val` is a non-empty string */ -nanomatch.clearCache = function() { - nanomatch.cache.__data__ = {}; +utils.isObject = function(val) { + return utils.typeOf(val) === 'object'; }; /** - * Compose a matcher function with the given patterns. - * This allows matcher functions to be compiled once and - * called multiple times. + * Escape regex characters in the given string */ -function compose(patterns, options, matcher) { - var matchers; +utils.escapeRegex = function(str) { + return str.replace(/[-[\]{}()^$|*+?.\\/\s]/g, '\\$&'); +}; - return memoize('compose', String(patterns), options, function() { - return function(file) { - // delay composition until it's invoked the first time, - // after that it won't be called again - if (!matchers) { - matchers = []; - for (var i = 0; i < patterns.length; i++) { - matchers.push(matcher(patterns[i], options)); - } - } +/** + * Combines duplicate characters in the provided `input` string. + * @param {String} `input` + * @returns {String} + */ - var len = matchers.length; - while (len--) { - if (matchers[len](file) === true) { - return true; - } - } - return false; - }; +utils.combineDupes = function(input, patterns) { + patterns = utils.arrayify(patterns).join('|').split('|'); + patterns = patterns.map(function(s) { + return s.replace(/\\?([+*\\/])/g, '\\$1'); }); -} + var substr = patterns.join('|'); + var regex = new RegExp('(' + substr + ')(?=\\1)', 'g'); + return input.replace(regex, ''); +}; /** - * Memoize a generated regex or function. A unique key is generated - * from the `type` (usually method name), the `pattern`, and - * user-defined options. + * Returns true if the given `str` has special characters */ -function memoize(type, pattern, options, fn) { - var key = utils.createKey(type + '=' + pattern, options); - - if (options && options.cache === false) { - return fn(pattern, options); - } - - if (cache.has(type, key)) { - return cache.get(type, key); - } - - var val = fn(pattern, options); - cache.set(type, key, val); - return val; -} +utils.hasSpecialChars = function(str) { + return /(?:(?:(^|\/)[!.])|[*?+()|[\]{}]|[+@]\()/.test(str); +}; /** - * Expose compiler, parser and cache on `nanomatch` + * Normalize slashes in the given filepath. + * + * @param {String} `filepath` + * @return {String} */ -nanomatch.compilers = compilers; -nanomatch.parsers = parsers; -nanomatch.cache = cache; +utils.toPosixPath = function(str) { + return str.replace(/\\+/g, '/'); +}; /** - * Expose `nanomatch` - * @type {Function} + * Strip backslashes before special characters in a string. + * + * @param {String} `str` + * @return {String} */ -module.exports = nanomatch; +utils.unescape = function(str) { + return utils.toPosixPath(str.replace(/\\(?=[*+?!.])/g, '')); +}; +/** + * Strip the drive letter from a windows filepath + * @param {String} `fp` + * @return {String} + */ -/***/ }), -/* 689 */ -/***/ (function(module, exports, __webpack_require__) { +utils.stripDrive = function(fp) { + return utils.isWindows() ? fp.replace(/^[a-z]:[\\/]+?/i, '/') : fp; +}; -"use strict"; +/** + * Strip the prefix from a filepath + * @param {String} `fp` + * @return {String} + */ +utils.stripPrefix = function(str) { + if (str.charAt(0) === '.' && (str.charAt(1) === '/' || str.charAt(1) === '\\')) { + return str.slice(2); + } + return str; +}; -var isExtendable = __webpack_require__(690); -var assignSymbols = __webpack_require__(589); +/** + * Returns true if `str` is a common character that doesn't need + * to be processed to be used for matching. + * @param {String} `str` + * @return {Boolean} + */ -module.exports = Object.assign || function(obj/*, objects*/) { - if (obj === null || typeof obj === 'undefined') { - throw new TypeError('Cannot convert undefined or null to object'); - } - if (!isObject(obj)) { - obj = {}; - } - for (var i = 1; i < arguments.length; i++) { - var val = arguments[i]; - if (isString(val)) { - val = toObject(val); - } - if (isObject(val)) { - assign(obj, val); - assignSymbols(obj, val); - } - } - return obj; +utils.isSimpleChar = function(str) { + return str.trim() === '' || str === '.'; }; -function assign(a, b) { - for (var key in b) { - if (hasOwn(b, key)) { - a[key] = b[key]; - } - } -} +/** + * Returns true if the given str is an escaped or + * unescaped path character + */ -function isString(val) { - return (val && typeof val === 'string'); -} +utils.isSlash = function(str) { + return str === '/' || str === '\\/' || str === '\\' || str === '\\\\'; +}; -function toObject(str) { - var obj = {}; - for (var i in str) { - obj[i] = str[i]; - } - return obj; -} +/** + * Returns a function that returns true if the given + * pattern matches or contains a `filepath` + * + * @param {String} `pattern` + * @return {Function} + */ -function isObject(val) { - return (val && typeof val === 'object') || isExtendable(val); -} +utils.matchPath = function(pattern, options) { + return (options && options.contains) + ? utils.containsPattern(pattern, options) + : utils.equalsPattern(pattern, options); +}; /** - * Returns true if the given `key` is an own property of `obj`. + * Returns true if the given (original) filepath or unixified path are equal + * to the given pattern. */ -function hasOwn(obj, key) { - return Object.prototype.hasOwnProperty.call(obj, key); -} - -function isEnum(obj, key) { - return Object.prototype.propertyIsEnumerable.call(obj, key); -} +utils._equals = function(filepath, unixPath, pattern) { + return pattern === filepath || pattern === unixPath; +}; +/** + * Returns true if the given (original) filepath or unixified path contain + * the given pattern. + */ -/***/ }), -/* 690 */ -/***/ (function(module, exports, __webpack_require__) { +utils._contains = function(filepath, unixPath, pattern) { + return filepath.indexOf(pattern) !== -1 || unixPath.indexOf(pattern) !== -1; +}; -"use strict"; -/*! - * is-extendable +/** + * Returns a function that returns true if the given + * pattern is the same as a given `filepath` * - * Copyright (c) 2015-2017, Jon Schlinkert. - * Released under the MIT License. + * @param {String} `pattern` + * @return {Function} */ +utils.equalsPattern = function(pattern, options) { + var unixify = utils.unixify(options); + options = options || {}; + + return function fn(filepath) { + var equal = utils._equals(filepath, unixify(filepath), pattern); + if (equal === true || options.nocase !== true) { + return equal; + } + var lower = filepath.toLowerCase(); + return utils._equals(lower, unixify(lower), pattern); + }; +}; +/** + * Returns a function that returns true if the given + * pattern contains a `filepath` + * + * @param {String} `pattern` + * @return {Function} + */ -var isPlainObject = __webpack_require__(588); +utils.containsPattern = function(pattern, options) { + var unixify = utils.unixify(options); + options = options || {}; -module.exports = function isExtendable(val) { - return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); + return function(filepath) { + var contains = utils._contains(filepath, unixify(filepath), pattern); + if (contains === true || options.nocase !== true) { + return contains; + } + var lower = filepath.toLowerCase(); + return utils._contains(lower, unixify(lower), pattern); + }; }; +/** + * Returns a function that returns true if the given + * regex matches the `filename` of a file path. + * + * @param {RegExp} `re` Matching regex + * @return {Function} + */ -/***/ }), -/* 691 */ -/***/ (function(module, exports, __webpack_require__) { +utils.matchBasename = function(re) { + return function(filepath) { + return re.test(filepath) || re.test(path.basename(filepath)); + }; +}; -"use strict"; +/** + * Returns the given value unchanced. + * @return {any} + */ +utils.identity = function(val) { + return val; +}; /** -* Nanomatch compilers -*/ + * Determines the filepath to return based on the provided options. + * @return {any} + */ -module.exports = function(nanomatch, options) { - function slash() { - if (options && typeof options.slash === 'string') { - return options.slash; - } - if (options && typeof options.slash === 'function') { - return options.slash.call(nanomatch); - } - return '\\\\/'; +utils.value = function(str, unixify, options) { + if (options && options.unixify === false) { + return str; } - - function star() { - if (options && typeof options.star === 'string') { - return options.star; - } - if (options && typeof options.star === 'function') { - return options.star.call(nanomatch); - } - return '[^' + slash() + ']*?'; + if (options && typeof options.unixify === 'function') { + return options.unixify(str); } + return unixify(str); +}; - var ast = nanomatch.ast = nanomatch.parser.ast; - ast.state = nanomatch.parser.state; - nanomatch.compiler.state = ast.state; - nanomatch.compiler +/** + * Returns a function that normalizes slashes in a string to forward + * slashes, strips `./` from beginning of paths, and optionally unescapes + * special characters. + * @return {Function} + */ - /** - * Negation / escaping - */ +utils.unixify = function(options) { + var opts = options || {}; + return function(filepath) { + if (opts.stripPrefix !== false) { + filepath = utils.stripPrefix(filepath); + } + if (opts.unescape === true) { + filepath = utils.unescape(filepath); + } + if (opts.unixify === true || utils.isWindows()) { + filepath = utils.toPosixPath(filepath); + } + return filepath; + }; +}; - .set('not', function(node) { - var prev = this.prev(); - if (this.options.nonegate === true || prev.type !== 'bos') { - return this.emit('\\' + node.val, node); - } - return this.emit(node.val, node); - }) - .set('escape', function(node) { - if (this.options.unescape && /^[-\w_.]/.test(node.val)) { - return this.emit(node.val, node); - } - return this.emit('\\' + node.val, node); - }) - .set('quoted', function(node) { - return this.emit(node.val, node); - }) - /** - * Regex - */ +/***/ }), +/* 704 */ +/***/ (function(module, exports, __webpack_require__) { - .set('dollar', function(node) { - if (node.parent.type === 'bracket') { - return this.emit(node.val, node); - } - return this.emit('\\' + node.val, node); - }) +var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*! + * is-windows + * + * Copyright © 2015-2018, Jon Schlinkert. + * Released under the MIT License. + */ - /** - * Dot: "." - */ +(function(factory) { + if (exports && typeof exports === 'object' && typeof module !== 'undefined') { + module.exports = factory(); + } else if (true) { + !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), + __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? + (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), + __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); + } else {} +})(function() { + 'use strict'; + return function isWindows() { + return process && (process.platform === 'win32' || /^(msys|cygwin)$/.test(process.env.OSTYPE)); + }; +}); - .set('dot', function(node) { - if (node.dotfiles === true) this.dotfiles = true; - return this.emit('\\' + node.val, node); - }) - /** - * Slashes: "/" and "\" - */ +/***/ }), +/* 705 */ +/***/ (function(module, exports, __webpack_require__) { - .set('backslash', function(node) { - return this.emit(node.val, node); - }) - .set('slash', function(node, nodes, i) { - var val = '[' + slash() + ']'; - var parent = node.parent; - var prev = this.prev(); +"use strict"; +/*! + * define-property + * + * Copyright (c) 2015-2018, Jon Schlinkert. + * Released under the MIT License. + */ - // set "node.hasSlash" to true on all ancestor parens nodes - while (parent.type === 'paren' && !parent.hasSlash) { - parent.hasSlash = true; - parent = parent.parent; - } - if (prev.addQmark) { - val += '?'; - } - // word boundary - if (node.rest.slice(0, 2) === '\\b') { - return this.emit(val, node); - } +var isobject = __webpack_require__(587); +var isDescriptor = __webpack_require__(588); +var define = (typeof Reflect !== 'undefined' && Reflect.defineProperty) + ? Reflect.defineProperty + : Object.defineProperty; - // globstars - if (node.parsed === '**' || node.parsed === './**') { - this.output = '(?:' + this.output; - return this.emit(val + ')?', node); - } +module.exports = function defineProperty(obj, key, val) { + if (!isobject(obj) && typeof obj !== 'function' && !Array.isArray(obj)) { + throw new TypeError('expected an object, function, or array'); + } - // negation - if (node.parsed === '!**' && this.options.nonegate !== true) { - return this.emit(val + '?\\b', node); - } - return this.emit(val, node); - }) + if (typeof key !== 'string') { + throw new TypeError('expected "key" to be a string'); + } - /** - * Square brackets - */ + if (isDescriptor(val)) { + define(obj, key, val); + return obj; + } - .set('bracket', function(node) { - var close = node.close; - var open = !node.escaped ? '[' : '\\['; - var negated = node.negated; - var inner = node.inner; - var val = node.val; + define(obj, key, { + configurable: true, + enumerable: false, + writable: true, + value: val + }); - if (node.escaped === true) { - inner = inner.replace(/\\?(\W)/g, '\\$1'); - negated = ''; - } + return obj; +}; - if (inner === ']-') { - inner = '\\]\\-'; - } - if (negated && inner.indexOf('.') === -1) { - inner += '.'; - } - if (negated && inner.indexOf('/') === -1) { - inner += '/'; - } +/***/ }), +/* 706 */ +/***/ (function(module, exports, __webpack_require__) { - val = open + negated + inner + close; - return this.emit(val, node); - }) +"use strict"; +/*! + * arr-diff + * + * Copyright (c) 2014-2017, Jon Schlinkert. + * Released under the MIT License. + */ - /** - * Square: "[.]" (only matches a single character in brackets) - */ - .set('square', function(node) { - var val = (/^\W/.test(node.val) ? '\\' : '') + node.val; - return this.emit(val, node); - }) - /** - * Question mark: "?" - */ +module.exports = function diff(arr/*, arrays*/) { + var len = arguments.length; + var idx = 0; + while (++idx < len) { + arr = diffArray(arr, arguments[idx]); + } + return arr; +}; - .set('qmark', function(node) { - var prev = this.prev(); - // don't use "slash" variable so that we always avoid - // matching backslashes and slashes with a qmark - var val = '[^.\\\\/]'; - if (this.options.dot || (prev.type !== 'bos' && prev.type !== 'slash')) { - val = '[^\\\\/]'; - } +function diffArray(one, two) { + if (!Array.isArray(two)) { + return one.slice(); + } - if (node.parsed.slice(-1) === '(') { - var ch = node.rest.charAt(0); - if (ch === '!' || ch === '=' || ch === ':') { - return this.emit(node.val, node); - } - } + var tlen = two.length + var olen = one.length; + var idx = -1; + var arr = []; - if (node.val.length > 1) { - val += '{' + node.val.length + '}'; - } - return this.emit(val, node); - }) + while (++idx < olen) { + var ele = one[idx]; - /** - * Plus - */ + var hasEle = false; + for (var i = 0; i < tlen; i++) { + var val = two[i]; - .set('plus', function(node) { - var prev = node.parsed.slice(-1); - if (prev === ']' || prev === ')') { - return this.emit(node.val, node); - } - if (!this.output || (/[?*+]/.test(ch) && node.parent.type !== 'bracket')) { - return this.emit('\\+', node); - } - var ch = this.output.slice(-1); - if (/\w/.test(ch) && !node.inside) { - return this.emit('+\\+?', node); + if (ele === val) { + hasEle = true; + break; } - return this.emit('+', node); - }) - - /** - * globstar: '**' - */ + } - .set('globstar', function(node, nodes, i) { - if (!this.output) { - this.state.leadingGlobstar = true; - } + if (hasEle === false) { + arr.push(ele); + } + } + return arr; +} - var prev = this.prev(); - var before = this.prev(2); - var next = this.next(); - var after = this.next(2); - var type = prev.type; - var val = node.val; - if (prev.type === 'slash' && next.type === 'slash') { - if (before.type === 'text') { - this.output += '?'; +/***/ }), +/* 707 */ +/***/ (function(module, exports, __webpack_require__) { - if (after.type !== 'text') { - this.output += '\\b'; - } - } - } +"use strict"; +/*! + * object.pick + * + * Copyright (c) 2014-2015 Jon Schlinkert, contributors. + * Licensed under the MIT License + */ - var parsed = node.parsed; - if (parsed.charAt(0) === '!') { - parsed = parsed.slice(1); - } - var isInside = node.isInside.paren || node.isInside.brace; - if (parsed && type !== 'slash' && type !== 'bos' && !isInside) { - val = star(); - } else { - val = this.options.dot !== true - ? '(?:(?!(?:[' + slash() + ']|^)\\.).)*?' - : '(?:(?!(?:[' + slash() + ']|^)(?:\\.{1,2})($|[' + slash() + ']))(?!\\.{2}).)*?'; - } - if ((type === 'slash' || type === 'bos') && this.options.dot !== true) { - val = '(?!\\.)' + val; - } +var isObject = __webpack_require__(587); - if (prev.type === 'slash' && next.type === 'slash' && before.type !== 'text') { - if (after.type === 'text' || after.type === 'star') { - node.addQmark = true; - } - } +module.exports = function pick(obj, keys) { + if (!isObject(obj) && typeof obj !== 'function') { + return {}; + } - if (this.options.capture) { - val = '(' + val + ')'; - } + var res = {}; + if (typeof keys === 'string') { + if (keys in obj) { + res[keys] = obj[keys]; + } + return res; + } - return this.emit(val, node); - }) + var len = keys.length; + var idx = -1; - /** - * Star: "*" - */ + while (++idx < len) { + var key = keys[idx]; + if (key in obj) { + res[key] = obj[key]; + } + } + return res; +}; - .set('star', function(node, nodes, i) { - var prior = nodes[i - 2] || {}; - var prev = this.prev(); - var next = this.next(); - var type = prev.type; - function isStart(n) { - return n.type === 'bos' || n.type === 'slash'; - } +/***/ }), +/* 708 */ +/***/ (function(module, exports, __webpack_require__) { - if (this.output === '' && this.options.contains !== true) { - this.output = '(?![' + slash() + '])'; - } +"use strict"; - if (type === 'bracket' && this.options.bash === false) { - var str = next && next.type === 'bracket' ? star() : '*?'; - if (!prev.nodes || prev.nodes[1].type !== 'posix') { - return this.emit(str, node); - } - } - var prefix = !this.dotfiles && type !== 'text' && type !== 'escape' - ? (this.options.dot ? '(?!(?:^|[' + slash() + '])\\.{1,2}(?:$|[' + slash() + ']))' : '(?!\\.)') - : ''; +/** + * Module dependencies + */ - if (isStart(prev) || (isStart(prior) && type === 'not')) { - if (prefix !== '(?!\\.)') { - prefix += '(?!(\\.{2}|\\.[' + slash() + ']))(?=.)'; - } else { - prefix += '(?=.)'; - } - } else if (prefix === '(?!\\.)') { - prefix = ''; - } +var extend = __webpack_require__(638); +var unique = __webpack_require__(599); +var toRegex = __webpack_require__(579); - if (prev.type === 'not' && prior.type === 'bos' && this.options.dot === true) { - this.output = '(?!\\.)' + this.output; - } +/** + * Local dependencies + */ - var output = prefix + star(); - if (this.options.capture) { - output = '(' + output + ')'; - } +var compilers = __webpack_require__(709); +var parsers = __webpack_require__(715); +var Extglob = __webpack_require__(718); +var utils = __webpack_require__(717); +var MAX_LENGTH = 1024 * 64; - return this.emit(output, node); - }) +/** + * Convert the given `extglob` pattern into a regex-compatible string. Returns + * an object with the compiled result and the parsed AST. + * + * ```js + * var extglob = require('extglob'); + * console.log(extglob('*.!(*a)')); + * //=> '(?!\\.)[^/]*?\\.(?!(?!\\.)[^/]*?a\\b).*?' + * ``` + * @param {String} `pattern` + * @param {Object} `options` + * @return {String} + * @api public + */ - /** - * Text - */ +function extglob(pattern, options) { + return extglob.create(pattern, options).output; +} - .set('text', function(node) { - return this.emit(node.val, node); - }) +/** + * Takes an array of strings and an extglob pattern and returns a new + * array that contains only the strings that match the pattern. + * + * ```js + * var extglob = require('extglob'); + * console.log(extglob.match(['a.a', 'a.b', 'a.c'], '*.!(*a)')); + * //=> ['a.b', 'a.c'] + * ``` + * @param {Array} `list` Array of strings to match + * @param {String} `pattern` Extglob pattern + * @param {Object} `options` + * @return {Array} Returns an array of matches + * @api public + */ - /** - * End-of-string - */ +extglob.match = function(list, pattern, options) { + if (typeof pattern !== 'string') { + throw new TypeError('expected pattern to be a string'); + } - .set('eos', function(node) { - var prev = this.prev(); - var val = node.val; + list = utils.arrayify(list); + var isMatch = extglob.matcher(pattern, options); + var len = list.length; + var idx = -1; + var matches = []; - this.output = '(?:\\.[' + slash() + '](?=.))?' + this.output; - if (this.state.metachar && prev.type !== 'qmark' && prev.type !== 'slash') { - val += (this.options.contains ? '[' + slash() + ']?' : '(?:[' + slash() + ']|$)'); - } + while (++idx < len) { + var ele = list[idx]; - return this.emit(val, node); - }); + if (isMatch(ele)) { + matches.push(ele); + } + } - /** - * Allow custom compilers to be passed on options - */ + // if no options were passed, uniquify results and return + if (typeof options === 'undefined') { + return unique(matches); + } - if (options && typeof options.compilers === 'function') { - options.compilers(nanomatch.compiler); + if (matches.length === 0) { + if (options.failglob === true) { + throw new Error('no matches found for "' + pattern + '"'); + } + if (options.nonull === true || options.nullglob === true) { + return [pattern.split('\\').join('')]; + } } + + return options.nodupes !== false ? unique(matches) : matches; }; +/** + * Returns true if the specified `string` matches the given + * extglob `pattern`. + * + * ```js + * var extglob = require('extglob'); + * + * console.log(extglob.isMatch('a.a', '*.!(*a)')); + * //=> false + * console.log(extglob.isMatch('a.b', '*.!(*a)')); + * //=> true + * ``` + * @param {String} `string` String to match + * @param {String} `pattern` Extglob pattern + * @param {String} `options` + * @return {Boolean} + * @api public + */ +extglob.isMatch = function(str, pattern, options) { + if (typeof pattern !== 'string') { + throw new TypeError('expected pattern to be a string'); + } -/***/ }), -/* 692 */ -/***/ (function(module, exports, __webpack_require__) { + if (typeof str !== 'string') { + throw new TypeError('expected a string'); + } -"use strict"; + if (pattern === str) { + return true; + } + if (pattern === '' || pattern === ' ' || pattern === '.') { + return pattern === str; + } -var regexNot = __webpack_require__(590); -var toRegex = __webpack_require__(573); -var isOdd = __webpack_require__(693); + var isMatch = utils.memoize('isMatch', pattern, options, extglob.matcher); + return isMatch(str); +}; /** - * Characters to use in negation regex (we want to "not" match - * characters that are matched by other parsers) + * Returns true if the given `string` contains the given pattern. Similar to `.isMatch` but + * the pattern can match any part of the string. + * + * ```js + * var extglob = require('extglob'); + * console.log(extglob.contains('aa/bb/cc', '*b')); + * //=> true + * console.log(extglob.contains('aa/bb/cc', '*d')); + * //=> false + * ``` + * @param {String} `str` The string to match. + * @param {String} `pattern` Glob pattern to use for matching. + * @param {Object} `options` + * @return {Boolean} Returns true if the patter matches any part of `str`. + * @api public */ -var cached; -var NOT_REGEX = '[\\[!*+?$^"\'.\\\\/]+'; -var not = createTextRegex(NOT_REGEX); +extglob.contains = function(str, pattern, options) { + if (typeof str !== 'string') { + throw new TypeError('expected a string'); + } + + if (pattern === '' || pattern === ' ' || pattern === '.') { + return pattern === str; + } + + var opts = extend({}, options, {contains: true}); + opts.strictClose = false; + opts.strictOpen = false; + return extglob.isMatch(str, pattern, opts); +}; /** - * Nanomatch parsers + * Takes an extglob pattern and returns a matcher function. The returned + * function takes the string to match as its only argument. + * + * ```js + * var extglob = require('extglob'); + * var isMatch = extglob.matcher('*.!(*a)'); + * + * console.log(isMatch('a.a')); + * //=> false + * console.log(isMatch('a.b')); + * //=> true + * ``` + * @param {String} `pattern` Extglob pattern + * @param {String} `options` + * @return {Boolean} + * @api public */ -module.exports = function(nanomatch, options) { - var parser = nanomatch.parser; - var opts = parser.options; - - parser.state = { - slashes: 0, - paths: [] - }; +extglob.matcher = function(pattern, options) { + if (typeof pattern !== 'string') { + throw new TypeError('expected pattern to be a string'); + } - parser.ast.state = parser.state; - parser + function matcher() { + var re = extglob.makeRe(pattern, options); + return function(str) { + return re.test(str); + }; + } - /** - * Beginning-of-string - */ + return utils.memoize('matcher', pattern, options, matcher); +}; - .capture('prefix', function() { - if (this.parsed) return; - var m = this.match(/^\.[\\/]/); - if (!m) return; - this.state.strictOpen = !!this.options.strictOpen; - this.state.addPrefix = true; - }) +/** + * Convert the given `extglob` pattern into a regex-compatible string. Returns + * an object with the compiled result and the parsed AST. + * + * ```js + * var extglob = require('extglob'); + * console.log(extglob.create('*.!(*a)').output); + * //=> '(?!\\.)[^/]*?\\.(?!(?!\\.)[^/]*?a\\b).*?' + * ``` + * @param {String} `str` + * @param {Object} `options` + * @return {String} + * @api public + */ - /** - * Escape: "\\." - */ +extglob.create = function(pattern, options) { + if (typeof pattern !== 'string') { + throw new TypeError('expected pattern to be a string'); + } - .capture('escape', function() { - if (this.isInside('bracket')) return; - var pos = this.position(); - var m = this.match(/^(?:\\(.)|([$^]))/); - if (!m) return; + function create() { + var ext = new Extglob(options); + var ast = ext.parse(pattern, options); + return ext.compile(ast, options); + } - return pos({ - type: 'escape', - val: m[2] || m[1] - }); - }) + return utils.memoize('create', pattern, options, create); +}; - /** - * Quoted strings - */ +/** + * Returns an array of matches captured by `pattern` in `string`, or `null` + * if the pattern did not match. + * + * ```js + * var extglob = require('extglob'); + * extglob.capture(pattern, string[, options]); + * + * console.log(extglob.capture('test/*.js', 'test/foo.js')); + * //=> ['foo'] + * console.log(extglob.capture('test/*.js', 'foo/bar.css')); + * //=> null + * ``` + * @param {String} `pattern` Glob pattern to use for matching. + * @param {String} `string` String to match + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Boolean} Returns an array of captures if the string matches the glob pattern, otherwise `null`. + * @api public + */ - .capture('quoted', function() { - var pos = this.position(); - var m = this.match(/^["']/); - if (!m) return; +extglob.capture = function(pattern, str, options) { + var re = extglob.makeRe(pattern, extend({capture: true}, options)); - var quote = m[0]; - if (this.input.indexOf(quote) === -1) { - return pos({ - type: 'escape', - val: quote - }); + function match() { + return function(string) { + var match = re.exec(string); + if (!match) { + return null; } - var tok = advanceTo(this.input, quote); - this.consume(tok.len); + return match.slice(1); + }; + } - return pos({ - type: 'quoted', - val: tok.esc - }); - }) + var capture = utils.memoize('capture', pattern, options, match); + return capture(str); +}; - /** - * Negations: "!" - */ +/** + * Create a regular expression from the given `pattern` and `options`. + * + * ```js + * var extglob = require('extglob'); + * var re = extglob.makeRe('*.!(*a)'); + * console.log(re); + * //=> /^[^\/]*?\.(?![^\/]*?a)[^\/]*?$/ + * ``` + * @param {String} `pattern` The pattern to convert to regex. + * @param {Object} `options` + * @return {RegExp} + * @api public + */ - .capture('not', function() { - var parsed = this.parsed; - var pos = this.position(); - var m = this.match(this.notRegex || /^!+/); - if (!m) return; - var val = m[0]; +extglob.makeRe = function(pattern, options) { + if (pattern instanceof RegExp) { + return pattern; + } - var isNegated = isOdd(val.length); - if (parsed === '' && !isNegated) { - val = ''; - } + if (typeof pattern !== 'string') { + throw new TypeError('expected pattern to be a string'); + } - // if nothing has been parsed, we know `!` is at the start, - // so we need to wrap the result in a negation regex - if (parsed === '' && isNegated && this.options.nonegate !== true) { - this.bos.val = '(?!^(?:'; - this.append = ')$).*'; - val = ''; - } - return pos({ - type: 'not', - val: val - }); - }) + if (pattern.length > MAX_LENGTH) { + throw new Error('expected pattern to be less than ' + MAX_LENGTH + ' characters'); + } - /** - * Dot: "." - */ + function makeRe() { + var opts = extend({strictErrors: false}, options); + if (opts.strictErrors === true) opts.strict = true; + var res = extglob.create(pattern, opts); + return toRegex(res.output, opts); + } - .capture('dot', function() { - var parsed = this.parsed; - var pos = this.position(); - var m = this.match(/^\.+/); - if (!m) return; + var regex = utils.memoize('makeRe', pattern, options, makeRe); + if (regex.source.length > MAX_LENGTH) { + throw new SyntaxError('potentially malicious regex detected'); + } - var val = m[0]; - this.state.dot = val === '.' && (parsed === '' || parsed.slice(-1) === '/'); + return regex; +}; - return pos({ - type: 'dot', - dotfiles: this.state.dot, - val: val - }); - }) +/** + * Cache + */ - /** - * Plus: "+" - */ +extglob.cache = utils.cache; +extglob.clearCache = function() { + extglob.cache.__data__ = {}; +}; - .capture('plus', /^\+(?!\()/) +/** + * Expose `Extglob` constructor, parsers and compilers + */ - /** - * Question mark: "?" - */ +extglob.Extglob = Extglob; +extglob.compilers = compilers; +extglob.parsers = parsers; - .capture('qmark', function() { - var parsed = this.parsed; - var pos = this.position(); - var m = this.match(/^\?+(?!\()/); - if (!m) return; +/** + * Expose `extglob` + * @type {Function} + */ - this.state.metachar = true; - this.state.qmark = true; +module.exports = extglob; - return pos({ - type: 'qmark', - parsed: parsed, - val: m[0] - }); - }) - /** - * Globstar: "**" - */ +/***/ }), +/* 709 */ +/***/ (function(module, exports, __webpack_require__) { - .capture('globstar', function() { - var parsed = this.parsed; - var pos = this.position(); - var m = this.match(/^\*{2}(?![*(])(?=[,)/]|$)/); - if (!m) return; +"use strict"; - var type = opts.noglobstar !== true ? 'globstar' : 'star'; - var node = pos({type: type, parsed: parsed}); - this.state.metachar = true; - while (this.input.slice(0, 4) === '/**/') { - this.input = this.input.slice(3); - } +var brackets = __webpack_require__(710); - node.isInside = { - brace: this.isInside('brace'), - paren: this.isInside('paren') - }; +/** + * Extglob compilers + */ - if (type === 'globstar') { - this.state.globstar = true; - node.val = '**'; +module.exports = function(extglob) { + function star() { + if (typeof extglob.options.star === 'function') { + return extglob.options.star.apply(this, arguments); + } + if (typeof extglob.options.star === 'string') { + return extglob.options.star; + } + return '.*?'; + } - } else { - this.state.star = true; - node.val = '*'; - } + /** + * Use `expand-brackets` compilers + */ - return node; - }) + extglob.use(brackets.compilers); + extglob.compiler /** - * Star: "*" + * Escaped: "\\*" */ - .capture('star', function() { - var pos = this.position(); - var starRe = /^(?:\*(?![*(])|[*]{3,}(?!\()|[*]{2}(?![(/]|$)|\*(?=\*\())/; - var m = this.match(starRe); - if (!m) return; - - this.state.metachar = true; - this.state.star = true; - return pos({ - type: 'star', - val: m[0] - }); + .set('escape', function(node) { + return this.emit(node.val, node); }) /** - * Slash: "/" + * Dot: "." */ - .capture('slash', function() { - var pos = this.position(); - var m = this.match(/^\//); - if (!m) return; - - this.state.slashes++; - return pos({ - type: 'slash', - val: m[0] - }); + .set('dot', function(node) { + return this.emit('\\' + node.val, node); }) /** - * Backslash: "\\" + * Question mark: "?" */ - .capture('backslash', function() { - var pos = this.position(); - var m = this.match(/^\\(?![*+?(){}[\]'"])/); - if (!m) return; + .set('qmark', function(node) { + var val = '[^\\\\/.]'; + var prev = this.prev(); - var val = m[0]; + if (node.parsed.slice(-1) === '(') { + var ch = node.rest.charAt(0); + if (ch !== '!' && ch !== '=' && ch !== ':') { + return this.emit(val, node); + } + return this.emit(node.val, node); + } - if (this.isInside('bracket')) { - val = '\\'; - } else if (val.length > 1) { - val = '\\\\'; + if (prev.type === 'text' && prev.val) { + return this.emit(val, node); } - return pos({ - type: 'backslash', - val: val - }); + if (node.val.length > 1) { + val += '{' + node.val.length + '}'; + } + return this.emit(val, node); }) /** - * Square: "[.]" + * Plus: "+" */ - .capture('square', function() { - if (this.isInside('bracket')) return; - var pos = this.position(); - var m = this.match(/^\[([^!^\\])\]/); - if (!m) return; + .set('plus', function(node) { + var prev = node.parsed.slice(-1); + if (prev === ']' || prev === ')') { + return this.emit(node.val, node); + } + var ch = this.output.slice(-1); + if (!this.output || (/[?*+]/.test(ch) && node.parent.type !== 'bracket')) { + return this.emit('\\+', node); + } + if (/\w/.test(ch) && !node.inside) { + return this.emit('+\\+?', node); + } + return this.emit('+', node); + }) - return pos({ - type: 'square', - val: m[1] - }); + /** + * Star: "*" + */ + + .set('star', function(node) { + var prev = this.prev(); + var prefix = prev.type !== 'text' && prev.type !== 'escape' + ? '(?!\\.)' + : ''; + + return this.emit(prefix + star.call(this, node), node); }) /** - * Brackets: "[...]" (basic, this can be overridden by other parsers) + * Parens */ - .capture('bracket', function() { - var pos = this.position(); - var m = this.match(/^(?:\[([!^]?)([^\]]+|\]-)(\]|[^*+?]+)|\[)/); - if (!m) return; + .set('paren', function(node) { + return this.mapVisit(node.nodes); + }) + .set('paren.open', function(node) { + var capture = this.options.capture ? '(' : ''; - var val = m[0]; - var negated = m[1] ? '^' : ''; - var inner = (m[2] || '').replace(/\\\\+/, '\\\\'); - var close = m[3] || ''; + switch (node.parent.prefix) { + case '!': + case '^': + return this.emit(capture + '(?:(?!(?:', node); + case '*': + case '+': + case '?': + case '@': + return this.emit(capture + '(?:', node); + default: { + var val = node.val; + if (this.options.bash === true) { + val = '\\' + val; + } else if (!this.options.capture && val === '(' && node.parent.rest[0] !== '?') { + val += '?:'; + } - if (m[2] && inner.length < m[2].length) { - val = val.replace(/\\\\+/, '\\\\'); + return this.emit(val, node); + } } + }) + .set('paren.close', function(node) { + var capture = this.options.capture ? ')' : ''; - var esc = this.input.slice(0, 2); - if (inner === '' && esc === '\\]') { - inner += esc; - this.consume(2); - - var str = this.input; - var idx = -1; - var ch; + switch (node.prefix) { + case '!': + case '^': + var prefix = /^(\)|$)/.test(node.rest) ? '$' : ''; + var str = star.call(this, node); - while ((ch = str[++idx])) { - this.consume(1); - if (ch === ']') { - close = ch; - break; + // if the extglob has a slash explicitly defined, we know the user wants + // to match slashes, so we need to ensure the "star" regex allows for it + if (node.parent.hasSlash && !this.options.star && this.options.slash !== false) { + str = '.*?'; } - inner += ch; + + return this.emit(prefix + ('))' + str + ')') + capture, node); + case '*': + case '+': + case '?': + return this.emit(')' + node.prefix + capture, node); + case '@': + return this.emit(')' + capture, node); + default: { + var val = (this.options.bash === true ? '\\' : '') + ')'; + return this.emit(val, node); } } - - return pos({ - type: 'bracket', - val: val, - escaped: close !== ']', - negated: negated, - inner: inner, - close: close - }); }) /** * Text */ - .capture('text', function() { - if (this.isInside('bracket')) return; - var pos = this.position(); - var m = this.match(not); - if (!m || !m[0]) return; - - return pos({ - type: 'text', - val: m[0] - }); + .set('text', function(node) { + var val = node.val.replace(/[\[\]]/g, '\\$&'); + return this.emit(val, node); }); - - /** - * Allow custom parsers to be passed on options - */ - - if (options && typeof options.parsers === 'function') { - options.parsers(nanomatch.parser); - } }; -/** - * Advance to the next non-escaped character - */ - -function advanceTo(input, endChar) { - var ch = input.charAt(0); - var tok = { len: 1, val: '', esc: '' }; - var idx = 0; - - function advance() { - if (ch !== '\\') { - tok.esc += '\\' + ch; - tok.val += ch; - } - ch = input.charAt(++idx); - tok.len++; +/***/ }), +/* 710 */ +/***/ (function(module, exports, __webpack_require__) { - if (ch === '\\') { - advance(); - advance(); - } - } +"use strict"; - while (ch && ch !== endChar) { - advance(); - } - return tok; -} /** - * Create text regex + * Local dependencies */ -function createTextRegex(pattern) { - if (cached) return cached; - var opts = {contains: true, strictClose: false}; - var not = regexNot.create(pattern, opts); - var re = toRegex('^(?:[*]\\((?=.)|' + not + ')', opts); - return (cached = re); -} +var compilers = __webpack_require__(711); +var parsers = __webpack_require__(713); /** - * Expose negation string + * Module dependencies */ -module.exports.not = NOT_REGEX; +var debug = __webpack_require__(205)('expand-brackets'); +var extend = __webpack_require__(638); +var Snapdragon = __webpack_require__(623); +var toRegex = __webpack_require__(579); +/** + * Parses the given POSIX character class `pattern` and returns a + * string that can be used for creating regular expressions for matching. + * + * @param {String} `pattern` + * @param {Object} `options` + * @return {Object} + * @api public + */ -/***/ }), -/* 693 */ -/***/ (function(module, exports, __webpack_require__) { +function brackets(pattern, options) { + debug('initializing from <%s>', __filename); + var res = brackets.create(pattern, options); + return res.output; +} -"use strict"; -/*! - * is-odd +/** + * Takes an array of strings and a POSIX character class pattern, and returns a new + * array with only the strings that matched the pattern. * - * Copyright (c) 2015-2017, Jon Schlinkert. - * Released under the MIT License. + * ```js + * var brackets = require('expand-brackets'); + * console.log(brackets.match(['1', 'a', 'ab'], '[[:alpha:]]')); + * //=> ['a'] + * + * console.log(brackets.match(['1', 'a', 'ab'], '[[:alpha:]]+')); + * //=> ['a', 'ab'] + * ``` + * @param {Array} `arr` Array of strings to match + * @param {String} `pattern` POSIX character class pattern(s) + * @param {Object} `options` + * @return {Array} + * @api public */ +brackets.match = function(arr, pattern, options) { + arr = [].concat(arr); + var opts = extend({}, options); + var isMatch = brackets.matcher(pattern, opts); + var len = arr.length; + var idx = -1; + var res = []; + while (++idx < len) { + var ele = arr[idx]; + if (isMatch(ele)) { + res.push(ele); + } + } -var isNumber = __webpack_require__(694); + if (res.length === 0) { + if (opts.failglob === true) { + throw new Error('no matches found for "' + pattern + '"'); + } -module.exports = function isOdd(i) { - if (!isNumber(i)) { - throw new TypeError('is-odd expects a number.'); - } - if (Number(i) !== Math.floor(i)) { - throw new RangeError('is-odd expects an integer.'); + if (opts.nonull === true || opts.nullglob === true) { + return [pattern.split('\\').join('')]; + } } - return !!(~~i & 1); + return res; }; - -/***/ }), -/* 694 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/*! - * is-number +/** + * Returns true if the specified `string` matches the given + * brackets `pattern`. * - * Copyright (c) 2014-2017, Jon Schlinkert. - * Released under the MIT License. + * ```js + * var brackets = require('expand-brackets'); + * + * console.log(brackets.isMatch('a.a', '[[:alpha:]].[[:alpha:]]')); + * //=> true + * console.log(brackets.isMatch('1.2', '[[:alpha:]].[[:alpha:]]')); + * //=> false + * ``` + * @param {String} `string` String to match + * @param {String} `pattern` Poxis pattern + * @param {String} `options` + * @return {Boolean} + * @api public */ - - -module.exports = function isNumber(num) { - var type = typeof num; - - if (type === 'string' || num instanceof String) { - // an empty string would be coerced to true with the below logic - if (!num.trim()) return false; - } else if (type !== 'number' && !(num instanceof Number)) { - return false; - } - - return (num - num + 1) >= 0; +brackets.isMatch = function(str, pattern, options) { + return brackets.matcher(pattern, options)(str); }; - -/***/ }), -/* 695 */ -/***/ (function(module, exports, __webpack_require__) { - -module.exports = new (__webpack_require__(696))(); - - -/***/ }), -/* 696 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/*! - * fragment-cache +/** + * Takes a POSIX character class pattern and returns a matcher function. The returned + * function takes the string to match as its only argument. * - * Copyright (c) 2016-2017, Jon Schlinkert. - * Released under the MIT License. + * ```js + * var brackets = require('expand-brackets'); + * var isMatch = brackets.matcher('[[:lower:]].[[:upper:]]'); + * + * console.log(isMatch('a.a')); + * //=> false + * console.log(isMatch('a.A')); + * //=> true + * ``` + * @param {String} `pattern` Poxis pattern + * @param {String} `options` + * @return {Boolean} + * @api public */ - - -var MapCache = __webpack_require__(683); +brackets.matcher = function(pattern, options) { + var re = brackets.makeRe(pattern, options); + return function(str) { + return re.test(str); + }; +}; /** - * Create a new `FragmentCache` with an optional object to use for `caches`. + * Create a regular expression from the given `pattern`. * * ```js - * var fragment = new FragmentCache(); + * var brackets = require('expand-brackets'); + * var re = brackets.makeRe('[[:alpha:]]'); + * console.log(re); + * //=> /^(?:[a-zA-Z])$/ * ``` - * @name FragmentCache - * @param {String} `cacheName` - * @return {Object} Returns the [map-cache][] instance. + * @param {String} `pattern` The pattern to convert to regex. + * @param {Object} `options` + * @return {RegExp} * @api public */ -function FragmentCache(caches) { - this.caches = caches || {}; -} +brackets.makeRe = function(pattern, options) { + var res = brackets.create(pattern, options); + var opts = extend({strictErrors: false}, options); + return toRegex(res.output, opts); +}; /** - * Prototype + * Parses the given POSIX character class `pattern` and returns an object + * with the compiled `output` and optional source `map`. + * + * ```js + * var brackets = require('expand-brackets'); + * console.log(brackets('[[:alpha:]]')); + * // { options: { source: 'string' }, + * // input: '[[:alpha:]]', + * // state: {}, + * // compilers: + * // { eos: [Function], + * // noop: [Function], + * // bos: [Function], + * // not: [Function], + * // escape: [Function], + * // text: [Function], + * // posix: [Function], + * // bracket: [Function], + * // 'bracket.open': [Function], + * // 'bracket.inner': [Function], + * // 'bracket.literal': [Function], + * // 'bracket.close': [Function] }, + * // output: '[a-zA-Z]', + * // ast: + * // { type: 'root', + * // errors: [], + * // nodes: [ [Object], [Object], [Object] ] }, + * // parsingErrors: [] } + * ``` + * @param {String} `pattern` + * @param {Object} `options` + * @return {Object} + * @api public */ -FragmentCache.prototype = { - - /** - * Get cache `name` from the `fragment.caches` object. Creates a new - * `MapCache` if it doesn't already exist. - * - * ```js - * var cache = fragment.cache('files'); - * console.log(fragment.caches.hasOwnProperty('files')); - * //=> true - * ``` - * @name .cache - * @param {String} `cacheName` - * @return {Object} Returns the [map-cache][] instance. - * @api public - */ - - cache: function(cacheName) { - return this.caches[cacheName] || (this.caches[cacheName] = new MapCache()); - }, - - /** - * Set a value for property `key` on cache `name` - * - * ```js - * fragment.set('files', 'somefile.js', new File({path: 'somefile.js'})); - * ``` - * @name .set - * @param {String} `name` - * @param {String} `key` Property name to set - * @param {any} `val` The value of `key` - * @return {Object} The cache instance for chaining - * @api public - */ - - set: function(cacheName, key, val) { - var cache = this.cache(cacheName); - cache.set(key, val); - return cache; - }, - - /** - * Returns true if a non-undefined value is set for `key` on fragment cache `name`. - * - * ```js - * var cache = fragment.cache('files'); - * cache.set('somefile.js'); - * - * console.log(cache.has('somefile.js')); - * //=> true - * - * console.log(cache.has('some-other-file.js')); - * //=> false - * ``` - * @name .has - * @param {String} `name` Cache name - * @param {String} `key` Optionally specify a property to check for on cache `name` - * @return {Boolean} - * @api public - */ - - has: function(cacheName, key) { - return typeof this.get(cacheName, key) !== 'undefined'; - }, - - /** - * Get `name`, or if specified, the value of `key`. Invokes the [cache]() method, - * so that cache `name` will be created it doesn't already exist. If `key` is not passed, - * the entire cache (`name`) is returned. - * - * ```js - * var Vinyl = require('vinyl'); - * var cache = fragment.cache('files'); - * cache.set('somefile.js', new Vinyl({path: 'somefile.js'})); - * console.log(cache.get('somefile.js')); - * //=> - * ``` - * @name .get - * @param {String} `name` - * @return {Object} Returns cache `name`, or the value of `key` if specified - * @api public - */ +brackets.create = function(pattern, options) { + var snapdragon = (options && options.snapdragon) || new Snapdragon(options); + compilers(snapdragon); + parsers(snapdragon); - get: function(name, key) { - var cache = this.cache(name); - if (typeof key === 'string') { - return cache.get(key); - } - return cache; - } + var ast = snapdragon.parse(pattern, options); + ast.input = pattern; + var res = snapdragon.compile(ast, options); + res.input = pattern; + return res; }; /** - * Expose `FragmentCache` + * Expose `brackets` constructor, parsers and compilers */ -exports = module.exports = FragmentCache; +brackets.compilers = compilers; +brackets.parsers = parsers; + +/** + * Expose `brackets` + * @type {Function} + */ + +module.exports = brackets; /***/ }), -/* 697 */ +/* 711 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var utils = module.exports; -var path = __webpack_require__(4); - -/** - * Module dependencies - */ +var posix = __webpack_require__(712); -var isWindows = __webpack_require__(698)(); -var Snapdragon = __webpack_require__(617); -utils.define = __webpack_require__(699); -utils.diff = __webpack_require__(700); -utils.extend = __webpack_require__(689); -utils.pick = __webpack_require__(701); -utils.typeOf = __webpack_require__(583); -utils.unique = __webpack_require__(593); +module.exports = function(brackets) { + brackets.compiler -/** - * Returns true if the given value is effectively an empty string - */ + /** + * Escaped characters + */ -utils.isEmptyString = function(val) { - return String(val) === '' || String(val) === './'; -}; + .set('escape', function(node) { + return this.emit('\\' + node.val.replace(/^\\/, ''), node); + }) -/** - * Returns true if the platform is windows, or `path.sep` is `\\`. - * This is defined as a function to allow `path.sep` to be set in unit tests, - * or by the user, if there is a reason to do so. - * @return {Boolean} - */ + /** + * Text + */ -utils.isWindows = function() { - return path.sep === '\\' || isWindows === true; -}; + .set('text', function(node) { + return this.emit(node.val.replace(/([{}])/g, '\\$1'), node); + }) -/** - * Return the last element from an array - */ + /** + * POSIX character classes + */ -utils.last = function(arr, n) { - return arr[arr.length - (n || 1)]; -}; + .set('posix', function(node) { + if (node.val === '[::]') { + return this.emit('\\[::\\]', node); + } -/** - * Get the `Snapdragon` instance to use - */ + var val = posix[node.inner]; + if (typeof val === 'undefined') { + val = '[' + node.inner + ']'; + } + return this.emit(val, node); + }) -utils.instantiate = function(ast, options) { - var snapdragon; - // if an instance was created by `.parse`, use that instance - if (utils.typeOf(ast) === 'object' && ast.snapdragon) { - snapdragon = ast.snapdragon; - // if the user supplies an instance on options, use that instance - } else if (utils.typeOf(options) === 'object' && options.snapdragon) { - snapdragon = options.snapdragon; - // create a new instance - } else { - snapdragon = new Snapdragon(options); - } + /** + * Non-posix brackets + */ - utils.define(snapdragon, 'parse', function(str, options) { - var parsed = Snapdragon.prototype.parse.call(this, str, options); - parsed.input = str; + .set('bracket', function(node) { + return this.mapVisit(node.nodes); + }) + .set('bracket.open', function(node) { + return this.emit(node.val, node); + }) + .set('bracket.inner', function(node) { + var inner = node.val; - // escape unmatched brace/bracket/parens - var last = this.parser.stack.pop(); - if (last && this.options.strictErrors !== true) { - var open = last.nodes[0]; - var inner = last.nodes[1]; - if (last.type === 'bracket') { - if (inner.val.charAt(0) === '[') { - inner.val = '\\' + inner.val; - } + if (inner === '[' || inner === ']') { + return this.emit('\\' + node.val, node); + } + if (inner === '^]') { + return this.emit('^\\]', node); + } + if (inner === '^') { + return this.emit('^', node); + } - } else { - open.val = '\\' + open.val; - var sibling = open.parent.nodes[1]; - if (sibling.type === 'star') { - sibling.loose = true; - } + if (/-/.test(inner) && !/(\d-\d|\w-\w)/.test(inner)) { + inner = inner.split('-').join('\\-'); } - } - // add non-enumerable parser reference - utils.define(parsed, 'parser', this.parser); - return parsed; - }); + var isNegated = inner.charAt(0) === '^'; + // add slashes to negated brackets, per spec + if (isNegated && inner.indexOf('/') === -1) { + inner += '/'; + } + if (isNegated && inner.indexOf('.') === -1) { + inner += '.'; + } - return snapdragon; + // don't unescape `0` (octal literal) + inner = inner.replace(/\\([1-9])/g, '$1'); + return this.emit(inner, node); + }) + .set('bracket.close', function(node) { + var val = node.val.replace(/^\\/, ''); + if (node.parent.escaped === true) { + return this.emit('\\' + val, node); + } + return this.emit(val, node); + }); }; -/** - * Create the key to use for memoization. The key is generated - * by iterating over the options and concatenating key-value pairs - * to the pattern string. - */ -utils.createKey = function(pattern, options) { - if (typeof options === 'undefined') { - return pattern; - } - var key = pattern; - for (var prop in options) { - if (options.hasOwnProperty(prop)) { - key += ';' + prop + '=' + String(options[prop]); - } - } - return key; -}; +/***/ }), +/* 712 */ +/***/ (function(module, exports, __webpack_require__) { -/** - * Cast `val` to an array - * @return {Array} - */ +"use strict"; -utils.arrayify = function(val) { - if (typeof val === 'string') return [val]; - return val ? (Array.isArray(val) ? val : [val]) : []; -}; /** - * Return true if `val` is a non-empty string + * POSIX character classes */ -utils.isString = function(val) { - return typeof val === 'string'; +module.exports = { + alnum: 'a-zA-Z0-9', + alpha: 'a-zA-Z', + ascii: '\\x00-\\x7F', + blank: ' \\t', + cntrl: '\\x00-\\x1F\\x7F', + digit: '0-9', + graph: '\\x21-\\x7E', + lower: 'a-z', + print: '\\x20-\\x7E ', + punct: '\\-!"#$%&\'()\\*+,./:;<=>?@[\\]^_`{|}~', + space: ' \\t\\r\\n\\v\\f', + upper: 'A-Z', + word: 'A-Za-z0-9_', + xdigit: 'A-Fa-f0-9' }; -/** - * Return true if `val` is a non-empty string - */ -utils.isRegex = function(val) { - return utils.typeOf(val) === 'regexp'; -}; +/***/ }), +/* 713 */ +/***/ (function(module, exports, __webpack_require__) { -/** - * Return true if `val` is a non-empty string - */ +"use strict"; -utils.isObject = function(val) { - return utils.typeOf(val) === 'object'; -}; + +var utils = __webpack_require__(714); +var define = __webpack_require__(654); /** - * Escape regex characters in the given string + * Text regex */ -utils.escapeRegex = function(str) { - return str.replace(/[-[\]{}()^$|*+?.\\/\s]/g, '\\$&'); -}; +var TEXT_REGEX = '(\\[(?=.*\\])|\\])+'; +var not = utils.createRegex(TEXT_REGEX); /** - * Combines duplicate characters in the provided `input` string. - * @param {String} `input` - * @returns {String} + * Brackets parsers */ -utils.combineDupes = function(input, patterns) { - patterns = utils.arrayify(patterns).join('|').split('|'); - patterns = patterns.map(function(s) { - return s.replace(/\\?([+*\\/])/g, '\\$1'); - }); - var substr = patterns.join('|'); - var regex = new RegExp('(' + substr + ')(?=\\1)', 'g'); - return input.replace(regex, ''); -}; +function parsers(brackets) { + brackets.state = brackets.state || {}; + brackets.parser.sets.bracket = brackets.parser.sets.bracket || []; + brackets.parser -/** - * Returns true if the given `str` has special characters - */ + .capture('escape', function() { + if (this.isInside('bracket')) return; + var pos = this.position(); + var m = this.match(/^\\(.)/); + if (!m) return; -utils.hasSpecialChars = function(str) { - return /(?:(?:(^|\/)[!.])|[*?+()|[\]{}]|[+@]\()/.test(str); -}; + return pos({ + type: 'escape', + val: m[0] + }); + }) -/** - * Normalize slashes in the given filepath. - * - * @param {String} `filepath` - * @return {String} - */ + /** + * Text parser + */ -utils.toPosixPath = function(str) { - return str.replace(/\\+/g, '/'); -}; + .capture('text', function() { + if (this.isInside('bracket')) return; + var pos = this.position(); + var m = this.match(not); + if (!m || !m[0]) return; -/** - * Strip backslashes before special characters in a string. - * - * @param {String} `str` - * @return {String} - */ + return pos({ + type: 'text', + val: m[0] + }); + }) -utils.unescape = function(str) { - return utils.toPosixPath(str.replace(/\\(?=[*+?!.])/g, '')); -}; + /** + * POSIX character classes: "[[:alpha:][:digits:]]" + */ -/** - * Strip the drive letter from a windows filepath - * @param {String} `fp` - * @return {String} - */ + .capture('posix', function() { + var pos = this.position(); + var m = this.match(/^\[:(.*?):\](?=.*\])/); + if (!m) return; -utils.stripDrive = function(fp) { - return utils.isWindows() ? fp.replace(/^[a-z]:[\\/]+?/i, '/') : fp; -}; + var inside = this.isInside('bracket'); + if (inside) { + brackets.posix++; + } -/** - * Strip the prefix from a filepath - * @param {String} `fp` - * @return {String} - */ + return pos({ + type: 'posix', + insideBracket: inside, + inner: m[1], + val: m[0] + }); + }) -utils.stripPrefix = function(str) { - if (str.charAt(0) === '.' && (str.charAt(1) === '/' || str.charAt(1) === '\\')) { - return str.slice(2); - } - return str; -}; + /** + * Bracket (noop) + */ -/** - * Returns true if `str` is a common character that doesn't need - * to be processed to be used for matching. - * @param {String} `str` - * @return {Boolean} - */ + .capture('bracket', function() {}) -utils.isSimpleChar = function(str) { - return str.trim() === '' || str === '.'; -}; + /** + * Open: '[' + */ -/** - * Returns true if the given str is an escaped or - * unescaped path character - */ + .capture('bracket.open', function() { + var parsed = this.parsed; + var pos = this.position(); + var m = this.match(/^\[(?=.*\])/); + if (!m) return; -utils.isSlash = function(str) { - return str === '/' || str === '\\/' || str === '\\' || str === '\\\\'; -}; + var prev = this.prev(); + var last = utils.last(prev.nodes); -/** - * Returns a function that returns true if the given - * pattern matches or contains a `filepath` - * - * @param {String} `pattern` - * @return {Function} - */ + if (parsed.slice(-1) === '\\' && !this.isInside('bracket')) { + last.val = last.val.slice(0, last.val.length - 1); + return pos({ + type: 'escape', + val: m[0] + }); + } -utils.matchPath = function(pattern, options) { - return (options && options.contains) - ? utils.containsPattern(pattern, options) - : utils.equalsPattern(pattern, options); -}; + var open = pos({ + type: 'bracket.open', + val: m[0] + }); -/** - * Returns true if the given (original) filepath or unixified path are equal - * to the given pattern. - */ + if (last.type === 'bracket.open' || this.isInside('bracket')) { + open.val = '\\' + open.val; + open.type = 'bracket.inner'; + open.escaped = true; + return open; + } -utils._equals = function(filepath, unixPath, pattern) { - return pattern === filepath || pattern === unixPath; -}; + var node = pos({ + type: 'bracket', + nodes: [open] + }); -/** - * Returns true if the given (original) filepath or unixified path contain - * the given pattern. - */ + define(node, 'parent', prev); + define(open, 'parent', node); + this.push('bracket', node); + prev.nodes.push(node); + }) -utils._contains = function(filepath, unixPath, pattern) { - return filepath.indexOf(pattern) !== -1 || unixPath.indexOf(pattern) !== -1; -}; + /** + * Bracket text + */ -/** - * Returns a function that returns true if the given - * pattern is the same as a given `filepath` - * - * @param {String} `pattern` - * @return {Function} - */ + .capture('bracket.inner', function() { + if (!this.isInside('bracket')) return; + var pos = this.position(); + var m = this.match(not); + if (!m || !m[0]) return; -utils.equalsPattern = function(pattern, options) { - var unixify = utils.unixify(options); - options = options || {}; + var next = this.input.charAt(0); + var val = m[0]; - return function fn(filepath) { - var equal = utils._equals(filepath, unixify(filepath), pattern); - if (equal === true || options.nocase !== true) { - return equal; - } - var lower = filepath.toLowerCase(); - return utils._equals(lower, unixify(lower), pattern); - }; -}; + var node = pos({ + type: 'bracket.inner', + val: val + }); -/** - * Returns a function that returns true if the given - * pattern contains a `filepath` - * - * @param {String} `pattern` - * @return {Function} - */ + if (val === '\\\\') { + return node; + } -utils.containsPattern = function(pattern, options) { - var unixify = utils.unixify(options); - options = options || {}; + var first = val.charAt(0); + var last = val.slice(-1); - return function(filepath) { - var contains = utils._contains(filepath, unixify(filepath), pattern); - if (contains === true || options.nocase !== true) { - return contains; - } - var lower = filepath.toLowerCase(); - return utils._contains(lower, unixify(lower), pattern); - }; -}; + if (first === '!') { + val = '^' + val.slice(1); + } -/** - * Returns a function that returns true if the given - * regex matches the `filename` of a file path. - * - * @param {RegExp} `re` Matching regex - * @return {Function} - */ + if (last === '\\' || (val === '^' && next === ']')) { + val += this.input[0]; + this.consume(1); + } -utils.matchBasename = function(re) { - return function(filepath) { - return re.test(filepath) || re.test(path.basename(filepath)); - }; -}; + node.val = val; + return node; + }) + + /** + * Close: ']' + */ + + .capture('bracket.close', function() { + var parsed = this.parsed; + var pos = this.position(); + var m = this.match(/^\]/); + if (!m) return; + + var prev = this.prev(); + var last = utils.last(prev.nodes); + + if (parsed.slice(-1) === '\\' && !this.isInside('bracket')) { + last.val = last.val.slice(0, last.val.length - 1); + + return pos({ + type: 'escape', + val: m[0] + }); + } + + var node = pos({ + type: 'bracket.close', + rest: this.input, + val: m[0] + }); -/** - * Returns the given value unchanced. - * @return {any} - */ + if (last.type === 'bracket.open') { + node.type = 'bracket.inner'; + node.escaped = true; + return node; + } -utils.identity = function(val) { - return val; -}; + var bracket = this.pop('bracket'); + if (!this.isType(bracket, 'bracket')) { + if (this.options.strict) { + throw new Error('missing opening "["'); + } + node.type = 'bracket.inner'; + node.escaped = true; + return node; + } + + bracket.nodes.push(node); + define(node, 'parent', bracket); + }); +} /** - * Determines the filepath to return based on the provided options. - * @return {any} + * Brackets parsers */ -utils.value = function(str, unixify, options) { - if (options && options.unixify === false) { - return str; - } - if (options && typeof options.unixify === 'function') { - return options.unixify(str); - } - return unixify(str); -}; +module.exports = parsers; /** - * Returns a function that normalizes slashes in a string to forward - * slashes, strips `./` from beginning of paths, and optionally unescapes - * special characters. - * @return {Function} + * Expose text regex */ -utils.unixify = function(options) { - var opts = options || {}; - return function(filepath) { - if (opts.stripPrefix !== false) { - filepath = utils.stripPrefix(filepath); - } - if (opts.unescape === true) { - filepath = utils.unescape(filepath); - } - if (opts.unixify === true || utils.isWindows()) { - filepath = utils.toPosixPath(filepath); - } - return filepath; - }; -}; +module.exports.TEXT_REGEX = TEXT_REGEX; /***/ }), -/* 698 */ +/* 714 */ /***/ (function(module, exports, __webpack_require__) { -var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*! - * is-windows - * - * Copyright © 2015-2018, Jon Schlinkert. - * Released under the MIT License. - */ - -(function(factory) { - if (exports && typeof exports === 'object' && typeof module !== 'undefined') { - module.exports = factory(); - } else if (true) { - !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), - __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? - (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), - __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); - } else {} -})(function() { - 'use strict'; - return function isWindows() { - return process && (process.platform === 'win32' || /^(msys|cygwin)$/.test(process.env.OSTYPE)); - }; -}); +"use strict"; -/***/ }), -/* 699 */ -/***/ (function(module, exports, __webpack_require__) { +var toRegex = __webpack_require__(579); +var regexNot = __webpack_require__(596); +var cached; -"use strict"; -/*! - * define-property - * - * Copyright (c) 2015-2018, Jon Schlinkert. - * Released under the MIT License. +/** + * Get the last element from `array` + * @param {Array} `array` + * @return {*} */ +exports.last = function(arr) { + return arr[arr.length - 1]; +}; +/** + * Create and cache regex to use for text nodes + */ -var isobject = __webpack_require__(581); -var isDescriptor = __webpack_require__(582); -var define = (typeof Reflect !== 'undefined' && Reflect.defineProperty) - ? Reflect.defineProperty - : Object.defineProperty; - -module.exports = function defineProperty(obj, key, val) { - if (!isobject(obj) && typeof obj !== 'function' && !Array.isArray(obj)) { - throw new TypeError('expected an object, function, or array'); - } - - if (typeof key !== 'string') { - throw new TypeError('expected "key" to be a string'); - } +exports.createRegex = function(pattern, include) { + if (cached) return cached; + var opts = {contains: true, strictClose: false}; + var not = regexNot.create(pattern, opts); + var re; - if (isDescriptor(val)) { - define(obj, key, val); - return obj; + if (typeof include === 'string') { + re = toRegex('^(?:' + include + '|' + not + ')', opts); + } else { + re = toRegex(not, opts); } - define(obj, key, { - configurable: true, - enumerable: false, - writable: true, - value: val - }); - - return obj; + return (cached = re); }; /***/ }), -/* 700 */ +/* 715 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -/*! - * arr-diff - * - * Copyright (c) 2014-2017, Jon Schlinkert. - * Released under the MIT License. + + +var brackets = __webpack_require__(710); +var define = __webpack_require__(716); +var utils = __webpack_require__(717); + +/** + * Characters to use in text regex (we want to "not" match + * characters that are matched by other parsers) */ +var TEXT_REGEX = '([!@*?+]?\\(|\\)|[*?.+\\\\]|\\[:?(?=.*\\])|:?\\])+'; +var not = utils.createRegex(TEXT_REGEX); +/** + * Extglob parsers + */ -module.exports = function diff(arr/*, arrays*/) { - var len = arguments.length; - var idx = 0; - while (++idx < len) { - arr = diffArray(arr, arguments[idx]); - } - return arr; -}; +function parsers(extglob) { + extglob.state = extglob.state || {}; -function diffArray(one, two) { - if (!Array.isArray(two)) { - return one.slice(); - } + /** + * Use `expand-brackets` parsers + */ - var tlen = two.length - var olen = one.length; - var idx = -1; - var arr = []; + extglob.use(brackets.parsers); + extglob.parser.sets.paren = extglob.parser.sets.paren || []; + extglob.parser - while (++idx < olen) { - var ele = one[idx]; + /** + * Extglob open: "*(" + */ - var hasEle = false; - for (var i = 0; i < tlen; i++) { - var val = two[i]; + .capture('paren.open', function() { + var parsed = this.parsed; + var pos = this.position(); + var m = this.match(/^([!@*?+])?\(/); + if (!m) return; - if (ele === val) { - hasEle = true; - break; - } - } + var prev = this.prev(); + var prefix = m[1]; + var val = m[0]; - if (hasEle === false) { - arr.push(ele); - } - } - return arr; -} + var open = pos({ + type: 'paren.open', + parsed: parsed, + val: val + }); + var node = pos({ + type: 'paren', + prefix: prefix, + nodes: [open] + }); -/***/ }), -/* 701 */ -/***/ (function(module, exports, __webpack_require__) { + // if nested negation extglobs, just cancel them out to simplify + if (prefix === '!' && prev.type === 'paren' && prev.prefix === '!') { + prev.prefix = '@'; + node.prefix = '@'; + } -"use strict"; -/*! - * object.pick - * - * Copyright (c) 2014-2015 Jon Schlinkert, contributors. - * Licensed under the MIT License - */ + define(node, 'rest', this.input); + define(node, 'parsed', parsed); + define(node, 'parent', prev); + define(open, 'parent', node); + this.push('paren', node); + prev.nodes.push(node); + }) + /** + * Extglob close: ")" + */ -var isObject = __webpack_require__(581); + .capture('paren.close', function() { + var parsed = this.parsed; + var pos = this.position(); + var m = this.match(/^\)/); + if (!m) return; -module.exports = function pick(obj, keys) { - if (!isObject(obj) && typeof obj !== 'function') { - return {}; - } + var parent = this.pop('paren'); + var node = pos({ + type: 'paren.close', + rest: this.input, + parsed: parsed, + val: m[0] + }); - var res = {}; - if (typeof keys === 'string') { - if (keys in obj) { - res[keys] = obj[keys]; - } - return res; - } + if (!this.isType(parent, 'paren')) { + if (this.options.strict) { + throw new Error('missing opening paren: "("'); + } + node.escaped = true; + return node; + } - var len = keys.length; - var idx = -1; + node.prefix = parent.prefix; + parent.nodes.push(node); + define(node, 'parent', parent); + }) - while (++idx < len) { - var key = keys[idx]; - if (key in obj) { - res[key] = obj[key]; - } - } - return res; -}; + /** + * Escape: "\\." + */ + .capture('escape', function() { + var pos = this.position(); + var m = this.match(/^\\(.)/); + if (!m) return; -/***/ }), -/* 702 */ -/***/ (function(module, exports, __webpack_require__) { + return pos({ + type: 'escape', + val: m[0], + ch: m[1] + }); + }) -"use strict"; + /** + * Question marks: "?" + */ + .capture('qmark', function() { + var parsed = this.parsed; + var pos = this.position(); + var m = this.match(/^\?+(?!\()/); + if (!m) return; + extglob.state.metachar = true; + return pos({ + type: 'qmark', + rest: this.input, + parsed: parsed, + val: m[0] + }); + }) -/** - * Module dependencies - */ + /** + * Character parsers + */ -var extend = __webpack_require__(632); -var unique = __webpack_require__(593); -var toRegex = __webpack_require__(573); + .capture('star', /^\*(?!\()/) + .capture('plus', /^\+(?!\()/) + .capture('dot', /^\./) + .capture('text', not); +}; /** - * Local dependencies + * Expose text regex string */ -var compilers = __webpack_require__(703); -var parsers = __webpack_require__(709); -var Extglob = __webpack_require__(712); -var utils = __webpack_require__(711); -var MAX_LENGTH = 1024 * 64; +module.exports.TEXT_REGEX = TEXT_REGEX; /** - * Convert the given `extglob` pattern into a regex-compatible string. Returns - * an object with the compiled result and the parsed AST. - * - * ```js - * var extglob = require('extglob'); - * console.log(extglob('*.!(*a)')); - * //=> '(?!\\.)[^/]*?\\.(?!(?!\\.)[^/]*?a\\b).*?' - * ``` - * @param {String} `pattern` - * @param {Object} `options` - * @return {String} - * @api public + * Extglob parsers */ -function extglob(pattern, options) { - return extglob.create(pattern, options).output; -} +module.exports = parsers; -/** - * Takes an array of strings and an extglob pattern and returns a new - * array that contains only the strings that match the pattern. + +/***/ }), +/* 716 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +/*! + * define-property * - * ```js - * var extglob = require('extglob'); - * console.log(extglob.match(['a.a', 'a.b', 'a.c'], '*.!(*a)')); - * //=> ['a.b', 'a.c'] - * ``` - * @param {Array} `list` Array of strings to match - * @param {String} `pattern` Extglob pattern - * @param {Object} `options` - * @return {Array} Returns an array of matches - * @api public + * Copyright (c) 2015, 2017, Jon Schlinkert. + * Released under the MIT License. */ -extglob.match = function(list, pattern, options) { - if (typeof pattern !== 'string') { - throw new TypeError('expected pattern to be a string'); - } - list = utils.arrayify(list); - var isMatch = extglob.matcher(pattern, options); - var len = list.length; - var idx = -1; - var matches = []; - while (++idx < len) { - var ele = list[idx]; +var isDescriptor = __webpack_require__(588); - if (isMatch(ele)) { - matches.push(ele); - } +module.exports = function defineProperty(obj, prop, val) { + if (typeof obj !== 'object' && typeof obj !== 'function') { + throw new TypeError('expected an object or function.'); } - // if no options were passed, uniquify results and return - if (typeof options === 'undefined') { - return unique(matches); + if (typeof prop !== 'string') { + throw new TypeError('expected `prop` to be a string.'); } - if (matches.length === 0) { - if (options.failglob === true) { - throw new Error('no matches found for "' + pattern + '"'); - } - if (options.nonull === true || options.nullglob === true) { - return [pattern.split('\\').join('')]; - } + if (isDescriptor(val) && ('set' in val || 'get' in val)) { + return Object.defineProperty(obj, prop, val); } - return options.nodupes !== false ? unique(matches) : matches; + return Object.defineProperty(obj, prop, { + configurable: true, + enumerable: false, + writable: true, + value: val + }); }; -/** - * Returns true if the specified `string` matches the given - * extglob `pattern`. - * - * ```js - * var extglob = require('extglob'); - * - * console.log(extglob.isMatch('a.a', '*.!(*a)')); - * //=> false - * console.log(extglob.isMatch('a.b', '*.!(*a)')); - * //=> true - * ``` - * @param {String} `string` String to match - * @param {String} `pattern` Extglob pattern - * @param {String} `options` - * @return {Boolean} - * @api public - */ - -extglob.isMatch = function(str, pattern, options) { - if (typeof pattern !== 'string') { - throw new TypeError('expected pattern to be a string'); - } - if (typeof str !== 'string') { - throw new TypeError('expected a string'); - } +/***/ }), +/* 717 */ +/***/ (function(module, exports, __webpack_require__) { - if (pattern === str) { - return true; - } +"use strict"; - if (pattern === '' || pattern === ' ' || pattern === '.') { - return pattern === str; - } - var isMatch = utils.memoize('isMatch', pattern, options, extglob.matcher); - return isMatch(str); -}; +var regex = __webpack_require__(596); +var Cache = __webpack_require__(702); /** - * Returns true if the given `string` contains the given pattern. Similar to `.isMatch` but - * the pattern can match any part of the string. - * - * ```js - * var extglob = require('extglob'); - * console.log(extglob.contains('aa/bb/cc', '*b')); - * //=> true - * console.log(extglob.contains('aa/bb/cc', '*d')); - * //=> false - * ``` - * @param {String} `str` The string to match. - * @param {String} `pattern` Glob pattern to use for matching. - * @param {Object} `options` - * @return {Boolean} Returns true if the patter matches any part of `str`. - * @api public + * Utils */ -extglob.contains = function(str, pattern, options) { - if (typeof str !== 'string') { - throw new TypeError('expected a string'); - } - - if (pattern === '' || pattern === ' ' || pattern === '.') { - return pattern === str; - } - - var opts = extend({}, options, {contains: true}); - opts.strictClose = false; - opts.strictOpen = false; - return extglob.isMatch(str, pattern, opts); -}; +var utils = module.exports; +var cache = utils.cache = new Cache(); /** - * Takes an extglob pattern and returns a matcher function. The returned - * function takes the string to match as its only argument. - * - * ```js - * var extglob = require('extglob'); - * var isMatch = extglob.matcher('*.!(*a)'); - * - * console.log(isMatch('a.a')); - * //=> false - * console.log(isMatch('a.b')); - * //=> true - * ``` - * @param {String} `pattern` Extglob pattern - * @param {String} `options` - * @return {Boolean} - * @api public - */ - -extglob.matcher = function(pattern, options) { - if (typeof pattern !== 'string') { - throw new TypeError('expected pattern to be a string'); - } - - function matcher() { - var re = extglob.makeRe(pattern, options); - return function(str) { - return re.test(str); - }; - } + * Cast `val` to an array + * @return {Array} + */ - return utils.memoize('matcher', pattern, options, matcher); +utils.arrayify = function(val) { + if (!Array.isArray(val)) { + return [val]; + } + return val; }; /** - * Convert the given `extglob` pattern into a regex-compatible string. Returns - * an object with the compiled result and the parsed AST. - * - * ```js - * var extglob = require('extglob'); - * console.log(extglob.create('*.!(*a)').output); - * //=> '(?!\\.)[^/]*?\\.(?!(?!\\.)[^/]*?a\\b).*?' - * ``` - * @param {String} `str` - * @param {Object} `options` - * @return {String} - * @api public + * Memoize a generated regex or function */ -extglob.create = function(pattern, options) { - if (typeof pattern !== 'string') { - throw new TypeError('expected pattern to be a string'); +utils.memoize = function(type, pattern, options, fn) { + var key = utils.createKey(type + pattern, options); + + if (cache.has(type, key)) { + return cache.get(type, key); } - function create() { - var ext = new Extglob(options); - var ast = ext.parse(pattern, options); - return ext.compile(ast, options); + var val = fn(pattern, options); + if (options && options.cache === false) { + return val; } - return utils.memoize('create', pattern, options, create); + cache.set(type, key, val); + return val; }; /** - * Returns an array of matches captured by `pattern` in `string`, or `null` - * if the pattern did not match. - * - * ```js - * var extglob = require('extglob'); - * extglob.capture(pattern, string[, options]); - * - * console.log(extglob.capture('test/*.js', 'test/foo.js')); - * //=> ['foo'] - * console.log(extglob.capture('test/*.js', 'foo/bar.css')); - * //=> null - * ``` - * @param {String} `pattern` Glob pattern to use for matching. - * @param {String} `string` String to match - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Boolean} Returns an array of captures if the string matches the glob pattern, otherwise `null`. - * @api public + * Create the key to use for memoization. The key is generated + * by iterating over the options and concatenating key-value pairs + * to the pattern string. */ -extglob.capture = function(pattern, str, options) { - var re = extglob.makeRe(pattern, extend({capture: true}, options)); - - function match() { - return function(string) { - var match = re.exec(string); - if (!match) { - return null; - } - - return match.slice(1); - }; +utils.createKey = function(pattern, options) { + var key = pattern; + if (typeof options === 'undefined') { + return key; } - - var capture = utils.memoize('capture', pattern, options, match); - return capture(str); + for (var prop in options) { + key += ';' + prop + '=' + String(options[prop]); + } + return key; }; /** - * Create a regular expression from the given `pattern` and `options`. - * - * ```js - * var extglob = require('extglob'); - * var re = extglob.makeRe('*.!(*a)'); - * console.log(re); - * //=> /^[^\/]*?\.(?![^\/]*?a)[^\/]*?$/ - * ``` - * @param {String} `pattern` The pattern to convert to regex. - * @param {Object} `options` - * @return {RegExp} - * @api public + * Create the regex to use for matching text */ -extglob.makeRe = function(pattern, options) { - if (pattern instanceof RegExp) { - return pattern; - } +utils.createRegex = function(str) { + var opts = {contains: true, strictClose: false}; + return regex(str, opts); +}; - if (typeof pattern !== 'string') { - throw new TypeError('expected pattern to be a string'); - } - if (pattern.length > MAX_LENGTH) { - throw new Error('expected pattern to be less than ' + MAX_LENGTH + ' characters'); - } +/***/ }), +/* 718 */ +/***/ (function(module, exports, __webpack_require__) { - function makeRe() { - var opts = extend({strictErrors: false}, options); - if (opts.strictErrors === true) opts.strict = true; - var res = extglob.create(pattern, opts); - return toRegex(res.output, opts); - } +"use strict"; - var regex = utils.memoize('makeRe', pattern, options, makeRe); - if (regex.source.length > MAX_LENGTH) { - throw new SyntaxError('potentially malicious regex detected'); - } - return regex; -}; +/** + * Module dependencies + */ + +var Snapdragon = __webpack_require__(623); +var define = __webpack_require__(716); +var extend = __webpack_require__(638); /** - * Cache + * Local dependencies */ -extglob.cache = utils.cache; -extglob.clearCache = function() { - extglob.cache.__data__ = {}; -}; +var compilers = __webpack_require__(709); +var parsers = __webpack_require__(715); /** - * Expose `Extglob` constructor, parsers and compilers + * Customize Snapdragon parser and renderer */ -extglob.Extglob = Extglob; -extglob.compilers = compilers; -extglob.parsers = parsers; +function Extglob(options) { + this.options = extend({source: 'extglob'}, options); + this.snapdragon = this.options.snapdragon || new Snapdragon(this.options); + this.snapdragon.patterns = this.snapdragon.patterns || {}; + this.compiler = this.snapdragon.compiler; + this.parser = this.snapdragon.parser; + + compilers(this.snapdragon); + parsers(this.snapdragon); + + /** + * Override Snapdragon `.parse` method + */ + + define(this.snapdragon, 'parse', function(str, options) { + var parsed = Snapdragon.prototype.parse.apply(this, arguments); + parsed.input = str; + + // escape unmatched brace/bracket/parens + var last = this.parser.stack.pop(); + if (last && this.options.strict !== true) { + var node = last.nodes[0]; + node.val = '\\' + node.val; + var sibling = node.parent.nodes[1]; + if (sibling.type === 'star') { + sibling.loose = true; + } + } + + // add non-enumerable parser reference + define(parsed, 'parser', this.parser); + return parsed; + }); + + /** + * Decorate `.parse` method + */ + + define(this, 'parse', function(ast, options) { + return this.snapdragon.parse.apply(this.snapdragon, arguments); + }); + + /** + * Decorate `.compile` method + */ + + define(this, 'compile', function(ast, options) { + return this.snapdragon.compile.apply(this.snapdragon, arguments); + }); + +} /** - * Expose `extglob` - * @type {Function} + * Expose `Extglob` */ -module.exports = extglob; +module.exports = Extglob; /***/ }), -/* 703 */ +/* 719 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var brackets = __webpack_require__(704); +var extglob = __webpack_require__(708); +var nanomatch = __webpack_require__(694); +var regexNot = __webpack_require__(596); +var toRegex = __webpack_require__(579); +var not; /** - * Extglob compilers + * Characters to use in negation regex (we want to "not" match + * characters that are matched by other parsers) */ -module.exports = function(extglob) { - function star() { - if (typeof extglob.options.star === 'function') { - return extglob.options.star.apply(this, arguments); - } - if (typeof extglob.options.star === 'string') { - return extglob.options.star; - } - return '.*?'; - } +var TEXT = '([!@*?+]?\\(|\\)|\\[:?(?=.*?:?\\])|:?\\]|[*+?!^$.\\\\/])+'; +var createNotRegex = function(opts) { + return not || (not = textRegex(TEXT)); +}; - /** - * Use `expand-brackets` compilers - */ +/** + * Parsers + */ - extglob.use(brackets.compilers); - extglob.compiler +module.exports = function(snapdragon) { + var parsers = snapdragon.parser.parsers; - /** - * Escaped: "\\*" - */ + // register nanomatch parsers + snapdragon.use(nanomatch.parsers); - .set('escape', function(node) { - return this.emit(node.val, node); - }) + // get references to some specific nanomatch parsers before they + // are overridden by the extglob and/or parsers + var escape = parsers.escape; + var slash = parsers.slash; + var qmark = parsers.qmark; + var plus = parsers.plus; + var star = parsers.star; + var dot = parsers.dot; - /** - * Dot: "." - */ + // register extglob parsers + snapdragon.use(extglob.parsers); - .set('dot', function(node) { - return this.emit('\\' + node.val, node); + // custom micromatch parsers + snapdragon.parser + .use(function() { + // override "notRegex" created in nanomatch parser + this.notRegex = /^\!+(?!\()/; }) + // reset the referenced parsers + .capture('escape', escape) + .capture('slash', slash) + .capture('qmark', qmark) + .capture('star', star) + .capture('plus', plus) + .capture('dot', dot) /** - * Question mark: "?" + * Override `text` parser */ - .set('qmark', function(node) { - var val = '[^\\\\/.]'; - var prev = this.prev(); + .capture('text', function() { + if (this.isInside('bracket')) return; + var pos = this.position(); + var m = this.match(createNotRegex(this.options)); + if (!m || !m[0]) return; - if (node.parsed.slice(-1) === '(') { - var ch = node.rest.charAt(0); - if (ch !== '!' && ch !== '=' && ch !== ':') { - return this.emit(val, node); - } - return this.emit(node.val, node); - } + // escape regex boundary characters and simple brackets + var val = m[0].replace(/([[\]^$])/g, '\\$1'); - if (prev.type === 'text' && prev.val) { - return this.emit(val, node); - } + return pos({ + type: 'text', + val: val + }); + }); +}; - if (node.val.length > 1) { - val += '{' + node.val.length + '}'; - } - return this.emit(val, node); - }) +/** + * Create text regex + */ - /** - * Plus: "+" - */ +function textRegex(pattern) { + var notStr = regexNot.create(pattern, {contains: true, strictClose: false}); + var prefix = '(?:[\\^]|\\\\|'; + return toRegex(prefix + notStr + ')', {strictClose: false}); +} - .set('plus', function(node) { - var prev = node.parsed.slice(-1); - if (prev === ']' || prev === ')') { - return this.emit(node.val, node); - } - var ch = this.output.slice(-1); - if (!this.output || (/[?*+]/.test(ch) && node.parent.type !== 'bracket')) { - return this.emit('\\+', node); - } - if (/\w/.test(ch) && !node.inside) { - return this.emit('+\\+?', node); - } - return this.emit('+', node); - }) - /** - * Star: "*" - */ +/***/ }), +/* 720 */ +/***/ (function(module, exports, __webpack_require__) { - .set('star', function(node) { - var prev = this.prev(); - var prefix = prev.type !== 'text' && prev.type !== 'escape' - ? '(?!\\.)' - : ''; +module.exports = new (__webpack_require__(702))(); - return this.emit(prefix + star.call(this, node), node); - }) - /** - * Parens - */ +/***/ }), +/* 721 */ +/***/ (function(module, exports, __webpack_require__) { - .set('paren', function(node) { - return this.mapVisit(node.nodes); - }) - .set('paren.open', function(node) { - var capture = this.options.capture ? '(' : ''; +"use strict"; - switch (node.parent.prefix) { - case '!': - case '^': - return this.emit(capture + '(?:(?!(?:', node); - case '*': - case '+': - case '?': - case '@': - return this.emit(capture + '(?:', node); - default: { - var val = node.val; - if (this.options.bash === true) { - val = '\\' + val; - } else if (!this.options.capture && val === '(' && node.parent.rest[0] !== '?') { - val += '?:'; - } - return this.emit(val, node); - } - } - }) - .set('paren.close', function(node) { - var capture = this.options.capture ? ')' : ''; +var utils = module.exports; +var path = __webpack_require__(4); - switch (node.prefix) { - case '!': - case '^': - var prefix = /^(\)|$)/.test(node.rest) ? '$' : ''; - var str = star.call(this, node); +/** + * Module dependencies + */ - // if the extglob has a slash explicitly defined, we know the user wants - // to match slashes, so we need to ensure the "star" regex allows for it - if (node.parent.hasSlash && !this.options.star && this.options.slash !== false) { - str = '.*?'; - } +var Snapdragon = __webpack_require__(623); +utils.define = __webpack_require__(722); +utils.diff = __webpack_require__(706); +utils.extend = __webpack_require__(691); +utils.pick = __webpack_require__(707); +utils.typeOf = __webpack_require__(589); +utils.unique = __webpack_require__(599); - return this.emit(prefix + ('))' + str + ')') + capture, node); - case '*': - case '+': - case '?': - return this.emit(')' + node.prefix + capture, node); - case '@': - return this.emit(')' + capture, node); - default: { - var val = (this.options.bash === true ? '\\' : '') + ')'; - return this.emit(val, node); +/** + * Returns true if the platform is windows, or `path.sep` is `\\`. + * This is defined as a function to allow `path.sep` to be set in unit tests, + * or by the user, if there is a reason to do so. + * @return {Boolean} + */ + +utils.isWindows = function() { + return path.sep === '\\' || process.platform === 'win32'; +}; + +/** + * Get the `Snapdragon` instance to use + */ + +utils.instantiate = function(ast, options) { + var snapdragon; + // if an instance was created by `.parse`, use that instance + if (utils.typeOf(ast) === 'object' && ast.snapdragon) { + snapdragon = ast.snapdragon; + // if the user supplies an instance on options, use that instance + } else if (utils.typeOf(options) === 'object' && options.snapdragon) { + snapdragon = options.snapdragon; + // create a new instance + } else { + snapdragon = new Snapdragon(options); + } + + utils.define(snapdragon, 'parse', function(str, options) { + var parsed = Snapdragon.prototype.parse.apply(this, arguments); + parsed.input = str; + + // escape unmatched brace/bracket/parens + var last = this.parser.stack.pop(); + if (last && this.options.strictErrors !== true) { + var open = last.nodes[0]; + var inner = last.nodes[1]; + if (last.type === 'bracket') { + if (inner.val.charAt(0) === '[') { + inner.val = '\\' + inner.val; + } + + } else { + open.val = '\\' + open.val; + var sibling = open.parent.nodes[1]; + if (sibling.type === 'star') { + sibling.loose = true; } } - }) + } - /** - * Text - */ + // add non-enumerable parser reference + utils.define(parsed, 'parser', this.parser); + return parsed; + }); - .set('text', function(node) { - var val = node.val.replace(/[\[\]]/g, '\\$&'); - return this.emit(val, node); - }); + return snapdragon; +}; + +/** + * Create the key to use for memoization. The key is generated + * by iterating over the options and concatenating key-value pairs + * to the pattern string. + */ + +utils.createKey = function(pattern, options) { + if (utils.typeOf(options) !== 'object') { + return pattern; + } + var val = pattern; + var keys = Object.keys(options); + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + val += ';' + key + '=' + String(options[key]); + } + return val; +}; + +/** + * Cast `val` to an array + * @return {Array} + */ + +utils.arrayify = function(val) { + if (typeof val === 'string') return [val]; + return val ? (Array.isArray(val) ? val : [val]) : []; +}; + +/** + * Return true if `val` is a non-empty string + */ + +utils.isString = function(val) { + return typeof val === 'string'; +}; + +/** + * Return true if `val` is a non-empty string + */ + +utils.isObject = function(val) { + return utils.typeOf(val) === 'object'; +}; + +/** + * Returns true if the given `str` has special characters + */ + +utils.hasSpecialChars = function(str) { + return /(?:(?:(^|\/)[!.])|[*?+()|\[\]{}]|[+@]\()/.test(str); +}; + +/** + * Escape regex characters in the given string + */ + +utils.escapeRegex = function(str) { + return str.replace(/[-[\]{}()^$|*+?.\\\/\s]/g, '\\$&'); }; +/** + * Normalize slashes in the given filepath. + * + * @param {String} `filepath` + * @return {String} + */ -/***/ }), -/* 704 */ -/***/ (function(module, exports, __webpack_require__) { +utils.toPosixPath = function(str) { + return str.replace(/\\+/g, '/'); +}; -"use strict"; +/** + * Strip backslashes before special characters in a string. + * + * @param {String} `str` + * @return {String} + */ +utils.unescape = function(str) { + return utils.toPosixPath(str.replace(/\\(?=[*+?!.])/g, '')); +}; /** - * Local dependencies + * Strip the prefix from a filepath + * @param {String} `fp` + * @return {String} */ -var compilers = __webpack_require__(705); -var parsers = __webpack_require__(707); +utils.stripPrefix = function(str) { + if (str.charAt(0) !== '.') { + return str; + } + var ch = str.charAt(1); + if (utils.isSlash(ch)) { + return str.slice(2); + } + return str; +}; /** - * Module dependencies + * Returns true if the given str is an escaped or + * unescaped path character */ -var debug = __webpack_require__(205)('expand-brackets'); -var extend = __webpack_require__(632); -var Snapdragon = __webpack_require__(617); -var toRegex = __webpack_require__(573); +utils.isSlash = function(str) { + return str === '/' || str === '\\/' || str === '\\' || str === '\\\\'; +}; /** - * Parses the given POSIX character class `pattern` and returns a - * string that can be used for creating regular expressions for matching. + * Returns a function that returns true if the given + * pattern matches or contains a `filepath` * * @param {String} `pattern` - * @param {Object} `options` - * @return {Object} - * @api public + * @return {Function} */ -function brackets(pattern, options) { - debug('initializing from <%s>', __filename); - var res = brackets.create(pattern, options); - return res.output; -} +utils.matchPath = function(pattern, options) { + return (options && options.contains) + ? utils.containsPattern(pattern, options) + : utils.equalsPattern(pattern, options); +}; /** - * Takes an array of strings and a POSIX character class pattern, and returns a new - * array with only the strings that matched the pattern. - * - * ```js - * var brackets = require('expand-brackets'); - * console.log(brackets.match(['1', 'a', 'ab'], '[[:alpha:]]')); - * //=> ['a'] - * - * console.log(brackets.match(['1', 'a', 'ab'], '[[:alpha:]]+')); - * //=> ['a', 'ab'] - * ``` - * @param {Array} `arr` Array of strings to match - * @param {String} `pattern` POSIX character class pattern(s) - * @param {Object} `options` - * @return {Array} - * @api public + * Returns true if the given (original) filepath or unixified path are equal + * to the given pattern. */ -brackets.match = function(arr, pattern, options) { - arr = [].concat(arr); - var opts = extend({}, options); - var isMatch = brackets.matcher(pattern, opts); - var len = arr.length; - var idx = -1; - var res = []; - - while (++idx < len) { - var ele = arr[idx]; - if (isMatch(ele)) { - res.push(ele); - } - } - - if (res.length === 0) { - if (opts.failglob === true) { - throw new Error('no matches found for "' + pattern + '"'); - } - - if (opts.nonull === true || opts.nullglob === true) { - return [pattern.split('\\').join('')]; - } - } - return res; +utils._equals = function(filepath, unixPath, pattern) { + return pattern === filepath || pattern === unixPath; }; /** - * Returns true if the specified `string` matches the given - * brackets `pattern`. - * - * ```js - * var brackets = require('expand-brackets'); - * - * console.log(brackets.isMatch('a.a', '[[:alpha:]].[[:alpha:]]')); - * //=> true - * console.log(brackets.isMatch('1.2', '[[:alpha:]].[[:alpha:]]')); - * //=> false - * ``` - * @param {String} `string` String to match - * @param {String} `pattern` Poxis pattern - * @param {String} `options` - * @return {Boolean} - * @api public + * Returns true if the given (original) filepath or unixified path contain + * the given pattern. */ -brackets.isMatch = function(str, pattern, options) { - return brackets.matcher(pattern, options)(str); +utils._contains = function(filepath, unixPath, pattern) { + return filepath.indexOf(pattern) !== -1 || unixPath.indexOf(pattern) !== -1; }; /** - * Takes a POSIX character class pattern and returns a matcher function. The returned - * function takes the string to match as its only argument. - * - * ```js - * var brackets = require('expand-brackets'); - * var isMatch = brackets.matcher('[[:lower:]].[[:upper:]]'); + * Returns a function that returns true if the given + * pattern is the same as a given `filepath` * - * console.log(isMatch('a.a')); - * //=> false - * console.log(isMatch('a.A')); - * //=> true - * ``` - * @param {String} `pattern` Poxis pattern - * @param {String} `options` - * @return {Boolean} - * @api public + * @param {String} `pattern` + * @return {Function} */ -brackets.matcher = function(pattern, options) { - var re = brackets.makeRe(pattern, options); - return function(str) { - return re.test(str); +utils.equalsPattern = function(pattern, options) { + var unixify = utils.unixify(options); + options = options || {}; + + return function fn(filepath) { + var equal = utils._equals(filepath, unixify(filepath), pattern); + if (equal === true || options.nocase !== true) { + return equal; + } + var lower = filepath.toLowerCase(); + return utils._equals(lower, unixify(lower), pattern); }; }; /** - * Create a regular expression from the given `pattern`. + * Returns a function that returns true if the given + * pattern contains a `filepath` * - * ```js - * var brackets = require('expand-brackets'); - * var re = brackets.makeRe('[[:alpha:]]'); - * console.log(re); - * //=> /^(?:[a-zA-Z])$/ - * ``` - * @param {String} `pattern` The pattern to convert to regex. - * @param {Object} `options` - * @return {RegExp} - * @api public + * @param {String} `pattern` + * @return {Function} */ -brackets.makeRe = function(pattern, options) { - var res = brackets.create(pattern, options); - var opts = extend({strictErrors: false}, options); - return toRegex(res.output, opts); +utils.containsPattern = function(pattern, options) { + var unixify = utils.unixify(options); + options = options || {}; + + return function(filepath) { + var contains = utils._contains(filepath, unixify(filepath), pattern); + if (contains === true || options.nocase !== true) { + return contains; + } + var lower = filepath.toLowerCase(); + return utils._contains(lower, unixify(lower), pattern); + }; }; /** - * Parses the given POSIX character class `pattern` and returns an object - * with the compiled `output` and optional source `map`. + * Returns a function that returns true if the given + * regex matches the `filename` of a file path. * - * ```js - * var brackets = require('expand-brackets'); - * console.log(brackets('[[:alpha:]]')); - * // { options: { source: 'string' }, - * // input: '[[:alpha:]]', - * // state: {}, - * // compilers: - * // { eos: [Function], - * // noop: [Function], - * // bos: [Function], - * // not: [Function], - * // escape: [Function], - * // text: [Function], - * // posix: [Function], - * // bracket: [Function], - * // 'bracket.open': [Function], - * // 'bracket.inner': [Function], - * // 'bracket.literal': [Function], - * // 'bracket.close': [Function] }, - * // output: '[a-zA-Z]', - * // ast: - * // { type: 'root', - * // errors: [], - * // nodes: [ [Object], [Object], [Object] ] }, - * // parsingErrors: [] } - * ``` - * @param {String} `pattern` - * @param {Object} `options` - * @return {Object} - * @api public + * @param {RegExp} `re` Matching regex + * @return {Function} */ -brackets.create = function(pattern, options) { - var snapdragon = (options && options.snapdragon) || new Snapdragon(options); - compilers(snapdragon); - parsers(snapdragon); - - var ast = snapdragon.parse(pattern, options); - ast.input = pattern; - var res = snapdragon.compile(ast, options); - res.input = pattern; - return res; +utils.matchBasename = function(re) { + return function(filepath) { + return re.test(path.basename(filepath)); + }; }; /** - * Expose `brackets` constructor, parsers and compilers + * Determines the filepath to return based on the provided options. + * @return {any} */ -brackets.compilers = compilers; -brackets.parsers = parsers; +utils.value = function(str, unixify, options) { + if (options && options.unixify === false) { + return str; + } + return unixify(str); +}; /** - * Expose `brackets` - * @type {Function} + * Returns a function that normalizes slashes in a string to forward + * slashes, strips `./` from beginning of paths, and optionally unescapes + * special characters. + * @return {Function} */ -module.exports = brackets; +utils.unixify = function(options) { + options = options || {}; + return function(filepath) { + if (utils.isWindows() || options.unixify === true) { + filepath = utils.toPosixPath(filepath); + } + if (options.stripPrefix !== false) { + filepath = utils.stripPrefix(filepath); + } + if (options.unescape === true) { + filepath = utils.unescape(filepath); + } + return filepath; + }; +}; /***/ }), -/* 705 */ +/* 722 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; +/*! + * define-property + * + * Copyright (c) 2015-2018, Jon Schlinkert. + * Released under the MIT License. + */ -var posix = __webpack_require__(706); -module.exports = function(brackets) { - brackets.compiler +var isobject = __webpack_require__(587); +var isDescriptor = __webpack_require__(588); +var define = (typeof Reflect !== 'undefined' && Reflect.defineProperty) + ? Reflect.defineProperty + : Object.defineProperty; - /** - * Escaped characters - */ +module.exports = function defineProperty(obj, key, val) { + if (!isobject(obj) && typeof obj !== 'function' && !Array.isArray(obj)) { + throw new TypeError('expected an object, function, or array'); + } - .set('escape', function(node) { - return this.emit('\\' + node.val.replace(/^\\/, ''), node); - }) + if (typeof key !== 'string') { + throw new TypeError('expected "key" to be a string'); + } - /** - * Text - */ + if (isDescriptor(val)) { + define(obj, key, val); + return obj; + } - .set('text', function(node) { - return this.emit(node.val.replace(/([{}])/g, '\\$1'), node); - }) + define(obj, key, { + configurable: true, + enumerable: false, + writable: true, + value: val + }); - /** - * POSIX character classes - */ + return obj; +}; - .set('posix', function(node) { - if (node.val === '[::]') { - return this.emit('\\[::\\]', node); - } - var val = posix[node.inner]; - if (typeof val === 'undefined') { - val = '[' + node.inner + ']'; - } - return this.emit(val, node); - }) +/***/ }), +/* 723 */ +/***/ (function(module, exports, __webpack_require__) { - /** - * Non-posix brackets - */ +"use strict"; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var readdir = __webpack_require__(724); +var reader_1 = __webpack_require__(737); +var fs_stream_1 = __webpack_require__(741); +var ReaderAsync = /** @class */ (function (_super) { + __extends(ReaderAsync, _super); + function ReaderAsync() { + return _super !== null && _super.apply(this, arguments) || this; + } + Object.defineProperty(ReaderAsync.prototype, "fsAdapter", { + /** + * Returns FileSystem adapter. + */ + get: function () { + return new fs_stream_1.default(this.options); + }, + enumerable: true, + configurable: true + }); + /** + * Use async API to read entries for Task. + */ + ReaderAsync.prototype.read = function (task) { + var _this = this; + var root = this.getRootDirectory(task); + var options = this.getReaderOptions(task); + var entries = []; + return new Promise(function (resolve, reject) { + var stream = _this.api(root, task, options); + stream.on('error', function (err) { + _this.isEnoentCodeError(err) ? resolve([]) : reject(err); + stream.pause(); + }); + stream.on('data', function (entry) { return entries.push(_this.transform(entry)); }); + stream.on('end', function () { return resolve(entries); }); + }); + }; + /** + * Returns founded paths. + */ + ReaderAsync.prototype.api = function (root, task, options) { + if (task.dynamic) { + return this.dynamicApi(root, options); + } + return this.staticApi(task, options); + }; + /** + * Api for dynamic tasks. + */ + ReaderAsync.prototype.dynamicApi = function (root, options) { + return readdir.readdirStreamStat(root, options); + }; + /** + * Api for static tasks. + */ + ReaderAsync.prototype.staticApi = function (task, options) { + return this.fsAdapter.read(task.patterns, options.filter); + }; + return ReaderAsync; +}(reader_1.default)); +exports.default = ReaderAsync; - .set('bracket', function(node) { - return this.mapVisit(node.nodes); - }) - .set('bracket.open', function(node) { - return this.emit(node.val, node); - }) - .set('bracket.inner', function(node) { - var inner = node.val; - if (inner === '[' || inner === ']') { - return this.emit('\\' + node.val, node); - } - if (inner === '^]') { - return this.emit('^\\]', node); - } - if (inner === '^') { - return this.emit('^', node); - } +/***/ }), +/* 724 */ +/***/ (function(module, exports, __webpack_require__) { - if (/-/.test(inner) && !/(\d-\d|\w-\w)/.test(inner)) { - inner = inner.split('-').join('\\-'); - } +"use strict"; - var isNegated = inner.charAt(0) === '^'; - // add slashes to negated brackets, per spec - if (isNegated && inner.indexOf('/') === -1) { - inner += '/'; - } - if (isNegated && inner.indexOf('.') === -1) { - inner += '.'; - } - // don't unescape `0` (octal literal) - inner = inner.replace(/\\([1-9])/g, '$1'); - return this.emit(inner, node); - }) - .set('bracket.close', function(node) { - var val = node.val.replace(/^\\/, ''); - if (node.parent.escaped === true) { - return this.emit('\\' + val, node); - } - return this.emit(val, node); - }); -}; +const readdirSync = __webpack_require__(725); +const readdirAsync = __webpack_require__(733); +const readdirStream = __webpack_require__(736); + +module.exports = exports = readdirAsyncPath; +exports.readdir = exports.readdirAsync = exports.async = readdirAsyncPath; +exports.readdirAsyncStat = exports.async.stat = readdirAsyncStat; +exports.readdirStream = exports.stream = readdirStreamPath; +exports.readdirStreamStat = exports.stream.stat = readdirStreamStat; +exports.readdirSync = exports.sync = readdirSyncPath; +exports.readdirSyncStat = exports.sync.stat = readdirSyncStat; + +/** + * Synchronous readdir that returns an array of string paths. + * + * @param {string} dir + * @param {object} [options] + * @returns {string[]} + */ +function readdirSyncPath (dir, options) { + return readdirSync(dir, options, {}); +} + +/** + * Synchronous readdir that returns results as an array of {@link fs.Stats} objects + * + * @param {string} dir + * @param {object} [options] + * @returns {fs.Stats[]} + */ +function readdirSyncStat (dir, options) { + return readdirSync(dir, options, { stats: true }); +} + +/** + * Aynchronous readdir (accepts an error-first callback or returns a {@link Promise}). + * Results are an array of path strings. + * + * @param {string} dir + * @param {object} [options] + * @param {function} [callback] + * @returns {Promise} + */ +function readdirAsyncPath (dir, options, callback) { + return readdirAsync(dir, options, callback, {}); +} + +/** + * Aynchronous readdir (accepts an error-first callback or returns a {@link Promise}). + * Results are an array of {@link fs.Stats} objects. + * + * @param {string} dir + * @param {object} [options] + * @param {function} [callback] + * @returns {Promise} + */ +function readdirAsyncStat (dir, options, callback) { + return readdirAsync(dir, options, callback, { stats: true }); +} + +/** + * Aynchronous readdir that returns a {@link stream.Readable} (which is also an {@link EventEmitter}). + * All stream data events ("data", "file", "directory", "symlink") are passed a path string. + * + * @param {string} dir + * @param {object} [options] + * @returns {stream.Readable} + */ +function readdirStreamPath (dir, options) { + return readdirStream(dir, options, {}); +} + +/** + * Aynchronous readdir that returns a {@link stream.Readable} (which is also an {@link EventEmitter}) + * All stream data events ("data", "file", "directory", "symlink") are passed an {@link fs.Stats} object. + * + * @param {string} dir + * @param {object} [options] + * @returns {stream.Readable} + */ +function readdirStreamStat (dir, options) { + return readdirStream(dir, options, { stats: true }); +} /***/ }), -/* 706 */ +/* 725 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; +module.exports = readdirSync; + +const DirectoryReader = __webpack_require__(726); + +let syncFacade = { + fs: __webpack_require__(731), + forEach: __webpack_require__(732), + sync: true +}; + /** - * POSIX character classes + * Returns the buffered output from a synchronous {@link DirectoryReader}. + * + * @param {string} dir + * @param {object} [options] + * @param {object} internalOptions */ +function readdirSync (dir, options, internalOptions) { + internalOptions.facade = syncFacade; -module.exports = { - alnum: 'a-zA-Z0-9', - alpha: 'a-zA-Z', - ascii: '\\x00-\\x7F', - blank: ' \\t', - cntrl: '\\x00-\\x1F\\x7F', - digit: '0-9', - graph: '\\x21-\\x7E', - lower: 'a-z', - print: '\\x20-\\x7E ', - punct: '\\-!"#$%&\'()\\*+,./:;<=>?@[\\]^_`{|}~', - space: ' \\t\\r\\n\\v\\f', - upper: 'A-Z', - word: 'A-Za-z0-9_', - xdigit: 'A-Fa-f0-9' -}; + let reader = new DirectoryReader(dir, options, internalOptions); + let stream = reader.stream; + + let results = []; + let data = stream.read(); + while (data !== null) { + results.push(data); + data = stream.read(); + } + + return results; +} /***/ }), -/* 707 */ +/* 726 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var utils = __webpack_require__(708); -var define = __webpack_require__(648); +const Readable = __webpack_require__(173).Readable; +const EventEmitter = __webpack_require__(164).EventEmitter; +const path = __webpack_require__(4); +const normalizeOptions = __webpack_require__(727); +const stat = __webpack_require__(729); +const call = __webpack_require__(730); /** - * Text regex + * Asynchronously reads the contents of a directory and streams the results + * via a {@link stream.Readable}. */ +class DirectoryReader { + /** + * @param {string} dir - The absolute or relative directory path to read + * @param {object} [options] - User-specified options, if any (see {@link normalizeOptions}) + * @param {object} internalOptions - Internal options that aren't part of the public API + * @class + */ + constructor (dir, options, internalOptions) { + this.options = options = normalizeOptions(options, internalOptions); -var TEXT_REGEX = '(\\[(?=.*\\])|\\])+'; -var not = utils.createRegex(TEXT_REGEX); + // Indicates whether we should keep reading + // This is set false if stream.Readable.push() returns false. + this.shouldRead = true; -/** - * Brackets parsers - */ + // The directories to read + // (initialized with the top-level directory) + this.queue = [{ + path: dir, + basePath: options.basePath, + posixBasePath: options.posixBasePath, + depth: 0 + }]; -function parsers(brackets) { - brackets.state = brackets.state || {}; - brackets.parser.sets.bracket = brackets.parser.sets.bracket || []; - brackets.parser + // The number of directories that are currently being processed + this.pending = 0; - .capture('escape', function() { - if (this.isInside('bracket')) return; - var pos = this.position(); - var m = this.match(/^\\(.)/); - if (!m) return; + // The data that has been read, but not yet emitted + this.buffer = []; - return pos({ - type: 'escape', - val: m[0] - }); - }) + this.stream = new Readable({ objectMode: true }); + this.stream._read = () => { + // Start (or resume) reading + this.shouldRead = true; - /** - * Text parser - */ + // If we have data in the buffer, then send the next chunk + if (this.buffer.length > 0) { + this.pushFromBuffer(); + } - .capture('text', function() { - if (this.isInside('bracket')) return; - var pos = this.position(); - var m = this.match(not); - if (!m || !m[0]) return; + // If we have directories queued, then start processing the next one + if (this.queue.length > 0) { + if (this.options.facade.sync) { + while (this.queue.length > 0) { + this.readNextDirectory(); + } + } + else { + this.readNextDirectory(); + } + } - return pos({ - type: 'text', - val: m[0] - }); - }) + this.checkForEOF(); + }; + } - /** - * POSIX character classes: "[[:alpha:][:digits:]]" - */ + /** + * Reads the next directory in the queue + */ + readNextDirectory () { + let facade = this.options.facade; + let dir = this.queue.shift(); + this.pending++; - .capture('posix', function() { - var pos = this.position(); - var m = this.match(/^\[:(.*?):\](?=.*\])/); - if (!m) return; + // Read the directory listing + call.safe(facade.fs.readdir, dir.path, (err, items) => { + if (err) { + // fs.readdir threw an error + this.emit('error', err); + return this.finishedReadingDirectory(); + } - var inside = this.isInside('bracket'); - if (inside) { - brackets.posix++; + try { + // Process each item in the directory (simultaneously, if async) + facade.forEach( + items, + this.processItem.bind(this, dir), + this.finishedReadingDirectory.bind(this, dir) + ); + } + catch (err2) { + // facade.forEach threw an error + // (probably because fs.readdir returned an invalid result) + this.emit('error', err2); + this.finishedReadingDirectory(); } + }); + } - return pos({ - type: 'posix', - insideBracket: inside, - inner: m[1], - val: m[0] - }); - }) + /** + * This method is called after all items in a directory have been processed. + * + * NOTE: This does not necessarily mean that the reader is finished, since there may still + * be other directories queued or pending. + */ + finishedReadingDirectory () { + this.pending--; - /** - * Bracket (noop) - */ + if (this.shouldRead) { + // If we have directories queued, then start processing the next one + if (this.queue.length > 0 && this.options.facade.async) { + this.readNextDirectory(); + } - .capture('bracket', function() {}) + this.checkForEOF(); + } + } - /** - * Open: '[' - */ + /** + * Determines whether the reader has finished processing all items in all directories. + * If so, then the "end" event is fired (via {@Readable#push}) + */ + checkForEOF () { + if (this.buffer.length === 0 && // The stuff we've already read + this.pending === 0 && // The stuff we're currently reading + this.queue.length === 0) { // The stuff we haven't read yet + // There's no more stuff! + this.stream.push(null); + } + } - .capture('bracket.open', function() { - var parsed = this.parsed; - var pos = this.position(); - var m = this.match(/^\[(?=.*\])/); - if (!m) return; + /** + * Processes a single item in a directory. + * + * If the item is a directory, and `option.deep` is enabled, then the item will be added + * to the directory queue. + * + * If the item meets the filter criteria, then it will be emitted to the reader's stream. + * + * @param {object} dir - A directory object from the queue + * @param {string} item - The name of the item (name only, no path) + * @param {function} done - A callback function that is called after the item has been processed + */ + processItem (dir, item, done) { + let stream = this.stream; + let options = this.options; - var prev = this.prev(); - var last = utils.last(prev.nodes); + let itemPath = dir.basePath + item; + let posixPath = dir.posixBasePath + item; + let fullPath = path.join(dir.path, item); - if (parsed.slice(-1) === '\\' && !this.isInside('bracket')) { - last.val = last.val.slice(0, last.val.length - 1); - return pos({ - type: 'escape', - val: m[0] - }); - } + // If `options.deep` is a number, and we've already recursed to the max depth, + // then there's no need to check fs.Stats to know if it's a directory. + // If `options.deep` is a function, then we'll need fs.Stats + let maxDepthReached = dir.depth >= options.recurseDepth; - var open = pos({ - type: 'bracket.open', - val: m[0] - }); + // Do we need to call `fs.stat`? + let needStats = + !maxDepthReached || // we need the fs.Stats to know if it's a directory + options.stats || // the user wants fs.Stats objects returned + options.recurseFn || // we need fs.Stats for the recurse function + options.filterFn || // we need fs.Stats for the filter function + EventEmitter.listenerCount(stream, 'file') || // we need the fs.Stats to know if it's a file + EventEmitter.listenerCount(stream, 'directory') || // we need the fs.Stats to know if it's a directory + EventEmitter.listenerCount(stream, 'symlink'); // we need the fs.Stats to know if it's a symlink - if (last.type === 'bracket.open' || this.isInside('bracket')) { - open.val = '\\' + open.val; - open.type = 'bracket.inner'; - open.escaped = true; - return open; + // If we don't need stats, then exit early + if (!needStats) { + if (this.filter(itemPath, posixPath)) { + this.pushOrBuffer({ data: itemPath }); } + return done(); + } - var node = pos({ - type: 'bracket', - nodes: [open] - }); - - define(node, 'parent', prev); - define(open, 'parent', node); - this.push('bracket', node); - prev.nodes.push(node); - }) + // Get the fs.Stats object for this path + stat(options.facade.fs, fullPath, (err, stats) => { + if (err) { + // fs.stat threw an error + this.emit('error', err); + return done(); + } - /** - * Bracket text - */ + try { + // Add the item's path to the fs.Stats object + // The base of this path, and its separators are determined by the options + // (i.e. options.basePath and options.sep) + stats.path = itemPath; - .capture('bracket.inner', function() { - if (!this.isInside('bracket')) return; - var pos = this.position(); - var m = this.match(not); - if (!m || !m[0]) return; + // Add depth of the path to the fs.Stats object for use this in the filter function + stats.depth = dir.depth; - var next = this.input.charAt(0); - var val = m[0]; + if (this.shouldRecurse(stats, posixPath, maxDepthReached)) { + // Add this subdirectory to the queue + this.queue.push({ + path: fullPath, + basePath: itemPath + options.sep, + posixBasePath: posixPath + '/', + depth: dir.depth + 1, + }); + } - var node = pos({ - type: 'bracket.inner', - val: val - }); + // Determine whether this item matches the filter criteria + if (this.filter(stats, posixPath)) { + this.pushOrBuffer({ + data: options.stats ? stats : itemPath, + file: stats.isFile(), + directory: stats.isDirectory(), + symlink: stats.isSymbolicLink(), + }); + } - if (val === '\\\\') { - return node; + done(); + } + catch (err2) { + // An error occurred while processing the item + // (probably during a user-specified function, such as options.deep, options.filter, etc.) + this.emit('error', err2); + done(); } + }); + } - var first = val.charAt(0); - var last = val.slice(-1); + /** + * Pushes the given chunk of data to the stream, or adds it to the buffer, + * depending on the state of the stream. + * + * @param {object} chunk + */ + pushOrBuffer (chunk) { + // Add the chunk to the buffer + this.buffer.push(chunk); - if (first === '!') { - val = '^' + val.slice(1); - } + // If we're still reading, then immediately emit the next chunk in the buffer + // (which may or may not be the chunk that we just added) + if (this.shouldRead) { + this.pushFromBuffer(); + } + } - if (last === '\\' || (val === '^' && next === ']')) { - val += this.input[0]; - this.consume(1); - } + /** + * Immediately pushes the next chunk in the buffer to the reader's stream. + * The "data" event will always be fired (via {@link Readable#push}). + * In addition, the "file", "directory", and/or "symlink" events may be fired, + * depending on the type of properties of the chunk. + */ + pushFromBuffer () { + let stream = this.stream; + let chunk = this.buffer.shift(); - node.val = val; - return node; - }) + // Stream the data + try { + this.shouldRead = stream.push(chunk.data); + } + catch (err) { + this.emit('error', err); + } - /** - * Close: ']' - */ + // Also emit specific events, based on the type of chunk + chunk.file && this.emit('file', chunk.data); + chunk.symlink && this.emit('symlink', chunk.data); + chunk.directory && this.emit('directory', chunk.data); + } - .capture('bracket.close', function() { - var parsed = this.parsed; - var pos = this.position(); - var m = this.match(/^\]/); - if (!m) return; + /** + * Determines whether the given directory meets the user-specified recursion criteria. + * If the user didn't specify recursion criteria, then this function will default to true. + * + * @param {fs.Stats} stats - The directory's {@link fs.Stats} object + * @param {string} posixPath - The item's POSIX path (used for glob matching) + * @param {boolean} maxDepthReached - Whether we've already crawled the user-specified depth + * @returns {boolean} + */ + shouldRecurse (stats, posixPath, maxDepthReached) { + let options = this.options; - var prev = this.prev(); - var last = utils.last(prev.nodes); + if (maxDepthReached) { + // We've already crawled to the maximum depth. So no more recursion. + return false; + } + else if (!stats.isDirectory()) { + // It's not a directory. So don't try to crawl it. + return false; + } + else if (options.recurseGlob) { + // Glob patterns are always tested against the POSIX path, even on Windows + // https://github.com/isaacs/node-glob#windows + return options.recurseGlob.test(posixPath); + } + else if (options.recurseRegExp) { + // Regular expressions are tested against the normal path + // (based on the OS or options.sep) + return options.recurseRegExp.test(stats.path); + } + else if (options.recurseFn) { + try { + // Run the user-specified recursion criteria + return options.recurseFn.call(null, stats); + } + catch (err) { + // An error occurred in the user's code. + // In Sync and Async modes, this will return an error. + // In Streaming mode, we emit an "error" event, but continue processing + this.emit('error', err); + } + } + else { + // No recursion function was specified, and we're within the maximum depth. + // So crawl this directory. + return true; + } + } - if (parsed.slice(-1) === '\\' && !this.isInside('bracket')) { - last.val = last.val.slice(0, last.val.length - 1); + /** + * Determines whether the given item meets the user-specified filter criteria. + * If the user didn't specify a filter, then this function will always return true. + * + * @param {string|fs.Stats} value - Either the item's path, or the item's {@link fs.Stats} object + * @param {string} posixPath - The item's POSIX path (used for glob matching) + * @returns {boolean} + */ + filter (value, posixPath) { + let options = this.options; - return pos({ - type: 'escape', - val: m[0] - }); + if (options.filterGlob) { + // Glob patterns are always tested against the POSIX path, even on Windows + // https://github.com/isaacs/node-glob#windows + return options.filterGlob.test(posixPath); + } + else if (options.filterRegExp) { + // Regular expressions are tested against the normal path + // (based on the OS or options.sep) + return options.filterRegExp.test(value.path || value); + } + else if (options.filterFn) { + try { + // Run the user-specified filter function + return options.filterFn.call(null, value); + } + catch (err) { + // An error occurred in the user's code. + // In Sync and Async modes, this will return an error. + // In Streaming mode, we emit an "error" event, but continue processing + this.emit('error', err); } + } + else { + // No filter was specified, so match everything + return true; + } + } - var node = pos({ - type: 'bracket.close', - rest: this.input, - val: m[0] - }); + /** + * Emits an event. If one of the event listeners throws an error, + * then an "error" event is emitted. + * + * @param {string} eventName + * @param {*} data + */ + emit (eventName, data) { + let stream = this.stream; - if (last.type === 'bracket.open') { - node.type = 'bracket.inner'; - node.escaped = true; - return node; + try { + stream.emit(eventName, data); + } + catch (err) { + if (eventName === 'error') { + // Don't recursively emit "error" events. + // If the first one fails, then just throw + throw err; } - - var bracket = this.pop('bracket'); - if (!this.isType(bracket, 'bracket')) { - if (this.options.strict) { - throw new Error('missing opening "["'); - } - node.type = 'bracket.inner'; - node.escaped = true; - return node; + else { + stream.emit('error', err); } - - bracket.nodes.push(node); - define(node, 'parent', bracket); - }); + } + } } -/** - * Brackets parsers - */ - -module.exports = parsers; - -/** - * Expose text regex - */ - -module.exports.TEXT_REGEX = TEXT_REGEX; +module.exports = DirectoryReader; /***/ }), -/* 708 */ +/* 727 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var toRegex = __webpack_require__(573); -var regexNot = __webpack_require__(590); -var cached; +const path = __webpack_require__(4); +const globToRegExp = __webpack_require__(728); + +module.exports = normalizeOptions; + +let isWindows = /^win/.test(process.platform); /** - * Get the last element from `array` - * @param {Array} `array` - * @return {*} + * @typedef {Object} FSFacade + * @property {fs.readdir} readdir + * @property {fs.stat} stat + * @property {fs.lstat} lstat */ -exports.last = function(arr) { - return arr[arr.length - 1]; -}; - /** - * Create and cache regex to use for text nodes + * Validates and normalizes the options argument + * + * @param {object} [options] - User-specified options, if any + * @param {object} internalOptions - Internal options that aren't part of the public API + * + * @param {number|boolean|function} [options.deep] + * The number of directories to recursively traverse. Any falsy value or negative number will + * default to zero, so only the top-level contents will be returned. Set to `true` or `Infinity` + * to traverse all subdirectories. Or provide a function that accepts a {@link fs.Stats} object + * and returns a truthy value if the directory's contents should be crawled. + * + * @param {function|string|RegExp} [options.filter] + * A function that accepts a {@link fs.Stats} object and returns a truthy value if the data should + * be returned. Or a RegExp or glob string pattern, to filter by file name. + * + * @param {string} [options.sep] + * The path separator to use. By default, the OS-specific separator will be used, but this can be + * set to a specific value to ensure consistency across platforms. + * + * @param {string} [options.basePath] + * The base path to prepend to each result. If empty, then all results will be relative to `dir`. + * + * @param {FSFacade} [options.fs] + * Synchronous or asynchronous facades for Node.js File System module + * + * @param {object} [internalOptions.facade] + * Synchronous or asynchronous facades for various methods, including for the Node.js File System module + * + * @param {boolean} [internalOptions.emit] + * Indicates whether the reader should emit "file", "directory", and "symlink" events + * + * @param {boolean} [internalOptions.stats] + * Indicates whether the reader should emit {@link fs.Stats} objects instead of path strings + * + * @returns {object} */ +function normalizeOptions (options, internalOptions) { + if (options === null || options === undefined) { + options = {}; + } + else if (typeof options !== 'object') { + throw new TypeError('options must be an object'); + } -exports.createRegex = function(pattern, include) { - if (cached) return cached; - var opts = {contains: true, strictClose: false}; - var not = regexNot.create(pattern, opts); - var re; + let recurseDepth, recurseFn, recurseRegExp, recurseGlob, deep = options.deep; + if (deep === null || deep === undefined) { + recurseDepth = 0; + } + else if (typeof deep === 'boolean') { + recurseDepth = deep ? Infinity : 0; + } + else if (typeof deep === 'number') { + if (deep < 0 || isNaN(deep)) { + throw new Error('options.deep must be a positive number'); + } + else if (Math.floor(deep) !== deep) { + throw new Error('options.deep must be an integer'); + } + else { + recurseDepth = deep; + } + } + else if (typeof deep === 'function') { + recurseDepth = Infinity; + recurseFn = deep; + } + else if (deep instanceof RegExp) { + recurseDepth = Infinity; + recurseRegExp = deep; + } + else if (typeof deep === 'string' && deep.length > 0) { + recurseDepth = Infinity; + recurseGlob = globToRegExp(deep, { extended: true, globstar: true }); + } + else { + throw new TypeError('options.deep must be a boolean, number, function, regular expression, or glob pattern'); + } - if (typeof include === 'string') { - re = toRegex('^(?:' + include + '|' + not + ')', opts); - } else { - re = toRegex(not, opts); + let filterFn, filterRegExp, filterGlob, filter = options.filter; + if (filter !== null && filter !== undefined) { + if (typeof filter === 'function') { + filterFn = filter; + } + else if (filter instanceof RegExp) { + filterRegExp = filter; + } + else if (typeof filter === 'string' && filter.length > 0) { + filterGlob = globToRegExp(filter, { extended: true, globstar: true }); + } + else { + throw new TypeError('options.filter must be a function, regular expression, or glob pattern'); + } } - return (cached = re); -}; + let sep = options.sep; + if (sep === null || sep === undefined) { + sep = path.sep; + } + else if (typeof sep !== 'string') { + throw new TypeError('options.sep must be a string'); + } + let basePath = options.basePath; + if (basePath === null || basePath === undefined) { + basePath = ''; + } + else if (typeof basePath === 'string') { + // Append a path separator to the basePath, if necessary + if (basePath && basePath.substr(-1) !== sep) { + basePath += sep; + } + } + else { + throw new TypeError('options.basePath must be a string'); + } -/***/ }), -/* 709 */ -/***/ (function(module, exports, __webpack_require__) { + // Convert the basePath to POSIX (forward slashes) + // so that glob pattern matching works consistently, even on Windows + let posixBasePath = basePath; + if (posixBasePath && sep !== '/') { + posixBasePath = posixBasePath.replace(new RegExp('\\' + sep, 'g'), '/'); -"use strict"; + /* istanbul ignore if */ + if (isWindows) { + // Convert Windows root paths (C:\) and UNCs (\\) to POSIX root paths + posixBasePath = posixBasePath.replace(/^([a-zA-Z]\:\/|\/\/)/, '/'); + } + } + // Determine which facade methods to use + let facade; + if (options.fs === null || options.fs === undefined) { + // The user didn't provide their own facades, so use our internal ones + facade = internalOptions.facade; + } + else if (typeof options.fs === 'object') { + // Merge the internal facade methods with the user-provided `fs` facades + facade = Object.assign({}, internalOptions.facade); + facade.fs = Object.assign({}, internalOptions.facade.fs, options.fs); + } + else { + throw new TypeError('options.fs must be an object'); + } -var brackets = __webpack_require__(704); -var define = __webpack_require__(710); -var utils = __webpack_require__(711); + return { + recurseDepth, + recurseFn, + recurseRegExp, + recurseGlob, + filterFn, + filterRegExp, + filterGlob, + sep, + basePath, + posixBasePath, + facade, + emit: !!internalOptions.emit, + stats: !!internalOptions.stats, + }; +} -/** - * Characters to use in text regex (we want to "not" match - * characters that are matched by other parsers) - */ -var TEXT_REGEX = '([!@*?+]?\\(|\\)|[*?.+\\\\]|\\[:?(?=.*\\])|:?\\])+'; -var not = utils.createRegex(TEXT_REGEX); +/***/ }), +/* 728 */ +/***/ (function(module, exports) { -/** - * Extglob parsers - */ +module.exports = function (glob, opts) { + if (typeof glob !== 'string') { + throw new TypeError('Expected a string'); + } -function parsers(extglob) { - extglob.state = extglob.state || {}; + var str = String(glob); - /** - * Use `expand-brackets` parsers - */ + // The regexp we are building, as a string. + var reStr = ""; - extglob.use(brackets.parsers); - extglob.parser.sets.paren = extglob.parser.sets.paren || []; - extglob.parser + // Whether we are matching so called "extended" globs (like bash) and should + // support single character matching, matching ranges of characters, group + // matching, etc. + var extended = opts ? !!opts.extended : false; - /** - * Extglob open: "*(" - */ + // When globstar is _false_ (default), '/foo/*' is translated a regexp like + // '^\/foo\/.*$' which will match any string beginning with '/foo/' + // When globstar is _true_, '/foo/*' is translated to regexp like + // '^\/foo\/[^/]*$' which will match any string beginning with '/foo/' BUT + // which does not have a '/' to the right of it. + // E.g. with '/foo/*' these will match: '/foo/bar', '/foo/bar.txt' but + // these will not '/foo/bar/baz', '/foo/bar/baz.txt' + // Lastely, when globstar is _true_, '/foo/**' is equivelant to '/foo/*' when + // globstar is _false_ + var globstar = opts ? !!opts.globstar : false; - .capture('paren.open', function() { - var parsed = this.parsed; - var pos = this.position(); - var m = this.match(/^([!@*?+])?\(/); - if (!m) return; + // If we are doing extended matching, this boolean is true when we are inside + // a group (eg {*.html,*.js}), and false otherwise. + var inGroup = false; - var prev = this.prev(); - var prefix = m[1]; - var val = m[0]; + // RegExp flags (eg "i" ) to pass in to RegExp constructor. + var flags = opts && typeof( opts.flags ) === "string" ? opts.flags : ""; - var open = pos({ - type: 'paren.open', - parsed: parsed, - val: val - }); + var c; + for (var i = 0, len = str.length; i < len; i++) { + c = str[i]; - var node = pos({ - type: 'paren', - prefix: prefix, - nodes: [open] - }); + switch (c) { + case "\\": + case "/": + case "$": + case "^": + case "+": + case ".": + case "(": + case ")": + case "=": + case "!": + case "|": + reStr += "\\" + c; + break; - // if nested negation extglobs, just cancel them out to simplify - if (prefix === '!' && prev.type === 'paren' && prev.prefix === '!') { - prev.prefix = '@'; - node.prefix = '@'; + case "?": + if (extended) { + reStr += "."; + break; } - define(node, 'rest', this.input); - define(node, 'parsed', parsed); - define(node, 'parent', prev); - define(open, 'parent', node); + case "[": + case "]": + if (extended) { + reStr += c; + break; + } - this.push('paren', node); - prev.nodes.push(node); - }) + case "{": + if (extended) { + inGroup = true; + reStr += "("; + break; + } - /** - * Extglob close: ")" - */ + case "}": + if (extended) { + inGroup = false; + reStr += ")"; + break; + } - .capture('paren.close', function() { - var parsed = this.parsed; - var pos = this.position(); - var m = this.match(/^\)/); - if (!m) return; + case ",": + if (inGroup) { + reStr += "|"; + break; + } + reStr += "\\" + c; + break; - var parent = this.pop('paren'); - var node = pos({ - type: 'paren.close', - rest: this.input, - parsed: parsed, - val: m[0] - }); + case "*": + // Move over all consecutive "*"'s. + // Also store the previous and next characters + var prevChar = str[i - 1]; + var starCount = 1; + while(str[i + 1] === "*") { + starCount++; + i++; + } + var nextChar = str[i + 1]; - if (!this.isType(parent, 'paren')) { - if (this.options.strict) { - throw new Error('missing opening paren: "("'); + if (!globstar) { + // globstar is disabled, so treat any number of "*" as one + reStr += ".*"; + } else { + // globstar is enabled, so determine if this is a globstar segment + var isGlobstar = starCount > 1 // multiple "*"'s + && (prevChar === "/" || prevChar === undefined) // from the start of the segment + && (nextChar === "/" || nextChar === undefined) // to the end of the segment + + if (isGlobstar) { + // it's a globstar, so match zero or more path segments + reStr += "(?:[^/]*(?:\/|$))*"; + i++; // move over the "/" + } else { + // it's not a globstar, so only match one path segment + reStr += "[^/]*"; } - node.escaped = true; - return node; } + break; - node.prefix = parent.prefix; - parent.nodes.push(node); - define(node, 'parent', parent); - }) + default: + reStr += c; + } + } - /** - * Escape: "\\." - */ + // When regexp 'g' flag is specified don't + // constrain the regular expression with ^ & $ + if (!flags || !~flags.indexOf('g')) { + reStr = "^" + reStr + "$"; + } - .capture('escape', function() { - var pos = this.position(); - var m = this.match(/^\\(.)/); - if (!m) return; + return new RegExp(reStr, flags); +}; - return pos({ - type: 'escape', - val: m[0], - ch: m[1] - }); - }) - /** - * Question marks: "?" - */ +/***/ }), +/* 729 */ +/***/ (function(module, exports, __webpack_require__) { - .capture('qmark', function() { - var parsed = this.parsed; - var pos = this.position(); - var m = this.match(/^\?+(?!\()/); - if (!m) return; - extglob.state.metachar = true; - return pos({ - type: 'qmark', - rest: this.input, - parsed: parsed, - val: m[0] - }); - }) +"use strict"; - /** - * Character parsers - */ - .capture('star', /^\*(?!\()/) - .capture('plus', /^\+(?!\()/) - .capture('dot', /^\./) - .capture('text', not); -}; +const call = __webpack_require__(730); + +module.exports = stat; /** - * Expose text regex string + * Retrieves the {@link fs.Stats} for the given path. If the path is a symbolic link, + * then the Stats of the symlink's target are returned instead. If the symlink is broken, + * then the Stats of the symlink itself are returned. + * + * @param {object} fs - Synchronous or Asynchronouse facade for the "fs" module + * @param {string} path - The path to return stats for + * @param {function} callback */ +function stat (fs, path, callback) { + let isSymLink = false; -module.exports.TEXT_REGEX = TEXT_REGEX; + call.safe(fs.lstat, path, (err, lstats) => { + if (err) { + // fs.lstat threw an eror + return callback(err); + } + + try { + isSymLink = lstats.isSymbolicLink(); + } + catch (err2) { + // lstats.isSymbolicLink() threw an error + // (probably because fs.lstat returned an invalid result) + return callback(err2); + } + + if (isSymLink) { + // Try to resolve the symlink + symlinkStat(fs, path, lstats, callback); + } + else { + // It's not a symlink, so return the stats as-is + callback(null, lstats); + } + }); +} /** - * Extglob parsers + * Retrieves the {@link fs.Stats} for the target of the given symlink. + * If the symlink is broken, then the Stats of the symlink itself are returned. + * + * @param {object} fs - Synchronous or Asynchronouse facade for the "fs" module + * @param {string} path - The path of the symlink to return stats for + * @param {object} lstats - The stats of the symlink + * @param {function} callback */ +function symlinkStat (fs, path, lstats, callback) { + call.safe(fs.stat, path, (err, stats) => { + if (err) { + // The symlink is broken, so return the stats for the link itself + return callback(null, lstats); + } -module.exports = parsers; + try { + // Return the stats for the resolved symlink target, + // and override the `isSymbolicLink` method to indicate that it's a symlink + stats.isSymbolicLink = () => true; + } + catch (err2) { + // Setting stats.isSymbolicLink threw an error + // (probably because fs.stat returned an invalid result) + return callback(err2); + } + + callback(null, stats); + }); +} /***/ }), -/* 710 */ +/* 730 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -/*! - * define-property - * - * Copyright (c) 2015, 2017, Jon Schlinkert. - * Released under the MIT License. - */ +let call = module.exports = { + safe: safeCall, + once: callOnce, +}; -var isDescriptor = __webpack_require__(582); +/** + * Calls a function with the given arguments, and ensures that the error-first callback is _always_ + * invoked exactly once, even if the function throws an error. + * + * @param {function} fn - The function to invoke + * @param {...*} args - The arguments to pass to the function. The final argument must be a callback function. + */ +function safeCall (fn, args) { + // Get the function arguments as an array + args = Array.prototype.slice.call(arguments, 1); -module.exports = function defineProperty(obj, prop, val) { - if (typeof obj !== 'object' && typeof obj !== 'function') { - throw new TypeError('expected an object or function.'); - } + // Replace the callback function with a wrapper that ensures it will only be called once + let callback = call.once(args.pop()); + args.push(callback); - if (typeof prop !== 'string') { - throw new TypeError('expected `prop` to be a string.'); + try { + fn.apply(null, args); } - - if (isDescriptor(val) && ('set' in val || 'get' in val)) { - return Object.defineProperty(obj, prop, val); + catch (err) { + callback(err); } +} - return Object.defineProperty(obj, prop, { - configurable: true, - enumerable: false, - writable: true, - value: val - }); -}; +/** + * Returns a wrapper function that ensures the given callback function is only called once. + * Subsequent calls are ignored, unless the first argument is an Error, in which case the + * error is thrown. + * + * @param {function} fn - The function that should only be called once + * @returns {function} + */ +function callOnce (fn) { + let fulfilled = false; + + return function onceWrapper (err) { + if (!fulfilled) { + fulfilled = true; + return fn.apply(this, arguments); + } + else if (err) { + // The callback has already been called, but now an error has occurred + // (most likely inside the callback function). So re-throw the error, + // so it gets handled further up the call stack + throw err; + } + }; +} /***/ }), -/* 711 */ +/* 731 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var regex = __webpack_require__(590); -var Cache = __webpack_require__(696); - -/** - * Utils - */ - -var utils = module.exports; -var cache = utils.cache = new Cache(); +const fs = __webpack_require__(132); +const call = __webpack_require__(730); /** - * Cast `val` to an array - * @return {Array} + * A facade around {@link fs.readdirSync} that allows it to be called + * the same way as {@link fs.readdir}. + * + * @param {string} dir + * @param {function} callback */ +exports.readdir = function (dir, callback) { + // Make sure the callback is only called once + callback = call.once(callback); -utils.arrayify = function(val) { - if (!Array.isArray(val)) { - return [val]; + try { + let items = fs.readdirSync(dir); + callback(null, items); + } + catch (err) { + callback(err); } - return val; }; /** - * Memoize a generated regex or function + * A facade around {@link fs.statSync} that allows it to be called + * the same way as {@link fs.stat}. + * + * @param {string} path + * @param {function} callback */ +exports.stat = function (path, callback) { + // Make sure the callback is only called once + callback = call.once(callback); -utils.memoize = function(type, pattern, options, fn) { - var key = utils.createKey(type + pattern, options); - - if (cache.has(type, key)) { - return cache.get(type, key); + try { + let stats = fs.statSync(path); + callback(null, stats); } - - var val = fn(pattern, options); - if (options && options.cache === false) { - return val; + catch (err) { + callback(err); } - - cache.set(type, key, val); - return val; }; /** - * Create the key to use for memoization. The key is generated - * by iterating over the options and concatenating key-value pairs - * to the pattern string. + * A facade around {@link fs.lstatSync} that allows it to be called + * the same way as {@link fs.lstat}. + * + * @param {string} path + * @param {function} callback */ +exports.lstat = function (path, callback) { + // Make sure the callback is only called once + callback = call.once(callback); -utils.createKey = function(pattern, options) { - var key = pattern; - if (typeof options === 'undefined') { - return key; + try { + let stats = fs.lstatSync(path); + callback(null, stats); } - for (var prop in options) { - key += ';' + prop + '=' + String(options[prop]); + catch (err) { + callback(err); } - return key; -}; - -/** - * Create the regex to use for matching text - */ - -utils.createRegex = function(str) { - var opts = {contains: true, strictClose: false}; - return regex(str, opts); }; /***/ }), -/* 712 */ +/* 732 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -/** - * Module dependencies - */ - -var Snapdragon = __webpack_require__(617); -var define = __webpack_require__(710); -var extend = __webpack_require__(632); - -/** - * Local dependencies - */ - -var compilers = __webpack_require__(703); -var parsers = __webpack_require__(709); +module.exports = syncForEach; /** - * Customize Snapdragon parser and renderer + * A facade that allows {@link Array.forEach} to be called as though it were asynchronous. + * + * @param {array} array - The array to iterate over + * @param {function} iterator - The function to call for each item in the array + * @param {function} done - The function to call when all iterators have completed */ - -function Extglob(options) { - this.options = extend({source: 'extglob'}, options); - this.snapdragon = this.options.snapdragon || new Snapdragon(this.options); - this.snapdragon.patterns = this.snapdragon.patterns || {}; - this.compiler = this.snapdragon.compiler; - this.parser = this.snapdragon.parser; - - compilers(this.snapdragon); - parsers(this.snapdragon); - - /** - * Override Snapdragon `.parse` method - */ - - define(this.snapdragon, 'parse', function(str, options) { - var parsed = Snapdragon.prototype.parse.apply(this, arguments); - parsed.input = str; - - // escape unmatched brace/bracket/parens - var last = this.parser.stack.pop(); - if (last && this.options.strict !== true) { - var node = last.nodes[0]; - node.val = '\\' + node.val; - var sibling = node.parent.nodes[1]; - if (sibling.type === 'star') { - sibling.loose = true; - } - } - - // add non-enumerable parser reference - define(parsed, 'parser', this.parser); - return parsed; - }); - - /** - * Decorate `.parse` method - */ - - define(this, 'parse', function(ast, options) { - return this.snapdragon.parse.apply(this.snapdragon, arguments); - }); - - /** - * Decorate `.compile` method - */ - - define(this, 'compile', function(ast, options) { - return this.snapdragon.compile.apply(this.snapdragon, arguments); +function syncForEach (array, iterator, done) { + array.forEach(item => { + iterator(item, () => { + // Note: No error-handling here because this is currently only ever called + // by DirectoryReader, which never passes an `error` parameter to the callback. + // Instead, DirectoryReader emits an "error" event if an error occurs. + }); }); + done(); } -/** - * Expose `Extglob` - */ - -module.exports = Extglob; - /***/ }), -/* 713 */ +/* 733 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extglob = __webpack_require__(702); -var nanomatch = __webpack_require__(688); -var regexNot = __webpack_require__(590); -var toRegex = __webpack_require__(573); -var not; +module.exports = readdirAsync; -/** - * Characters to use in negation regex (we want to "not" match - * characters that are matched by other parsers) - */ +const maybe = __webpack_require__(734); +const DirectoryReader = __webpack_require__(726); -var TEXT = '([!@*?+]?\\(|\\)|\\[:?(?=.*?:?\\])|:?\\]|[*+?!^$.\\\\/])+'; -var createNotRegex = function(opts) { - return not || (not = textRegex(TEXT)); +let asyncFacade = { + fs: __webpack_require__(132), + forEach: __webpack_require__(735), + async: true }; /** - * Parsers + * Returns the buffered output from an asynchronous {@link DirectoryReader}, + * via an error-first callback or a {@link Promise}. + * + * @param {string} dir + * @param {object} [options] + * @param {function} [callback] + * @param {object} internalOptions */ +function readdirAsync (dir, options, callback, internalOptions) { + if (typeof options === 'function') { + callback = options; + options = undefined; + } -module.exports = function(snapdragon) { - var parsers = snapdragon.parser.parsers; - - // register nanomatch parsers - snapdragon.use(nanomatch.parsers); - - // get references to some specific nanomatch parsers before they - // are overridden by the extglob and/or parsers - var escape = parsers.escape; - var slash = parsers.slash; - var qmark = parsers.qmark; - var plus = parsers.plus; - var star = parsers.star; - var dot = parsers.dot; - - // register extglob parsers - snapdragon.use(extglob.parsers); - - // custom micromatch parsers - snapdragon.parser - .use(function() { - // override "notRegex" created in nanomatch parser - this.notRegex = /^\!+(?!\()/; - }) - // reset the referenced parsers - .capture('escape', escape) - .capture('slash', slash) - .capture('qmark', qmark) - .capture('star', star) - .capture('plus', plus) - .capture('dot', dot) - - /** - * Override `text` parser - */ + return maybe(callback, new Promise(((resolve, reject) => { + let results = []; - .capture('text', function() { - if (this.isInside('bracket')) return; - var pos = this.position(); - var m = this.match(createNotRegex(this.options)); - if (!m || !m[0]) return; + internalOptions.facade = asyncFacade; - // escape regex boundary characters and simple brackets - var val = m[0].replace(/([[\]^$])/g, '\\$1'); + let reader = new DirectoryReader(dir, options, internalOptions); + let stream = reader.stream; - return pos({ - type: 'text', - val: val - }); + stream.on('error', err => { + reject(err); + stream.pause(); }); -}; - -/** - * Create text regex - */ - -function textRegex(pattern) { - var notStr = regexNot.create(pattern, {contains: true, strictClose: false}); - var prefix = '(?:[\\^]|\\\\|'; - return toRegex(prefix + notStr + ')', {strictClose: false}); + stream.on('data', result => { + results.push(result); + }); + stream.on('end', () => { + resolve(results); + }); + }))); } /***/ }), -/* 714 */ -/***/ (function(module, exports, __webpack_require__) { - -module.exports = new (__webpack_require__(696))(); - - -/***/ }), -/* 715 */ +/* 734 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var utils = module.exports; -var path = __webpack_require__(4); - -/** - * Module dependencies - */ - -var Snapdragon = __webpack_require__(617); -utils.define = __webpack_require__(716); -utils.diff = __webpack_require__(700); -utils.extend = __webpack_require__(685); -utils.pick = __webpack_require__(701); -utils.typeOf = __webpack_require__(583); -utils.unique = __webpack_require__(593); - -/** - * Returns true if the platform is windows, or `path.sep` is `\\`. - * This is defined as a function to allow `path.sep` to be set in unit tests, - * or by the user, if there is a reason to do so. - * @return {Boolean} - */ - -utils.isWindows = function() { - return path.sep === '\\' || process.platform === 'win32'; -}; - -/** - * Get the `Snapdragon` instance to use - */ +var next = (global.process && process.nextTick) || global.setImmediate || function (f) { + setTimeout(f, 0) +} -utils.instantiate = function(ast, options) { - var snapdragon; - // if an instance was created by `.parse`, use that instance - if (utils.typeOf(ast) === 'object' && ast.snapdragon) { - snapdragon = ast.snapdragon; - // if the user supplies an instance on options, use that instance - } else if (utils.typeOf(options) === 'object' && options.snapdragon) { - snapdragon = options.snapdragon; - // create a new instance - } else { - snapdragon = new Snapdragon(options); +module.exports = function maybe (cb, promise) { + if (cb) { + promise + .then(function (result) { + next(function () { cb(null, result) }) + }, function (err) { + next(function () { cb(err) }) + }) + return undefined } + else { + return promise + } +} - utils.define(snapdragon, 'parse', function(str, options) { - var parsed = Snapdragon.prototype.parse.apply(this, arguments); - parsed.input = str; - // escape unmatched brace/bracket/parens - var last = this.parser.stack.pop(); - if (last && this.options.strictErrors !== true) { - var open = last.nodes[0]; - var inner = last.nodes[1]; - if (last.type === 'bracket') { - if (inner.val.charAt(0) === '[') { - inner.val = '\\' + inner.val; - } +/***/ }), +/* 735 */ +/***/ (function(module, exports, __webpack_require__) { - } else { - open.val = '\\' + open.val; - var sibling = open.parent.nodes[1]; - if (sibling.type === 'star') { - sibling.loose = true; - } - } - } +"use strict"; - // add non-enumerable parser reference - utils.define(parsed, 'parser', this.parser); - return parsed; - }); - return snapdragon; -}; +module.exports = asyncForEach; /** - * Create the key to use for memoization. The key is generated - * by iterating over the options and concatenating key-value pairs - * to the pattern string. + * Simultaneously processes all items in the given array. + * + * @param {array} array - The array to iterate over + * @param {function} iterator - The function to call for each item in the array + * @param {function} done - The function to call when all iterators have completed */ - -utils.createKey = function(pattern, options) { - if (utils.typeOf(options) !== 'object') { - return pattern; - } - var val = pattern; - var keys = Object.keys(options); - for (var i = 0; i < keys.length; i++) { - var key = keys[i]; - val += ';' + key + '=' + String(options[key]); +function asyncForEach (array, iterator, done) { + if (array.length === 0) { + // NOTE: Normally a bad idea to mix sync and async, but it's safe here because + // of the way that this method is currently used by DirectoryReader. + done(); + return; } - return val; -}; - -/** - * Cast `val` to an array - * @return {Array} - */ - -utils.arrayify = function(val) { - if (typeof val === 'string') return [val]; - return val ? (Array.isArray(val) ? val : [val]) : []; -}; -/** - * Return true if `val` is a non-empty string - */ + // Simultaneously process all items in the array. + let pending = array.length; + array.forEach(item => { + iterator(item, () => { + if (--pending === 0) { + done(); + } + }); + }); +} -utils.isString = function(val) { - return typeof val === 'string'; -}; -/** - * Return true if `val` is a non-empty string - */ +/***/ }), +/* 736 */ +/***/ (function(module, exports, __webpack_require__) { -utils.isObject = function(val) { - return utils.typeOf(val) === 'object'; -}; +"use strict"; -/** - * Returns true if the given `str` has special characters - */ -utils.hasSpecialChars = function(str) { - return /(?:(?:(^|\/)[!.])|[*?+()|\[\]{}]|[+@]\()/.test(str); -}; +module.exports = readdirStream; -/** - * Escape regex characters in the given string - */ +const DirectoryReader = __webpack_require__(726); -utils.escapeRegex = function(str) { - return str.replace(/[-[\]{}()^$|*+?.\\\/\s]/g, '\\$&'); +let streamFacade = { + fs: __webpack_require__(132), + forEach: __webpack_require__(735), + async: true }; /** - * Normalize slashes in the given filepath. + * Returns the {@link stream.Readable} of an asynchronous {@link DirectoryReader}. * - * @param {String} `filepath` - * @return {String} + * @param {string} dir + * @param {object} [options] + * @param {object} internalOptions */ +function readdirStream (dir, options, internalOptions) { + internalOptions.facade = streamFacade; -utils.toPosixPath = function(str) { - return str.replace(/\\+/g, '/'); -}; + let reader = new DirectoryReader(dir, options, internalOptions); + return reader.stream; +} -/** - * Strip backslashes before special characters in a string. - * - * @param {String} `str` - * @return {String} - */ -utils.unescape = function(str) { - return utils.toPosixPath(str.replace(/\\(?=[*+?!.])/g, '')); -}; +/***/ }), +/* 737 */ +/***/ (function(module, exports, __webpack_require__) { -/** - * Strip the prefix from a filepath - * @param {String} `fp` - * @return {String} - */ +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var path = __webpack_require__(4); +var deep_1 = __webpack_require__(738); +var entry_1 = __webpack_require__(740); +var pathUtil = __webpack_require__(739); +var Reader = /** @class */ (function () { + function Reader(options) { + this.options = options; + this.micromatchOptions = this.getMicromatchOptions(); + this.entryFilter = new entry_1.default(options, this.micromatchOptions); + this.deepFilter = new deep_1.default(options, this.micromatchOptions); + } + /** + * Returns root path to scanner. + */ + Reader.prototype.getRootDirectory = function (task) { + return path.resolve(this.options.cwd, task.base); + }; + /** + * Returns options for reader. + */ + Reader.prototype.getReaderOptions = function (task) { + return { + basePath: task.base === '.' ? '' : task.base, + filter: this.entryFilter.getFilter(task.positive, task.negative), + deep: this.deepFilter.getFilter(task.positive, task.negative), + sep: '/' + }; + }; + /** + * Returns options for micromatch. + */ + Reader.prototype.getMicromatchOptions = function () { + return { + dot: this.options.dot, + nobrace: !this.options.brace, + noglobstar: !this.options.globstar, + noext: !this.options.extension, + nocase: !this.options.case, + matchBase: this.options.matchBase + }; + }; + /** + * Returns transformed entry. + */ + Reader.prototype.transform = function (entry) { + if (this.options.absolute) { + entry.path = pathUtil.makeAbsolute(this.options.cwd, entry.path); + } + if (this.options.markDirectories && entry.isDirectory()) { + entry.path += '/'; + } + var item = this.options.stats ? entry : entry.path; + if (this.options.transform === null) { + return item; + } + return this.options.transform(item); + }; + /** + * Returns true if error has ENOENT code. + */ + Reader.prototype.isEnoentCodeError = function (err) { + return err.code === 'ENOENT'; + }; + return Reader; +}()); +exports.default = Reader; -utils.stripPrefix = function(str) { - if (str.charAt(0) !== '.') { - return str; - } - var ch = str.charAt(1); - if (utils.isSlash(ch)) { - return str.slice(2); - } - return str; -}; -/** - * Returns true if the given str is an escaped or - * unescaped path character - */ +/***/ }), +/* 738 */ +/***/ (function(module, exports, __webpack_require__) { -utils.isSlash = function(str) { - return str === '/' || str === '\\/' || str === '\\' || str === '\\\\'; -}; +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var pathUtils = __webpack_require__(739); +var patternUtils = __webpack_require__(573); +var DeepFilter = /** @class */ (function () { + function DeepFilter(options, micromatchOptions) { + this.options = options; + this.micromatchOptions = micromatchOptions; + } + /** + * Returns filter for directories. + */ + DeepFilter.prototype.getFilter = function (positive, negative) { + var _this = this; + var maxPatternDepth = this.getMaxPatternDepth(positive); + var negativeRe = this.getNegativePatternsRe(negative); + return function (entry) { return _this.filter(entry, negativeRe, maxPatternDepth); }; + }; + /** + * Returns max depth of the provided patterns. + */ + DeepFilter.prototype.getMaxPatternDepth = function (patterns) { + var globstar = patterns.some(patternUtils.hasGlobStar); + return globstar ? Infinity : patternUtils.getMaxNaivePatternsDepth(patterns); + }; + /** + * Returns RegExp's for patterns that can affect the depth of reading. + */ + DeepFilter.prototype.getNegativePatternsRe = function (patterns) { + var affectDepthOfReadingPatterns = patterns.filter(patternUtils.isAffectDepthOfReadingPattern); + return patternUtils.convertPatternsToRe(affectDepthOfReadingPatterns, this.micromatchOptions); + }; + /** + * Returns «true» for directory that should be read. + */ + DeepFilter.prototype.filter = function (entry, negativeRe, maxPatternDepth) { + if (this.isSkippedByDeepOption(entry.depth)) { + return false; + } + if (this.isSkippedByMaxPatternDepth(entry.depth, maxPatternDepth)) { + return false; + } + if (this.isSkippedSymlinkedDirectory(entry)) { + return false; + } + if (this.isSkippedDotDirectory(entry)) { + return false; + } + return this.isSkippedByNegativePatterns(entry, negativeRe); + }; + /** + * Returns «true» when the «deep» option is disabled or number and depth of the entry is greater that the option value. + */ + DeepFilter.prototype.isSkippedByDeepOption = function (entryDepth) { + return !this.options.deep || (typeof this.options.deep === 'number' && entryDepth >= this.options.deep); + }; + /** + * Returns «true» when depth parameter is not an Infinity and entry depth greater that the parameter value. + */ + DeepFilter.prototype.isSkippedByMaxPatternDepth = function (entryDepth, maxPatternDepth) { + return maxPatternDepth !== Infinity && entryDepth >= maxPatternDepth; + }; + /** + * Returns «true» for symlinked directory if the «followSymlinkedDirectories» option is disabled. + */ + DeepFilter.prototype.isSkippedSymlinkedDirectory = function (entry) { + return !this.options.followSymlinkedDirectories && entry.isSymbolicLink(); + }; + /** + * Returns «true» for a directory whose name starts with a period if «dot» option is disabled. + */ + DeepFilter.prototype.isSkippedDotDirectory = function (entry) { + return !this.options.dot && pathUtils.isDotDirectory(entry.path); + }; + /** + * Returns «true» for a directory whose path math to any negative pattern. + */ + DeepFilter.prototype.isSkippedByNegativePatterns = function (entry, negativeRe) { + return !patternUtils.matchAny(entry.path, negativeRe); + }; + return DeepFilter; +}()); +exports.default = DeepFilter; -/** - * Returns a function that returns true if the given - * pattern matches or contains a `filepath` - * - * @param {String} `pattern` - * @return {Function} - */ -utils.matchPath = function(pattern, options) { - return (options && options.contains) - ? utils.containsPattern(pattern, options) - : utils.equalsPattern(pattern, options); -}; +/***/ }), +/* 739 */ +/***/ (function(module, exports, __webpack_require__) { -/** - * Returns true if the given (original) filepath or unixified path are equal - * to the given pattern. - */ +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var path = __webpack_require__(4); +/** + * Returns «true» if the last partial of the path starting with a period. + */ +function isDotDirectory(filepath) { + return path.basename(filepath).startsWith('.'); +} +exports.isDotDirectory = isDotDirectory; +/** + * Convert a windows-like path to a unix-style path. + */ +function normalize(filepath) { + return filepath.replace(/\\/g, '/'); +} +exports.normalize = normalize; +/** + * Returns normalized absolute path of provided filepath. + */ +function makeAbsolute(cwd, filepath) { + return normalize(path.resolve(cwd, filepath)); +} +exports.makeAbsolute = makeAbsolute; -utils._equals = function(filepath, unixPath, pattern) { - return pattern === filepath || pattern === unixPath; -}; -/** - * Returns true if the given (original) filepath or unixified path contain - * the given pattern. - */ +/***/ }), +/* 740 */ +/***/ (function(module, exports, __webpack_require__) { -utils._contains = function(filepath, unixPath, pattern) { - return filepath.indexOf(pattern) !== -1 || unixPath.indexOf(pattern) !== -1; -}; +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var pathUtils = __webpack_require__(739); +var patternUtils = __webpack_require__(573); +var EntryFilter = /** @class */ (function () { + function EntryFilter(options, micromatchOptions) { + this.options = options; + this.micromatchOptions = micromatchOptions; + this.index = new Map(); + } + /** + * Returns filter for directories. + */ + EntryFilter.prototype.getFilter = function (positive, negative) { + var _this = this; + var positiveRe = patternUtils.convertPatternsToRe(positive, this.micromatchOptions); + var negativeRe = patternUtils.convertPatternsToRe(negative, this.micromatchOptions); + return function (entry) { return _this.filter(entry, positiveRe, negativeRe); }; + }; + /** + * Returns true if entry must be added to result. + */ + EntryFilter.prototype.filter = function (entry, positiveRe, negativeRe) { + // Exclude duplicate results + if (this.options.unique) { + if (this.isDuplicateEntry(entry)) { + return false; + } + this.createIndexRecord(entry); + } + // Filter files and directories by options + if (this.onlyFileFilter(entry) || this.onlyDirectoryFilter(entry)) { + return false; + } + if (this.isSkippedByAbsoluteNegativePatterns(entry, negativeRe)) { + return false; + } + return this.isMatchToPatterns(entry.path, positiveRe) && !this.isMatchToPatterns(entry.path, negativeRe); + }; + /** + * Return true if the entry already has in the cross reader index. + */ + EntryFilter.prototype.isDuplicateEntry = function (entry) { + return this.index.has(entry.path); + }; + /** + * Create record in the cross reader index. + */ + EntryFilter.prototype.createIndexRecord = function (entry) { + this.index.set(entry.path, undefined); + }; + /** + * Returns true for non-files if the «onlyFiles» option is enabled. + */ + EntryFilter.prototype.onlyFileFilter = function (entry) { + return this.options.onlyFiles && !entry.isFile(); + }; + /** + * Returns true for non-directories if the «onlyDirectories» option is enabled. + */ + EntryFilter.prototype.onlyDirectoryFilter = function (entry) { + return this.options.onlyDirectories && !entry.isDirectory(); + }; + /** + * Return true when `absolute` option is enabled and matched to the negative patterns. + */ + EntryFilter.prototype.isSkippedByAbsoluteNegativePatterns = function (entry, negativeRe) { + if (!this.options.absolute) { + return false; + } + var fullpath = pathUtils.makeAbsolute(this.options.cwd, entry.path); + return this.isMatchToPatterns(fullpath, negativeRe); + }; + /** + * Return true when entry match to provided patterns. + * + * First, just trying to apply patterns to the path. + * Second, trying to apply patterns to the path with final slash (need to micromatch to support «directory/**» patterns). + */ + EntryFilter.prototype.isMatchToPatterns = function (filepath, patternsRe) { + return patternUtils.matchAny(filepath, patternsRe) || patternUtils.matchAny(filepath + '/', patternsRe); + }; + return EntryFilter; +}()); +exports.default = EntryFilter; -/** - * Returns a function that returns true if the given - * pattern is the same as a given `filepath` - * - * @param {String} `pattern` - * @return {Function} - */ -utils.equalsPattern = function(pattern, options) { - var unixify = utils.unixify(options); - options = options || {}; +/***/ }), +/* 741 */ +/***/ (function(module, exports, __webpack_require__) { - return function fn(filepath) { - var equal = utils._equals(filepath, unixify(filepath), pattern); - if (equal === true || options.nocase !== true) { - return equal; - } - var lower = filepath.toLowerCase(); - return utils._equals(lower, unixify(lower), pattern); - }; -}; +"use strict"; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var stream = __webpack_require__(173); +var fsStat = __webpack_require__(742); +var fs_1 = __webpack_require__(746); +var FileSystemStream = /** @class */ (function (_super) { + __extends(FileSystemStream, _super); + function FileSystemStream() { + return _super !== null && _super.apply(this, arguments) || this; + } + /** + * Use stream API to read entries for Task. + */ + FileSystemStream.prototype.read = function (patterns, filter) { + var _this = this; + var filepaths = patterns.map(this.getFullEntryPath, this); + var transform = new stream.Transform({ objectMode: true }); + transform._transform = function (index, _enc, done) { + return _this.getEntry(filepaths[index], patterns[index]).then(function (entry) { + if (entry !== null && filter(entry)) { + transform.push(entry); + } + if (index === filepaths.length - 1) { + transform.end(); + } + done(); + }); + }; + for (var i = 0; i < filepaths.length; i++) { + transform.write(i); + } + return transform; + }; + /** + * Return entry for the provided path. + */ + FileSystemStream.prototype.getEntry = function (filepath, pattern) { + var _this = this; + return this.getStat(filepath) + .then(function (stat) { return _this.makeEntry(stat, pattern); }) + .catch(function () { return null; }); + }; + /** + * Return fs.Stats for the provided path. + */ + FileSystemStream.prototype.getStat = function (filepath) { + return fsStat.stat(filepath, { throwErrorOnBrokenSymlinks: false }); + }; + return FileSystemStream; +}(fs_1.default)); +exports.default = FileSystemStream; -/** - * Returns a function that returns true if the given - * pattern contains a `filepath` - * - * @param {String} `pattern` - * @return {Function} - */ -utils.containsPattern = function(pattern, options) { - var unixify = utils.unixify(options); - options = options || {}; +/***/ }), +/* 742 */ +/***/ (function(module, exports, __webpack_require__) { - return function(filepath) { - var contains = utils._contains(filepath, unixify(filepath), pattern); - if (contains === true || options.nocase !== true) { - return contains; - } - var lower = filepath.toLowerCase(); - return utils._contains(lower, unixify(lower), pattern); - }; -}; +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const optionsManager = __webpack_require__(743); +const statProvider = __webpack_require__(745); /** - * Returns a function that returns true if the given - * regex matches the `filename` of a file path. - * - * @param {RegExp} `re` Matching regex - * @return {Function} + * Asynchronous API. */ - -utils.matchBasename = function(re) { - return function(filepath) { - return re.test(path.basename(filepath)); - }; -}; - +function stat(path, opts) { + return new Promise((resolve, reject) => { + statProvider.async(path, optionsManager.prepare(opts), (err, stats) => err ? reject(err) : resolve(stats)); + }); +} +exports.stat = stat; +function statCallback(path, optsOrCallback, callback) { + if (typeof optsOrCallback === 'function') { + callback = optsOrCallback; /* tslint:disable-line: no-parameter-reassignment */ + optsOrCallback = undefined; /* tslint:disable-line: no-parameter-reassignment */ + } + if (typeof callback === 'undefined') { + throw new TypeError('The "callback" argument must be of type Function.'); + } + statProvider.async(path, optionsManager.prepare(optsOrCallback), callback); +} +exports.statCallback = statCallback; /** - * Determines the filepath to return based on the provided options. - * @return {any} + * Synchronous API. */ +function statSync(path, opts) { + return statProvider.sync(path, optionsManager.prepare(opts)); +} +exports.statSync = statSync; -utils.value = function(str, unixify, options) { - if (options && options.unixify === false) { - return str; - } - return unixify(str); -}; -/** - * Returns a function that normalizes slashes in a string to forward - * slashes, strips `./` from beginning of paths, and optionally unescapes - * special characters. - * @return {Function} - */ +/***/ }), +/* 743 */ +/***/ (function(module, exports, __webpack_require__) { -utils.unixify = function(options) { - options = options || {}; - return function(filepath) { - if (utils.isWindows() || options.unixify === true) { - filepath = utils.toPosixPath(filepath); - } - if (options.stripPrefix !== false) { - filepath = utils.stripPrefix(filepath); - } - if (options.unescape === true) { - filepath = utils.unescape(filepath); - } - return filepath; - }; -}; +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const fsAdapter = __webpack_require__(744); +function prepare(opts) { + const options = Object.assign({ + fs: fsAdapter.getFileSystemAdapter(opts ? opts.fs : undefined), + throwErrorOnBrokenSymlinks: true, + followSymlinks: true + }, opts); + return options; +} +exports.prepare = prepare; /***/ }), -/* 716 */ +/* 744 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -/*! - * define-property - * - * Copyright (c) 2015-2018, Jon Schlinkert. - * Released under the MIT License. - */ +Object.defineProperty(exports, "__esModule", { value: true }); +const fs = __webpack_require__(132); +exports.FILE_SYSTEM_ADAPTER = { + lstat: fs.lstat, + stat: fs.stat, + lstatSync: fs.lstatSync, + statSync: fs.statSync +}; +function getFileSystemAdapter(fsMethods) { + if (!fsMethods) { + return exports.FILE_SYSTEM_ADAPTER; + } + return Object.assign({}, exports.FILE_SYSTEM_ADAPTER, fsMethods); +} +exports.getFileSystemAdapter = getFileSystemAdapter; -var isobject = __webpack_require__(581); -var isDescriptor = __webpack_require__(582); -var define = (typeof Reflect !== 'undefined' && Reflect.defineProperty) - ? Reflect.defineProperty - : Object.defineProperty; +/***/ }), +/* 745 */ +/***/ (function(module, exports, __webpack_require__) { -module.exports = function defineProperty(obj, key, val) { - if (!isobject(obj) && typeof obj !== 'function' && !Array.isArray(obj)) { - throw new TypeError('expected an object, function, or array'); - } +"use strict"; - if (typeof key !== 'string') { - throw new TypeError('expected "key" to be a string'); - } +Object.defineProperty(exports, "__esModule", { value: true }); +function sync(path, options) { + const lstat = options.fs.lstatSync(path); + if (!isFollowedSymlink(lstat, options)) { + return lstat; + } + try { + const stat = options.fs.statSync(path); + stat.isSymbolicLink = () => true; + return stat; + } + catch (err) { + if (!options.throwErrorOnBrokenSymlinks) { + return lstat; + } + throw err; + } +} +exports.sync = sync; +function async(path, options, callback) { + options.fs.lstat(path, (err0, lstat) => { + if (err0) { + return callback(err0, undefined); + } + if (!isFollowedSymlink(lstat, options)) { + return callback(null, lstat); + } + options.fs.stat(path, (err1, stat) => { + if (err1) { + return options.throwErrorOnBrokenSymlinks ? callback(err1) : callback(null, lstat); + } + stat.isSymbolicLink = () => true; + callback(null, stat); + }); + }); +} +exports.async = async; +/** + * Returns `true` for followed symlink. + */ +function isFollowedSymlink(stat, options) { + return stat.isSymbolicLink() && options.followSymlinks; +} +exports.isFollowedSymlink = isFollowedSymlink; - if (isDescriptor(val)) { - define(obj, key, val); - return obj; - } - define(obj, key, { - configurable: true, - enumerable: false, - writable: true, - value: val - }); +/***/ }), +/* 746 */ +/***/ (function(module, exports, __webpack_require__) { - return obj; -}; +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var path = __webpack_require__(4); +var FileSystem = /** @class */ (function () { + function FileSystem(options) { + this.options = options; + } + /** + * Return full path to entry. + */ + FileSystem.prototype.getFullEntryPath = function (filepath) { + return path.resolve(this.options.cwd, filepath); + }; + /** + * Return an implementation of the Entry interface. + */ + FileSystem.prototype.makeEntry = function (stat, pattern) { + stat.path = pattern; + stat.depth = pattern.split('/').length; + return stat; + }; + return FileSystem; +}()); +exports.default = FileSystem; /***/ }), -/* 717 */ +/* 747 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82254,15 +86196,28 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var readdir = __webpack_require__(718); -var reader_1 = __webpack_require__(731); -var fs_stream_1 = __webpack_require__(735); -var ReaderAsync = /** @class */ (function (_super) { - __extends(ReaderAsync, _super); - function ReaderAsync() { +var stream = __webpack_require__(173); +var readdir = __webpack_require__(724); +var reader_1 = __webpack_require__(737); +var fs_stream_1 = __webpack_require__(741); +var TransformStream = /** @class */ (function (_super) { + __extends(TransformStream, _super); + function TransformStream(reader) { + var _this = _super.call(this, { objectMode: true }) || this; + _this.reader = reader; + return _this; + } + TransformStream.prototype._transform = function (entry, _encoding, callback) { + callback(null, this.reader.transform(entry)); + }; + return TransformStream; +}(stream.Transform)); +var ReaderStream = /** @class */ (function (_super) { + __extends(ReaderStream, _super); + function ReaderStream() { return _super !== null && _super.apply(this, arguments) || this; } - Object.defineProperty(ReaderAsync.prototype, "fsAdapter", { + Object.defineProperty(ReaderStream.prototype, "fsAdapter", { /** * Returns FileSystem adapter. */ @@ -82273,27 +86228,22 @@ var ReaderAsync = /** @class */ (function (_super) { configurable: true }); /** - * Use async API to read entries for Task. + * Use stream API to read entries for Task. */ - ReaderAsync.prototype.read = function (task) { + ReaderStream.prototype.read = function (task) { var _this = this; var root = this.getRootDirectory(task); var options = this.getReaderOptions(task); - var entries = []; - return new Promise(function (resolve, reject) { - var stream = _this.api(root, task, options); - stream.on('error', function (err) { - _this.isEnoentCodeError(err) ? resolve([]) : reject(err); - stream.pause(); - }); - stream.on('data', function (entry) { return entries.push(_this.transform(entry)); }); - stream.on('end', function () { return resolve(entries); }); - }); + var transform = new TransformStream(this); + var readable = this.api(root, task, options); + return readable + .on('error', function (err) { return _this.isEnoentCodeError(err) ? null : transform.emit('error', err); }) + .pipe(transform); }; /** * Returns founded paths. */ - ReaderAsync.prototype.api = function (root, task, options) { + ReaderStream.prototype.api = function (root, task, options) { if (task.dynamic) { return this.dynamicApi(root, options); } @@ -82302,6648 +86252,6908 @@ var ReaderAsync = /** @class */ (function (_super) { /** * Api for dynamic tasks. */ - ReaderAsync.prototype.dynamicApi = function (root, options) { + ReaderStream.prototype.dynamicApi = function (root, options) { return readdir.readdirStreamStat(root, options); }; /** * Api for static tasks. */ - ReaderAsync.prototype.staticApi = function (task, options) { + ReaderStream.prototype.staticApi = function (task, options) { return this.fsAdapter.read(task.patterns, options.filter); }; - return ReaderAsync; + return ReaderStream; }(reader_1.default)); -exports.default = ReaderAsync; +exports.default = ReaderStream; /***/ }), -/* 718 */ +/* 748 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var readdir = __webpack_require__(724); +var reader_1 = __webpack_require__(737); +var fs_sync_1 = __webpack_require__(749); +var ReaderSync = /** @class */ (function (_super) { + __extends(ReaderSync, _super); + function ReaderSync() { + return _super !== null && _super.apply(this, arguments) || this; + } + Object.defineProperty(ReaderSync.prototype, "fsAdapter", { + /** + * Returns FileSystem adapter. + */ + get: function () { + return new fs_sync_1.default(this.options); + }, + enumerable: true, + configurable: true + }); + /** + * Use sync API to read entries for Task. + */ + ReaderSync.prototype.read = function (task) { + var root = this.getRootDirectory(task); + var options = this.getReaderOptions(task); + try { + var entries = this.api(root, task, options); + return entries.map(this.transform, this); + } + catch (err) { + if (this.isEnoentCodeError(err)) { + return []; + } + throw err; + } + }; + /** + * Returns founded paths. + */ + ReaderSync.prototype.api = function (root, task, options) { + if (task.dynamic) { + return this.dynamicApi(root, options); + } + return this.staticApi(task, options); + }; + /** + * Api for dynamic tasks. + */ + ReaderSync.prototype.dynamicApi = function (root, options) { + return readdir.readdirSyncStat(root, options); + }; + /** + * Api for static tasks. + */ + ReaderSync.prototype.staticApi = function (task, options) { + return this.fsAdapter.read(task.patterns, options.filter); + }; + return ReaderSync; +}(reader_1.default)); +exports.default = ReaderSync; + + +/***/ }), +/* 749 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; + +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +var fsStat = __webpack_require__(742); +var fs_1 = __webpack_require__(746); +var FileSystemSync = /** @class */ (function (_super) { + __extends(FileSystemSync, _super); + function FileSystemSync() { + return _super !== null && _super.apply(this, arguments) || this; + } + /** + * Use sync API to read entries for Task. + */ + FileSystemSync.prototype.read = function (patterns, filter) { + var _this = this; + var entries = []; + patterns.forEach(function (pattern) { + var filepath = _this.getFullEntryPath(pattern); + var entry = _this.getEntry(filepath, pattern); + if (entry === null || !filter(entry)) { + return; + } + entries.push(entry); + }); + return entries; + }; + /** + * Return entry for the provided path. + */ + FileSystemSync.prototype.getEntry = function (filepath, pattern) { + try { + var stat = this.getStat(filepath); + return this.makeEntry(stat, pattern); + } + catch (err) { + return null; + } + }; + /** + * Return fs.Stats for the provided path. + */ + FileSystemSync.prototype.getStat = function (filepath) { + return fsStat.statSync(filepath, { throwErrorOnBrokenSymlinks: false }); + }; + return FileSystemSync; +}(fs_1.default)); +exports.default = FileSystemSync; -const readdirSync = __webpack_require__(719); -const readdirAsync = __webpack_require__(727); -const readdirStream = __webpack_require__(730); +/***/ }), +/* 750 */ +/***/ (function(module, exports, __webpack_require__) { -module.exports = exports = readdirAsyncPath; -exports.readdir = exports.readdirAsync = exports.async = readdirAsyncPath; -exports.readdirAsyncStat = exports.async.stat = readdirAsyncStat; -exports.readdirStream = exports.stream = readdirStreamPath; -exports.readdirStreamStat = exports.stream.stat = readdirStreamStat; -exports.readdirSync = exports.sync = readdirSyncPath; -exports.readdirSyncStat = exports.sync.stat = readdirSyncStat; +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +/** + * Flatten nested arrays (max depth is 2) into a non-nested array of non-array items. + */ +function flatten(items) { + return items.reduce(function (collection, item) { return [].concat(collection, item); }, []); +} +exports.flatten = flatten; -/** - * Synchronous readdir that returns an array of string paths. - * - * @param {string} dir - * @param {object} [options] - * @returns {string[]} - */ -function readdirSyncPath (dir, options) { - return readdirSync(dir, options, {}); -} -/** - * Synchronous readdir that returns results as an array of {@link fs.Stats} objects - * - * @param {string} dir - * @param {object} [options] - * @returns {fs.Stats[]} - */ -function readdirSyncStat (dir, options) { - return readdirSync(dir, options, { stats: true }); -} +/***/ }), +/* 751 */ +/***/ (function(module, exports, __webpack_require__) { -/** - * Aynchronous readdir (accepts an error-first callback or returns a {@link Promise}). - * Results are an array of path strings. - * - * @param {string} dir - * @param {object} [options] - * @param {function} [callback] - * @returns {Promise} - */ -function readdirAsyncPath (dir, options, callback) { - return readdirAsync(dir, options, callback, {}); -} +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var merge2 = __webpack_require__(243); +/** + * Merge multiple streams and propagate their errors into one stream in parallel. + */ +function merge(streams) { + var mergedStream = merge2(streams); + streams.forEach(function (stream) { + stream.on('error', function (err) { return mergedStream.emit('error', err); }); + }); + return mergedStream; +} +exports.merge = merge; -/** - * Aynchronous readdir (accepts an error-first callback or returns a {@link Promise}). - * Results are an array of {@link fs.Stats} objects. - * - * @param {string} dir - * @param {object} [options] - * @param {function} [callback] - * @returns {Promise} - */ -function readdirAsyncStat (dir, options, callback) { - return readdirAsync(dir, options, callback, { stats: true }); -} -/** - * Aynchronous readdir that returns a {@link stream.Readable} (which is also an {@link EventEmitter}). - * All stream data events ("data", "file", "directory", "symlink") are passed a path string. - * - * @param {string} dir - * @param {object} [options] - * @returns {stream.Readable} - */ -function readdirStreamPath (dir, options) { - return readdirStream(dir, options, {}); +/***/ }), +/* 752 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +const path = __webpack_require__(4); +const pathType = __webpack_require__(753); + +const getExtensions = extensions => extensions.length > 1 ? `{${extensions.join(',')}}` : extensions[0]; + +const getPath = (filepath, cwd) => { + const pth = filepath[0] === '!' ? filepath.slice(1) : filepath; + return path.isAbsolute(pth) ? pth : path.join(cwd, pth); +}; + +const addExtensions = (file, extensions) => { + if (path.extname(file)) { + return `**/${file}`; + } + + return `**/${file}.${getExtensions(extensions)}`; +}; + +const getGlob = (dir, opts) => { + if (opts.files && !Array.isArray(opts.files)) { + throw new TypeError(`Expected \`files\` to be of type \`Array\` but received type \`${typeof opts.files}\``); + } + + if (opts.extensions && !Array.isArray(opts.extensions)) { + throw new TypeError(`Expected \`extensions\` to be of type \`Array\` but received type \`${typeof opts.extensions}\``); + } + + if (opts.files && opts.extensions) { + return opts.files.map(x => path.join(dir, addExtensions(x, opts.extensions))); + } + + if (opts.files) { + return opts.files.map(x => path.join(dir, `**/${x}`)); + } + + if (opts.extensions) { + return [path.join(dir, `**/*.${getExtensions(opts.extensions)}`)]; + } + + return [path.join(dir, '**')]; +}; + +module.exports = (input, opts) => { + opts = Object.assign({cwd: process.cwd()}, opts); + + if (typeof opts.cwd !== 'string') { + return Promise.reject(new TypeError(`Expected \`cwd\` to be of type \`string\` but received type \`${typeof opts.cwd}\``)); + } + + return Promise.all([].concat(input).map(x => pathType.dir(getPath(x, opts.cwd)) + .then(isDir => isDir ? getGlob(x, opts) : x))) + .then(globs => [].concat.apply([], globs)); +}; + +module.exports.sync = (input, opts) => { + opts = Object.assign({cwd: process.cwd()}, opts); + + if (typeof opts.cwd !== 'string') { + throw new TypeError(`Expected \`cwd\` to be of type \`string\` but received type \`${typeof opts.cwd}\``); + } + + const globs = [].concat(input).map(x => pathType.dirSync(getPath(x, opts.cwd)) ? getGlob(x, opts) : x); + return [].concat.apply([], globs); +}; + + +/***/ }), +/* 753 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +const fs = __webpack_require__(132); +const pify = __webpack_require__(754); + +function type(fn, fn2, fp) { + if (typeof fp !== 'string') { + return Promise.reject(new TypeError(`Expected a string, got ${typeof fp}`)); + } + + return pify(fs[fn])(fp) + .then(stats => stats[fn2]()) + .catch(err => { + if (err.code === 'ENOENT') { + return false; + } + + throw err; + }); } -/** - * Aynchronous readdir that returns a {@link stream.Readable} (which is also an {@link EventEmitter}) - * All stream data events ("data", "file", "directory", "symlink") are passed an {@link fs.Stats} object. - * - * @param {string} dir - * @param {object} [options] - * @returns {stream.Readable} - */ -function readdirStreamStat (dir, options) { - return readdirStream(dir, options, { stats: true }); +function typeSync(fn, fn2, fp) { + if (typeof fp !== 'string') { + throw new TypeError(`Expected a string, got ${typeof fp}`); + } + + try { + return fs[fn](fp)[fn2](); + } catch (err) { + if (err.code === 'ENOENT') { + return false; + } + + throw err; + } } +exports.file = type.bind(null, 'stat', 'isFile'); +exports.dir = type.bind(null, 'stat', 'isDirectory'); +exports.symlink = type.bind(null, 'lstat', 'isSymbolicLink'); +exports.fileSync = typeSync.bind(null, 'statSync', 'isFile'); +exports.dirSync = typeSync.bind(null, 'statSync', 'isDirectory'); +exports.symlinkSync = typeSync.bind(null, 'lstatSync', 'isSymbolicLink'); + /***/ }), -/* 719 */ +/* 754 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -module.exports = readdirSync; +const processFn = (fn, opts) => function () { + const P = opts.promiseModule; + const args = new Array(arguments.length); -const DirectoryReader = __webpack_require__(720); + for (let i = 0; i < arguments.length; i++) { + args[i] = arguments[i]; + } -let syncFacade = { - fs: __webpack_require__(725), - forEach: __webpack_require__(726), - sync: true + return new P((resolve, reject) => { + if (opts.errorFirst) { + args.push(function (err, result) { + if (opts.multiArgs) { + const results = new Array(arguments.length - 1); + + for (let i = 1; i < arguments.length; i++) { + results[i - 1] = arguments[i]; + } + + if (err) { + results.unshift(err); + reject(results); + } else { + resolve(results); + } + } else if (err) { + reject(err); + } else { + resolve(result); + } + }); + } else { + args.push(function (result) { + if (opts.multiArgs) { + const results = new Array(arguments.length - 1); + + for (let i = 0; i < arguments.length; i++) { + results[i] = arguments[i]; + } + + resolve(results); + } else { + resolve(result); + } + }); + } + + fn.apply(this, args); + }); }; -/** - * Returns the buffered output from a synchronous {@link DirectoryReader}. - * - * @param {string} dir - * @param {object} [options] - * @param {object} internalOptions - */ -function readdirSync (dir, options, internalOptions) { - internalOptions.facade = syncFacade; +module.exports = (obj, opts) => { + opts = Object.assign({ + exclude: [/.+(Sync|Stream)$/], + errorFirst: true, + promiseModule: Promise + }, opts); - let reader = new DirectoryReader(dir, options, internalOptions); - let stream = reader.stream; + const filter = key => { + const match = pattern => typeof pattern === 'string' ? key === pattern : pattern.test(key); + return opts.include ? opts.include.some(match) : !opts.exclude.some(match); + }; - let results = []; - let data = stream.read(); - while (data !== null) { - results.push(data); - data = stream.read(); - } + let ret; + if (typeof obj === 'function') { + ret = function () { + if (opts.excludeMain) { + return obj.apply(this, arguments); + } - return results; -} + return processFn(obj, opts).apply(this, arguments); + }; + } else { + ret = Object.create(Object.getPrototypeOf(obj)); + } + + for (const key in obj) { // eslint-disable-line guard-for-in + const x = obj[key]; + ret[key] = typeof x === 'function' && filter(key) ? processFn(x, opts) : x; + } + + return ret; +}; /***/ }), -/* 720 */ +/* 755 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -const Readable = __webpack_require__(173).Readable; -const EventEmitter = __webpack_require__(164).EventEmitter; +const fs = __webpack_require__(132); const path = __webpack_require__(4); -const normalizeOptions = __webpack_require__(721); -const stat = __webpack_require__(723); -const call = __webpack_require__(724); - -/** - * Asynchronously reads the contents of a directory and streams the results - * via a {@link stream.Readable}. - */ -class DirectoryReader { - /** - * @param {string} dir - The absolute or relative directory path to read - * @param {object} [options] - User-specified options, if any (see {@link normalizeOptions}) - * @param {object} internalOptions - Internal options that aren't part of the public API - * @class - */ - constructor (dir, options, internalOptions) { - this.options = options = normalizeOptions(options, internalOptions); - - // Indicates whether we should keep reading - // This is set false if stream.Readable.push() returns false. - this.shouldRead = true; +const fastGlob = __webpack_require__(569); +const gitIgnore = __webpack_require__(756); +const pify = __webpack_require__(410); +const slash = __webpack_require__(757); - // The directories to read - // (initialized with the top-level directory) - this.queue = [{ - path: dir, - basePath: options.basePath, - posixBasePath: options.posixBasePath, - depth: 0 - }]; +const DEFAULT_IGNORE = [ + '**/node_modules/**', + '**/bower_components/**', + '**/flow-typed/**', + '**/coverage/**', + '**/.git' +]; - // The number of directories that are currently being processed - this.pending = 0; +const readFileP = pify(fs.readFile); - // The data that has been read, but not yet emitted - this.buffer = []; +const mapGitIgnorePatternTo = base => ignore => { + if (ignore.startsWith('!')) { + return '!' + path.posix.join(base, ignore.slice(1)); + } - this.stream = new Readable({ objectMode: true }); - this.stream._read = () => { - // Start (or resume) reading - this.shouldRead = true; + return path.posix.join(base, ignore); +}; - // If we have data in the buffer, then send the next chunk - if (this.buffer.length > 0) { - this.pushFromBuffer(); - } +const parseGitIgnore = (content, options) => { + const base = slash(path.relative(options.cwd, path.dirname(options.fileName))); - // If we have directories queued, then start processing the next one - if (this.queue.length > 0) { - if (this.options.facade.sync) { - while (this.queue.length > 0) { - this.readNextDirectory(); - } - } - else { - this.readNextDirectory(); - } - } + return content + .split(/\r?\n/) + .filter(Boolean) + .filter(line => line.charAt(0) !== '#') + .map(mapGitIgnorePatternTo(base)); +}; - this.checkForEOF(); - }; - } +const reduceIgnore = files => { + return files.reduce((ignores, file) => { + ignores.add(parseGitIgnore(file.content, { + cwd: file.cwd, + fileName: file.filePath + })); + return ignores; + }, gitIgnore()); +}; - /** - * Reads the next directory in the queue - */ - readNextDirectory () { - let facade = this.options.facade; - let dir = this.queue.shift(); - this.pending++; +const getIsIgnoredPredecate = (ignores, cwd) => { + return p => ignores.ignores(slash(path.relative(cwd, p))); +}; - // Read the directory listing - call.safe(facade.fs.readdir, dir.path, (err, items) => { - if (err) { - // fs.readdir threw an error - this.emit('error', err); - return this.finishedReadingDirectory(); - } +const getFile = (file, cwd) => { + const filePath = path.join(cwd, file); + return readFileP(filePath, 'utf8') + .then(content => ({ + content, + cwd, + filePath + })); +}; - try { - // Process each item in the directory (simultaneously, if async) - facade.forEach( - items, - this.processItem.bind(this, dir), - this.finishedReadingDirectory.bind(this, dir) - ); - } - catch (err2) { - // facade.forEach threw an error - // (probably because fs.readdir returned an invalid result) - this.emit('error', err2); - this.finishedReadingDirectory(); - } - }); - } +const getFileSync = (file, cwd) => { + const filePath = path.join(cwd, file); + const content = fs.readFileSync(filePath, 'utf8'); - /** - * This method is called after all items in a directory have been processed. - * - * NOTE: This does not necessarily mean that the reader is finished, since there may still - * be other directories queued or pending. - */ - finishedReadingDirectory () { - this.pending--; + return { + content, + cwd, + filePath + }; +}; - if (this.shouldRead) { - // If we have directories queued, then start processing the next one - if (this.queue.length > 0 && this.options.facade.async) { - this.readNextDirectory(); - } +const normalizeOptions = (options = {}) => { + const ignore = options.ignore || []; + const cwd = options.cwd || process.cwd(); + return {ignore, cwd}; +}; - this.checkForEOF(); - } - } +module.exports = options => { + options = normalizeOptions(options); - /** - * Determines whether the reader has finished processing all items in all directories. - * If so, then the "end" event is fired (via {@Readable#push}) - */ - checkForEOF () { - if (this.buffer.length === 0 && // The stuff we've already read - this.pending === 0 && // The stuff we're currently reading - this.queue.length === 0) { // The stuff we haven't read yet - // There's no more stuff! - this.stream.push(null); - } - } + return fastGlob('**/.gitignore', { + ignore: DEFAULT_IGNORE.concat(options.ignore), + cwd: options.cwd + }) + .then(paths => Promise.all(paths.map(file => getFile(file, options.cwd)))) + .then(files => reduceIgnore(files)) + .then(ignores => getIsIgnoredPredecate(ignores, options.cwd)); +}; - /** - * Processes a single item in a directory. - * - * If the item is a directory, and `option.deep` is enabled, then the item will be added - * to the directory queue. - * - * If the item meets the filter criteria, then it will be emitted to the reader's stream. - * - * @param {object} dir - A directory object from the queue - * @param {string} item - The name of the item (name only, no path) - * @param {function} done - A callback function that is called after the item has been processed - */ - processItem (dir, item, done) { - let stream = this.stream; - let options = this.options; +module.exports.sync = options => { + options = normalizeOptions(options); - let itemPath = dir.basePath + item; - let posixPath = dir.posixBasePath + item; - let fullPath = path.join(dir.path, item); + const paths = fastGlob.sync('**/.gitignore', { + ignore: DEFAULT_IGNORE.concat(options.ignore), + cwd: options.cwd + }); + const files = paths.map(file => getFileSync(file, options.cwd)); + const ignores = reduceIgnore(files); - // If `options.deep` is a number, and we've already recursed to the max depth, - // then there's no need to check fs.Stats to know if it's a directory. - // If `options.deep` is a function, then we'll need fs.Stats - let maxDepthReached = dir.depth >= options.recurseDepth; + return getIsIgnoredPredecate(ignores, options.cwd); +}; - // Do we need to call `fs.stat`? - let needStats = - !maxDepthReached || // we need the fs.Stats to know if it's a directory - options.stats || // the user wants fs.Stats objects returned - options.recurseFn || // we need fs.Stats for the recurse function - options.filterFn || // we need fs.Stats for the filter function - EventEmitter.listenerCount(stream, 'file') || // we need the fs.Stats to know if it's a file - EventEmitter.listenerCount(stream, 'directory') || // we need the fs.Stats to know if it's a directory - EventEmitter.listenerCount(stream, 'symlink'); // we need the fs.Stats to know if it's a symlink - // If we don't need stats, then exit early - if (!needStats) { - if (this.filter(itemPath, posixPath)) { - this.pushOrBuffer({ data: itemPath }); - } - return done(); - } +/***/ }), +/* 756 */ +/***/ (function(module, exports) { - // Get the fs.Stats object for this path - stat(options.facade.fs, fullPath, (err, stats) => { - if (err) { - // fs.stat threw an error - this.emit('error', err); - return done(); - } +// A simple implementation of make-array +function make_array (subject) { + return Array.isArray(subject) + ? subject + : [subject] +} - try { - // Add the item's path to the fs.Stats object - // The base of this path, and its separators are determined by the options - // (i.e. options.basePath and options.sep) - stats.path = itemPath; +const REGEX_BLANK_LINE = /^\s+$/ +const REGEX_LEADING_EXCAPED_EXCLAMATION = /^\\!/ +const REGEX_LEADING_EXCAPED_HASH = /^\\#/ +const SLASH = '/' +const KEY_IGNORE = typeof Symbol !== 'undefined' + ? Symbol.for('node-ignore') + /* istanbul ignore next */ + : 'node-ignore' - // Add depth of the path to the fs.Stats object for use this in the filter function - stats.depth = dir.depth; +const define = (object, key, value) => + Object.defineProperty(object, key, {value}) - if (this.shouldRecurse(stats, posixPath, maxDepthReached)) { - // Add this subdirectory to the queue - this.queue.push({ - path: fullPath, - basePath: itemPath + options.sep, - posixBasePath: posixPath + '/', - depth: dir.depth + 1, - }); - } +const REGEX_REGEXP_RANGE = /([0-z])-([0-z])/g - // Determine whether this item matches the filter criteria - if (this.filter(stats, posixPath)) { - this.pushOrBuffer({ - data: options.stats ? stats : itemPath, - file: stats.isFile(), - directory: stats.isDirectory(), - symlink: stats.isSymbolicLink(), - }); - } +// Sanitize the range of a regular expression +// The cases are complicated, see test cases for details +const sanitizeRange = range => range.replace( + REGEX_REGEXP_RANGE, + (match, from, to) => from.charCodeAt(0) <= to.charCodeAt(0) + ? match + // Invalid range (out of order) which is ok for gitignore rules but + // fatal for JavaScript regular expression, so eliminate it. + : '' +) - done(); - } - catch (err2) { - // An error occurred while processing the item - // (probably during a user-specified function, such as options.deep, options.filter, etc.) - this.emit('error', err2); - done(); - } - }); - } +// > If the pattern ends with a slash, +// > it is removed for the purpose of the following description, +// > but it would only find a match with a directory. +// > In other words, foo/ will match a directory foo and paths underneath it, +// > but will not match a regular file or a symbolic link foo +// > (this is consistent with the way how pathspec works in general in Git). +// '`foo/`' will not match regular file '`foo`' or symbolic link '`foo`' +// -> ignore-rules will not deal with it, because it costs extra `fs.stat` call +// you could use option `mark: true` with `glob` - /** - * Pushes the given chunk of data to the stream, or adds it to the buffer, - * depending on the state of the stream. - * - * @param {object} chunk - */ - pushOrBuffer (chunk) { - // Add the chunk to the buffer - this.buffer.push(chunk); +// '`foo/`' should not continue with the '`..`' +const DEFAULT_REPLACER_PREFIX = [ - // If we're still reading, then immediately emit the next chunk in the buffer - // (which may or may not be the chunk that we just added) - if (this.shouldRead) { - this.pushFromBuffer(); - } - } + // > Trailing spaces are ignored unless they are quoted with backslash ("\") + [ + // (a\ ) -> (a ) + // (a ) -> (a) + // (a \ ) -> (a ) + /\\?\s+$/, + match => match.indexOf('\\') === 0 + ? ' ' + : '' + ], - /** - * Immediately pushes the next chunk in the buffer to the reader's stream. - * The "data" event will always be fired (via {@link Readable#push}). - * In addition, the "file", "directory", and/or "symlink" events may be fired, - * depending on the type of properties of the chunk. - */ - pushFromBuffer () { - let stream = this.stream; - let chunk = this.buffer.shift(); + // replace (\ ) with ' ' + [ + /\\\s/g, + () => ' ' + ], - // Stream the data - try { - this.shouldRead = stream.push(chunk.data); - } - catch (err) { - this.emit('error', err); - } + // Escape metacharacters + // which is written down by users but means special for regular expressions. - // Also emit specific events, based on the type of chunk - chunk.file && this.emit('file', chunk.data); - chunk.symlink && this.emit('symlink', chunk.data); - chunk.directory && this.emit('directory', chunk.data); - } + // > There are 12 characters with special meanings: + // > - the backslash \, + // > - the caret ^, + // > - the dollar sign $, + // > - the period or dot ., + // > - the vertical bar or pipe symbol |, + // > - the question mark ?, + // > - the asterisk or star *, + // > - the plus sign +, + // > - the opening parenthesis (, + // > - the closing parenthesis ), + // > - and the opening square bracket [, + // > - the opening curly brace {, + // > These special characters are often called "metacharacters". + [ + /[\\^$.|*+(){]/g, + match => `\\${match}` + ], - /** - * Determines whether the given directory meets the user-specified recursion criteria. - * If the user didn't specify recursion criteria, then this function will default to true. - * - * @param {fs.Stats} stats - The directory's {@link fs.Stats} object - * @param {string} posixPath - The item's POSIX path (used for glob matching) - * @param {boolean} maxDepthReached - Whether we've already crawled the user-specified depth - * @returns {boolean} - */ - shouldRecurse (stats, posixPath, maxDepthReached) { - let options = this.options; + [ + // > [abc] matches any character inside the brackets + // > (in this case a, b, or c); + /\[([^\]/]*)($|\])/g, + (match, p1, p2) => p2 === ']' + ? `[${sanitizeRange(p1)}]` + : `\\${match}` + ], - if (maxDepthReached) { - // We've already crawled to the maximum depth. So no more recursion. - return false; - } - else if (!stats.isDirectory()) { - // It's not a directory. So don't try to crawl it. - return false; - } - else if (options.recurseGlob) { - // Glob patterns are always tested against the POSIX path, even on Windows - // https://github.com/isaacs/node-glob#windows - return options.recurseGlob.test(posixPath); - } - else if (options.recurseRegExp) { - // Regular expressions are tested against the normal path - // (based on the OS or options.sep) - return options.recurseRegExp.test(stats.path); - } - else if (options.recurseFn) { - try { - // Run the user-specified recursion criteria - return options.recurseFn.call(null, stats); - } - catch (err) { - // An error occurred in the user's code. - // In Sync and Async modes, this will return an error. - // In Streaming mode, we emit an "error" event, but continue processing - this.emit('error', err); - } - } - else { - // No recursion function was specified, and we're within the maximum depth. - // So crawl this directory. - return true; - } - } + [ + // > a question mark (?) matches a single character + /(?!\\)\?/g, + () => '[^/]' + ], - /** - * Determines whether the given item meets the user-specified filter criteria. - * If the user didn't specify a filter, then this function will always return true. - * - * @param {string|fs.Stats} value - Either the item's path, or the item's {@link fs.Stats} object - * @param {string} posixPath - The item's POSIX path (used for glob matching) - * @returns {boolean} - */ - filter (value, posixPath) { - let options = this.options; + // leading slash + [ - if (options.filterGlob) { - // Glob patterns are always tested against the POSIX path, even on Windows - // https://github.com/isaacs/node-glob#windows - return options.filterGlob.test(posixPath); - } - else if (options.filterRegExp) { - // Regular expressions are tested against the normal path - // (based on the OS or options.sep) - return options.filterRegExp.test(value.path || value); - } - else if (options.filterFn) { - try { - // Run the user-specified filter function - return options.filterFn.call(null, value); - } - catch (err) { - // An error occurred in the user's code. - // In Sync and Async modes, this will return an error. - // In Streaming mode, we emit an "error" event, but continue processing - this.emit('error', err); - } - } - else { - // No filter was specified, so match everything - return true; - } - } + // > A leading slash matches the beginning of the pathname. + // > For example, "/*.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c". + // A leading slash matches the beginning of the pathname + /^\//, + () => '^' + ], - /** - * Emits an event. If one of the event listeners throws an error, - * then an "error" event is emitted. - * - * @param {string} eventName - * @param {*} data - */ - emit (eventName, data) { - let stream = this.stream; + // replace special metacharacter slash after the leading slash + [ + /\//g, + () => '\\/' + ], - try { - stream.emit(eventName, data); - } - catch (err) { - if (eventName === 'error') { - // Don't recursively emit "error" events. - // If the first one fails, then just throw - throw err; - } - else { - stream.emit('error', err); - } - } - } -} + [ + // > A leading "**" followed by a slash means match in all directories. + // > For example, "**/foo" matches file or directory "foo" anywhere, + // > the same as pattern "foo". + // > "**/foo/bar" matches file or directory "bar" anywhere that is directly + // > under directory "foo". + // Notice that the '*'s have been replaced as '\\*' + /^\^*\\\*\\\*\\\//, -module.exports = DirectoryReader; + // '**/foo' <-> 'foo' + () => '^(?:.*\\/)?' + ] +] +const DEFAULT_REPLACER_SUFFIX = [ + // starting + [ + // there will be no leading '/' + // (which has been replaced by section "leading slash") + // If starts with '**', adding a '^' to the regular expression also works + /^(?=[^^])/, + function startingReplacer () { + return !/\/(?!$)/.test(this) + // > If the pattern does not contain a slash /, + // > Git treats it as a shell glob pattern + // Actually, if there is only a trailing slash, + // git also treats it as a shell glob pattern + ? '(?:^|\\/)' -/***/ }), -/* 721 */ -/***/ (function(module, exports, __webpack_require__) { + // > Otherwise, Git treats the pattern as a shell glob suitable for + // > consumption by fnmatch(3) + : '^' + } + ], -"use strict"; + // two globstars + [ + // Use lookahead assertions so that we could match more than one `'/**'` + /\\\/\\\*\\\*(?=\\\/|$)/g, + // Zero, one or several directories + // should not use '*', or it will be replaced by the next replacer -const path = __webpack_require__(4); -const globToRegExp = __webpack_require__(722); + // Check if it is not the last `'/**'` + (match, index, str) => index + 6 < str.length -module.exports = normalizeOptions; + // case: /**/ + // > A slash followed by two consecutive asterisks then a slash matches + // > zero or more directories. + // > For example, "a/**/b" matches "a/b", "a/x/b", "a/x/y/b" and so on. + // '/**/' + ? '(?:\\/[^\\/]+)*' -let isWindows = /^win/.test(process.platform); + // case: /** + // > A trailing `"/**"` matches everything inside. -/** - * @typedef {Object} FSFacade - * @property {fs.readdir} readdir - * @property {fs.stat} stat - * @property {fs.lstat} lstat - */ + // #21: everything inside but it should not include the current folder + : '\\/.+' + ], -/** - * Validates and normalizes the options argument - * - * @param {object} [options] - User-specified options, if any - * @param {object} internalOptions - Internal options that aren't part of the public API - * - * @param {number|boolean|function} [options.deep] - * The number of directories to recursively traverse. Any falsy value or negative number will - * default to zero, so only the top-level contents will be returned. Set to `true` or `Infinity` - * to traverse all subdirectories. Or provide a function that accepts a {@link fs.Stats} object - * and returns a truthy value if the directory's contents should be crawled. - * - * @param {function|string|RegExp} [options.filter] - * A function that accepts a {@link fs.Stats} object and returns a truthy value if the data should - * be returned. Or a RegExp or glob string pattern, to filter by file name. - * - * @param {string} [options.sep] - * The path separator to use. By default, the OS-specific separator will be used, but this can be - * set to a specific value to ensure consistency across platforms. - * - * @param {string} [options.basePath] - * The base path to prepend to each result. If empty, then all results will be relative to `dir`. - * - * @param {FSFacade} [options.fs] - * Synchronous or asynchronous facades for Node.js File System module - * - * @param {object} [internalOptions.facade] - * Synchronous or asynchronous facades for various methods, including for the Node.js File System module - * - * @param {boolean} [internalOptions.emit] - * Indicates whether the reader should emit "file", "directory", and "symlink" events - * - * @param {boolean} [internalOptions.stats] - * Indicates whether the reader should emit {@link fs.Stats} objects instead of path strings - * - * @returns {object} - */ -function normalizeOptions (options, internalOptions) { - if (options === null || options === undefined) { - options = {}; - } - else if (typeof options !== 'object') { - throw new TypeError('options must be an object'); - } + // intermediate wildcards + [ + // Never replace escaped '*' + // ignore rule '\*' will match the path '*' - let recurseDepth, recurseFn, recurseRegExp, recurseGlob, deep = options.deep; - if (deep === null || deep === undefined) { - recurseDepth = 0; - } - else if (typeof deep === 'boolean') { - recurseDepth = deep ? Infinity : 0; - } - else if (typeof deep === 'number') { - if (deep < 0 || isNaN(deep)) { - throw new Error('options.deep must be a positive number'); - } - else if (Math.floor(deep) !== deep) { - throw new Error('options.deep must be an integer'); - } - else { - recurseDepth = deep; - } - } - else if (typeof deep === 'function') { - recurseDepth = Infinity; - recurseFn = deep; - } - else if (deep instanceof RegExp) { - recurseDepth = Infinity; - recurseRegExp = deep; - } - else if (typeof deep === 'string' && deep.length > 0) { - recurseDepth = Infinity; - recurseGlob = globToRegExp(deep, { extended: true, globstar: true }); - } - else { - throw new TypeError('options.deep must be a boolean, number, function, regular expression, or glob pattern'); - } + // 'abc.*/' -> go + // 'abc.*' -> skip this rule + /(^|[^\\]+)\\\*(?=.+)/g, - let filterFn, filterRegExp, filterGlob, filter = options.filter; - if (filter !== null && filter !== undefined) { - if (typeof filter === 'function') { - filterFn = filter; - } - else if (filter instanceof RegExp) { - filterRegExp = filter; - } - else if (typeof filter === 'string' && filter.length > 0) { - filterGlob = globToRegExp(filter, { extended: true, globstar: true }); - } - else { - throw new TypeError('options.filter must be a function, regular expression, or glob pattern'); - } - } + // '*.js' matches '.js' + // '*.js' doesn't match 'abc' + (match, p1) => `${p1}[^\\/]*` + ], - let sep = options.sep; - if (sep === null || sep === undefined) { - sep = path.sep; - } - else if (typeof sep !== 'string') { - throw new TypeError('options.sep must be a string'); - } + // trailing wildcard + [ + /(\^|\\\/)?\\\*$/, + (match, p1) => { + const prefix = p1 + // '\^': + // '/*' does not match '' + // '/*' does not match everything - let basePath = options.basePath; - if (basePath === null || basePath === undefined) { - basePath = ''; - } - else if (typeof basePath === 'string') { - // Append a path separator to the basePath, if necessary - if (basePath && basePath.substr(-1) !== sep) { - basePath += sep; - } - } - else { - throw new TypeError('options.basePath must be a string'); - } + // '\\\/': + // 'abc/*' does not match 'abc/' + ? `${p1}[^/]+` - // Convert the basePath to POSIX (forward slashes) - // so that glob pattern matching works consistently, even on Windows - let posixBasePath = basePath; - if (posixBasePath && sep !== '/') { - posixBasePath = posixBasePath.replace(new RegExp('\\' + sep, 'g'), '/'); + // 'a*' matches 'a' + // 'a*' matches 'aa' + : '[^/]*' - /* istanbul ignore if */ - if (isWindows) { - // Convert Windows root paths (C:\) and UNCs (\\) to POSIX root paths - posixBasePath = posixBasePath.replace(/^([a-zA-Z]\:\/|\/\/)/, '/'); + return `${prefix}(?=$|\\/$)` } - } + ], - // Determine which facade methods to use - let facade; - if (options.fs === null || options.fs === undefined) { - // The user didn't provide their own facades, so use our internal ones - facade = internalOptions.facade; - } - else if (typeof options.fs === 'object') { - // Merge the internal facade methods with the user-provided `fs` facades - facade = Object.assign({}, internalOptions.facade); - facade.fs = Object.assign({}, internalOptions.facade.fs, options.fs); - } - else { - throw new TypeError('options.fs must be an object'); - } + [ + // unescape + /\\\\\\/g, + () => '\\' + ] +] - return { - recurseDepth, - recurseFn, - recurseRegExp, - recurseGlob, - filterFn, - filterRegExp, - filterGlob, - sep, - basePath, - posixBasePath, - facade, - emit: !!internalOptions.emit, - stats: !!internalOptions.stats, - }; -} +const POSITIVE_REPLACERS = [ + ...DEFAULT_REPLACER_PREFIX, + // 'f' + // matches + // - /f(end) + // - /f/ + // - (start)f(end) + // - (start)f/ + // doesn't match + // - oof + // - foo + // pseudo: + // -> (^|/)f(/|$) -/***/ }), -/* 722 */ -/***/ (function(module, exports) { + // ending + [ + // 'js' will not match 'js.' + // 'ab' will not match 'abc' + /(?:[^*/])$/, -module.exports = function (glob, opts) { - if (typeof glob !== 'string') { - throw new TypeError('Expected a string'); - } + // 'js*' will not match 'a.js' + // 'js/' will not match 'a.js' + // 'js' will match 'a.js' and 'a.js/' + match => `${match}(?=$|\\/)` + ], - var str = String(glob); + ...DEFAULT_REPLACER_SUFFIX +] - // The regexp we are building, as a string. - var reStr = ""; +const NEGATIVE_REPLACERS = [ + ...DEFAULT_REPLACER_PREFIX, - // Whether we are matching so called "extended" globs (like bash) and should - // support single character matching, matching ranges of characters, group - // matching, etc. - var extended = opts ? !!opts.extended : false; + // #24, #38 + // The MISSING rule of [gitignore docs](https://git-scm.com/docs/gitignore) + // A negative pattern without a trailing wildcard should not + // re-include the things inside that directory. - // When globstar is _false_ (default), '/foo/*' is translated a regexp like - // '^\/foo\/.*$' which will match any string beginning with '/foo/' - // When globstar is _true_, '/foo/*' is translated to regexp like - // '^\/foo\/[^/]*$' which will match any string beginning with '/foo/' BUT - // which does not have a '/' to the right of it. - // E.g. with '/foo/*' these will match: '/foo/bar', '/foo/bar.txt' but - // these will not '/foo/bar/baz', '/foo/bar/baz.txt' - // Lastely, when globstar is _true_, '/foo/**' is equivelant to '/foo/*' when - // globstar is _false_ - var globstar = opts ? !!opts.globstar : false; + // eg: + // ['node_modules/*', '!node_modules'] + // should ignore `node_modules/a.js` + [ + /(?:[^*])$/, + match => `${match}(?=$|\\/$)` + ], - // If we are doing extended matching, this boolean is true when we are inside - // a group (eg {*.html,*.js}), and false otherwise. - var inGroup = false; + ...DEFAULT_REPLACER_SUFFIX +] - // RegExp flags (eg "i" ) to pass in to RegExp constructor. - var flags = opts && typeof( opts.flags ) === "string" ? opts.flags : ""; +// A simple cache, because an ignore rule only has only one certain meaning +const cache = Object.create(null) - var c; - for (var i = 0, len = str.length; i < len; i++) { - c = str[i]; +// @param {pattern} +const make_regex = (pattern, negative, ignorecase) => { + const r = cache[pattern] + if (r) { + return r + } - switch (c) { - case "\\": - case "/": - case "$": - case "^": - case "+": - case ".": - case "(": - case ")": - case "=": - case "!": - case "|": - reStr += "\\" + c; - break; + const replacers = negative + ? NEGATIVE_REPLACERS + : POSITIVE_REPLACERS - case "?": - if (extended) { - reStr += "."; - break; - } + const source = replacers.reduce( + (prev, current) => prev.replace(current[0], current[1].bind(pattern)), + pattern + ) - case "[": - case "]": - if (extended) { - reStr += c; - break; - } + return cache[pattern] = ignorecase + ? new RegExp(source, 'i') + : new RegExp(source) +} - case "{": - if (extended) { - inGroup = true; - reStr += "("; - break; - } +// > A blank line matches no files, so it can serve as a separator for readability. +const checkPattern = pattern => pattern + && typeof pattern === 'string' + && !REGEX_BLANK_LINE.test(pattern) - case "}": - if (extended) { - inGroup = false; - reStr += ")"; - break; - } + // > A line starting with # serves as a comment. + && pattern.indexOf('#') !== 0 - case ",": - if (inGroup) { - reStr += "|"; - break; - } - reStr += "\\" + c; - break; +const createRule = (pattern, ignorecase) => { + const origin = pattern + let negative = false - case "*": - // Move over all consecutive "*"'s. - // Also store the previous and next characters - var prevChar = str[i - 1]; - var starCount = 1; - while(str[i + 1] === "*") { - starCount++; - i++; - } - var nextChar = str[i + 1]; + // > An optional prefix "!" which negates the pattern; + if (pattern.indexOf('!') === 0) { + negative = true + pattern = pattern.substr(1) + } - if (!globstar) { - // globstar is disabled, so treat any number of "*" as one - reStr += ".*"; - } else { - // globstar is enabled, so determine if this is a globstar segment - var isGlobstar = starCount > 1 // multiple "*"'s - && (prevChar === "/" || prevChar === undefined) // from the start of the segment - && (nextChar === "/" || nextChar === undefined) // to the end of the segment + pattern = pattern + // > Put a backslash ("\") in front of the first "!" for patterns that + // > begin with a literal "!", for example, `"\!important!.txt"`. + .replace(REGEX_LEADING_EXCAPED_EXCLAMATION, '!') + // > Put a backslash ("\") in front of the first hash for patterns that + // > begin with a hash. + .replace(REGEX_LEADING_EXCAPED_HASH, '#') - if (isGlobstar) { - // it's a globstar, so match zero or more path segments - reStr += "(?:[^/]*(?:\/|$))*"; - i++; // move over the "/" - } else { - // it's not a globstar, so only match one path segment - reStr += "[^/]*"; - } - } - break; + const regex = make_regex(pattern, negative, ignorecase) - default: - reStr += c; - } + return { + origin, + pattern, + negative, + regex } +} - // When regexp 'g' flag is specified don't - // constrain the regular expression with ^ & $ - if (!flags || !~flags.indexOf('g')) { - reStr = "^" + reStr + "$"; +class IgnoreBase { + constructor ({ + ignorecase = true + } = {}) { + this._rules = [] + this._ignorecase = ignorecase + define(this, KEY_IGNORE, true) + this._initCache() } - return new RegExp(reStr, flags); -}; - + _initCache () { + this._cache = Object.create(null) + } -/***/ }), -/* 723 */ -/***/ (function(module, exports, __webpack_require__) { + // @param {Array.|string|Ignore} pattern + add (pattern) { + this._added = false -"use strict"; + if (typeof pattern === 'string') { + pattern = pattern.split(/\r?\n/g) + } + make_array(pattern).forEach(this._addPattern, this) -const call = __webpack_require__(724); + // Some rules have just added to the ignore, + // making the behavior changed. + if (this._added) { + this._initCache() + } -module.exports = stat; + return this + } -/** - * Retrieves the {@link fs.Stats} for the given path. If the path is a symbolic link, - * then the Stats of the symlink's target are returned instead. If the symlink is broken, - * then the Stats of the symlink itself are returned. - * - * @param {object} fs - Synchronous or Asynchronouse facade for the "fs" module - * @param {string} path - The path to return stats for - * @param {function} callback - */ -function stat (fs, path, callback) { - let isSymLink = false; + // legacy + addPattern (pattern) { + return this.add(pattern) + } - call.safe(fs.lstat, path, (err, lstats) => { - if (err) { - // fs.lstat threw an eror - return callback(err); + _addPattern (pattern) { + // #32 + if (pattern && pattern[KEY_IGNORE]) { + this._rules = this._rules.concat(pattern._rules) + this._added = true + return } - try { - isSymLink = lstats.isSymbolicLink(); - } - catch (err2) { - // lstats.isSymbolicLink() threw an error - // (probably because fs.lstat returned an invalid result) - return callback(err2); + if (checkPattern(pattern)) { + const rule = createRule(pattern, this._ignorecase) + this._added = true + this._rules.push(rule) } + } - if (isSymLink) { - // Try to resolve the symlink - symlinkStat(fs, path, lstats, callback); - } - else { - // It's not a symlink, so return the stats as-is - callback(null, lstats); - } - }); -} + filter (paths) { + return make_array(paths).filter(path => this._filter(path)) + } -/** - * Retrieves the {@link fs.Stats} for the target of the given symlink. - * If the symlink is broken, then the Stats of the symlink itself are returned. - * - * @param {object} fs - Synchronous or Asynchronouse facade for the "fs" module - * @param {string} path - The path of the symlink to return stats for - * @param {object} lstats - The stats of the symlink - * @param {function} callback - */ -function symlinkStat (fs, path, lstats, callback) { - call.safe(fs.stat, path, (err, stats) => { - if (err) { - // The symlink is broken, so return the stats for the link itself - return callback(null, lstats); + createFilter () { + return path => this._filter(path) + } + + ignores (path) { + return !this._filter(path) + } + + // @returns `Boolean` true if the `path` is NOT ignored + _filter (path, slices) { + if (!path) { + return false } - try { - // Return the stats for the resolved symlink target, - // and override the `isSymbolicLink` method to indicate that it's a symlink - stats.isSymbolicLink = () => true; + if (path in this._cache) { + return this._cache[path] } - catch (err2) { - // Setting stats.isSymbolicLink threw an error - // (probably because fs.stat returned an invalid result) - return callback(err2); + + if (!slices) { + // path/to/a.js + // ['path', 'to', 'a.js'] + slices = path.split(SLASH) } - callback(null, stats); - }); -} + slices.pop() + return this._cache[path] = slices.length + // > It is not possible to re-include a file if a parent directory of + // > that file is excluded. + // If the path contains a parent directory, check the parent first + ? this._filter(slices.join(SLASH) + SLASH, slices) + && this._test(path) -/***/ }), -/* 724 */ -/***/ (function(module, exports, __webpack_require__) { + // Or only test the path + : this._test(path) + } -"use strict"; + // @returns {Boolean} true if a file is NOT ignored + _test (path) { + // Explicitly define variable type by setting matched to `0` + let matched = 0 + this._rules.forEach(rule => { + // if matched = true, then we only test negative rules + // if matched = false, then we test non-negative rules + if (!(matched ^ rule.negative)) { + matched = rule.negative ^ rule.regex.test(path) + } + }) -let call = module.exports = { - safe: safeCall, - once: callOnce, -}; + return !matched + } +} -/** - * Calls a function with the given arguments, and ensures that the error-first callback is _always_ - * invoked exactly once, even if the function throws an error. - * - * @param {function} fn - The function to invoke - * @param {...*} args - The arguments to pass to the function. The final argument must be a callback function. - */ -function safeCall (fn, args) { - // Get the function arguments as an array - args = Array.prototype.slice.call(arguments, 1); +// Windows +// -------------------------------------------------------------- +/* istanbul ignore if */ +if ( + // Detect `process` so that it can run in browsers. + typeof process !== 'undefined' + && ( + process.env && process.env.IGNORE_TEST_WIN32 + || process.platform === 'win32' + ) +) { + const filter = IgnoreBase.prototype._filter - // Replace the callback function with a wrapper that ensures it will only be called once - let callback = call.once(args.pop()); - args.push(callback); + /* eslint no-control-regex: "off" */ + const make_posix = str => /^\\\\\?\\/.test(str) + || /[^\x00-\x80]+/.test(str) + ? str + : str.replace(/\\/g, '/') - try { - fn.apply(null, args); - } - catch (err) { - callback(err); + IgnoreBase.prototype._filter = function filterWin32 (path, slices) { + path = make_posix(path) + return filter.call(this, path, slices) } } -/** - * Returns a wrapper function that ensures the given callback function is only called once. - * Subsequent calls are ignored, unless the first argument is an Error, in which case the - * error is thrown. - * - * @param {function} fn - The function that should only be called once - * @returns {function} - */ -function callOnce (fn) { - let fulfilled = false; - - return function onceWrapper (err) { - if (!fulfilled) { - fulfilled = true; - return fn.apply(this, arguments); - } - else if (err) { - // The callback has already been called, but now an error has occurred - // (most likely inside the callback function). So re-throw the error, - // so it gets handled further up the call stack - throw err; - } - }; -} +module.exports = options => new IgnoreBase(options) /***/ }), -/* 725 */ +/* 757 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; +module.exports = input => { + const isExtendedLengthPath = /^\\\\\?\\/.test(input); + const hasNonAscii = /[^\u0000-\u0080]+/.test(input); // eslint-disable-line no-control-regex -const fs = __webpack_require__(132); -const call = __webpack_require__(724); - -/** - * A facade around {@link fs.readdirSync} that allows it to be called - * the same way as {@link fs.readdir}. - * - * @param {string} dir - * @param {function} callback - */ -exports.readdir = function (dir, callback) { - // Make sure the callback is only called once - callback = call.once(callback); + if (isExtendedLengthPath || hasNonAscii) { + return input; + } - try { - let items = fs.readdirSync(dir); - callback(null, items); - } - catch (err) { - callback(err); - } + return input.replace(/\\/g, '/'); }; -/** - * A facade around {@link fs.statSync} that allows it to be called - * the same way as {@link fs.stat}. + +/***/ }), +/* 758 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +/*! + * has-glob * - * @param {string} path - * @param {function} callback + * Copyright (c) 2015, Jon Schlinkert. + * Licensed under the MIT License. */ -exports.stat = function (path, callback) { - // Make sure the callback is only called once - callback = call.once(callback); - try { - let stats = fs.statSync(path); - callback(null, stats); + + +var isGlob = __webpack_require__(759); + +module.exports = function hasGlob(val) { + if (val == null) return false; + if (typeof val === 'string') { + return isGlob(val); } - catch (err) { - callback(err); + if (Array.isArray(val)) { + var len = val.length; + while (len--) { + if (isGlob(val[len])) { + return true; + } + } } + return false; }; -/** - * A facade around {@link fs.lstatSync} that allows it to be called - * the same way as {@link fs.lstat}. + +/***/ }), +/* 759 */ +/***/ (function(module, exports, __webpack_require__) { + +/*! + * is-glob * - * @param {string} path - * @param {function} callback + * Copyright (c) 2014-2016, Jon Schlinkert. + * Licensed under the MIT License. */ -exports.lstat = function (path, callback) { - // Make sure the callback is only called once - callback = call.once(callback); - try { - let stats = fs.lstatSync(path); - callback(null, stats); +var isExtglob = __webpack_require__(267); + +module.exports = function isGlob(str) { + if (typeof str !== 'string' || str === '') { + return false; } - catch (err) { - callback(err); + + if (isExtglob(str)) return true; + + var regex = /(\\).|([*?]|\[.*\]|\{.*\}|\(.*\|.*\)|^!)/; + var match; + + while ((match = regex.exec(str))) { + if (match[2]) return true; + str = str.slice(match.index + match[0].length); } + return false; }; /***/ }), -/* 726 */ +/* 760 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; +const path = __webpack_require__(4); +const {constants: fsConstants} = __webpack_require__(132); +const pEvent = __webpack_require__(761); +const CpFileError = __webpack_require__(764); +const fs = __webpack_require__(766); +const ProgressEmitter = __webpack_require__(769); -module.exports = syncForEach; +const cpFileAsync = async (source, destination, options, progressEmitter) => { + let readError; + const stat = await fs.stat(source); + progressEmitter.size = stat.size; -/** - * A facade that allows {@link Array.forEach} to be called as though it were asynchronous. - * - * @param {array} array - The array to iterate over - * @param {function} iterator - The function to call for each item in the array - * @param {function} done - The function to call when all iterators have completed - */ -function syncForEach (array, iterator, done) { - array.forEach(item => { - iterator(item, () => { - // Note: No error-handling here because this is currently only ever called - // by DirectoryReader, which never passes an `error` parameter to the callback. - // Instead, DirectoryReader emits an "error" event if an error occurs. - }); - }); + const read = await fs.createReadStream(source); + await fs.makeDir(path.dirname(destination)); + const write = fs.createWriteStream(destination, {flags: options.overwrite ? 'w' : 'wx'}); + read.on('data', () => { + progressEmitter.written = write.bytesWritten; + }); + read.once('error', error => { + readError = new CpFileError(`Cannot read from \`${source}\`: ${error.message}`, error); + write.end(); + }); - done(); -} + let updateStats = false; + try { + const writePromise = pEvent(write, 'close'); + read.pipe(write); + await writePromise; + progressEmitter.written = progressEmitter.size; + updateStats = true; + } catch (error) { + if (options.overwrite || error.code !== 'EEXIST') { + throw new CpFileError(`Cannot write to \`${destination}\`: ${error.message}`, error); + } + } + if (readError) { + throw readError; + } -/***/ }), -/* 727 */ -/***/ (function(module, exports, __webpack_require__) { + if (updateStats) { + const stats = await fs.lstat(source); -"use strict"; + return Promise.all([ + fs.utimes(destination, stats.atime, stats.mtime), + fs.chmod(destination, stats.mode), + fs.chown(destination, stats.uid, stats.gid) + ]); + } +}; +const cpFile = (source, destination, options) => { + if (!source || !destination) { + return Promise.reject(new CpFileError('`source` and `destination` required')); + } -module.exports = readdirAsync; + options = { + overwrite: true, + ...options + }; + + const progressEmitter = new ProgressEmitter(path.resolve(source), path.resolve(destination)); + const promise = cpFileAsync(source, destination, options, progressEmitter); + promise.on = (...args) => { + progressEmitter.on(...args); + return promise; + }; -const maybe = __webpack_require__(728); -const DirectoryReader = __webpack_require__(720); + return promise; +}; -let asyncFacade = { - fs: __webpack_require__(132), - forEach: __webpack_require__(729), - async: true +module.exports = cpFile; + +const checkSourceIsFile = (stat, source) => { + if (stat.isDirectory()) { + throw Object.assign(new CpFileError(`EISDIR: illegal operation on a directory '${source}'`), { + errno: -21, + code: 'EISDIR', + source + }); + } }; -/** - * Returns the buffered output from an asynchronous {@link DirectoryReader}, - * via an error-first callback or a {@link Promise}. - * - * @param {string} dir - * @param {object} [options] - * @param {function} [callback] - * @param {object} internalOptions - */ -function readdirAsync (dir, options, callback, internalOptions) { - if (typeof options === 'function') { - callback = options; - options = undefined; - } +const fixupAttributes = (destination, stat) => { + fs.chmodSync(destination, stat.mode); + fs.chownSync(destination, stat.uid, stat.gid); +}; + +module.exports.sync = (source, destination, options) => { + if (!source || !destination) { + throw new CpFileError('`source` and `destination` required'); + } + + options = { + overwrite: true, + ...options + }; - return maybe(callback, new Promise(((resolve, reject) => { - let results = []; + const stat = fs.statSync(source); + checkSourceIsFile(stat, source); + fs.makeDirSync(path.dirname(destination)); - internalOptions.facade = asyncFacade; + const flags = options.overwrite ? null : fsConstants.COPYFILE_EXCL; + try { + fs.copyFileSync(source, destination, flags); + } catch (error) { + if (!options.overwrite && error.code === 'EEXIST') { + return; + } - let reader = new DirectoryReader(dir, options, internalOptions); - let stream = reader.stream; + throw error; + } - stream.on('error', err => { - reject(err); - stream.pause(); - }); - stream.on('data', result => { - results.push(result); - }); - stream.on('end', () => { - resolve(results); - }); - }))); -} + fs.utimesSync(destination, stat.atime, stat.mtime); + fixupAttributes(destination, stat); +}; /***/ }), -/* 728 */ +/* 761 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; +const pTimeout = __webpack_require__(762); -var next = (global.process && process.nextTick) || global.setImmediate || function (f) { - setTimeout(f, 0) -} +const symbolAsyncIterator = Symbol.asyncIterator || '@@asyncIterator'; -module.exports = function maybe (cb, promise) { - if (cb) { - promise - .then(function (result) { - next(function () { cb(null, result) }) - }, function (err) { - next(function () { cb(err) }) - }) - return undefined - } - else { - return promise - } -} +const normalizeEmitter = emitter => { + const addListener = emitter.on || emitter.addListener || emitter.addEventListener; + const removeListener = emitter.off || emitter.removeListener || emitter.removeEventListener; + if (!addListener || !removeListener) { + throw new TypeError('Emitter is not compatible'); + } -/***/ }), -/* 729 */ -/***/ (function(module, exports, __webpack_require__) { + return { + addListener: addListener.bind(emitter), + removeListener: removeListener.bind(emitter) + }; +}; -"use strict"; +const normalizeEvents = event => Array.isArray(event) ? event : [event]; +const multiple = (emitter, event, options) => { + let cancel; + const ret = new Promise((resolve, reject) => { + options = { + rejectionEvents: ['error'], + multiArgs: false, + resolveImmediately: false, + ...options + }; -module.exports = asyncForEach; + if (!(options.count >= 0 && (options.count === Infinity || Number.isInteger(options.count)))) { + throw new TypeError('The `count` option should be at least 0 or more'); + } -/** - * Simultaneously processes all items in the given array. - * - * @param {array} array - The array to iterate over - * @param {function} iterator - The function to call for each item in the array - * @param {function} done - The function to call when all iterators have completed - */ -function asyncForEach (array, iterator, done) { - if (array.length === 0) { - // NOTE: Normally a bad idea to mix sync and async, but it's safe here because - // of the way that this method is currently used by DirectoryReader. - done(); - return; - } + // Allow multiple events + const events = normalizeEvents(event); - // Simultaneously process all items in the array. - let pending = array.length; - array.forEach(item => { - iterator(item, () => { - if (--pending === 0) { - done(); - } - }); - }); -} + const items = []; + const {addListener, removeListener} = normalizeEmitter(emitter); + const onItem = (...args) => { + const value = options.multiArgs ? args : args[0]; -/***/ }), -/* 730 */ -/***/ (function(module, exports, __webpack_require__) { + if (options.filter && !options.filter(value)) { + return; + } -"use strict"; + items.push(value); + if (options.count === items.length) { + cancel(); + resolve(items); + } + }; -module.exports = readdirStream; + const rejectHandler = error => { + cancel(); + reject(error); + }; -const DirectoryReader = __webpack_require__(720); + cancel = () => { + for (const event of events) { + removeListener(event, onItem); + } -let streamFacade = { - fs: __webpack_require__(132), - forEach: __webpack_require__(729), - async: true -}; + for (const rejectionEvent of options.rejectionEvents) { + removeListener(rejectionEvent, rejectHandler); + } + }; -/** - * Returns the {@link stream.Readable} of an asynchronous {@link DirectoryReader}. - * - * @param {string} dir - * @param {object} [options] - * @param {object} internalOptions - */ -function readdirStream (dir, options, internalOptions) { - internalOptions.facade = streamFacade; + for (const event of events) { + addListener(event, onItem); + } - let reader = new DirectoryReader(dir, options, internalOptions); - return reader.stream; -} + for (const rejectionEvent of options.rejectionEvents) { + addListener(rejectionEvent, rejectHandler); + } + if (options.resolveImmediately) { + resolve(items); + } + }); -/***/ }), -/* 731 */ -/***/ (function(module, exports, __webpack_require__) { + ret.cancel = cancel; -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -var path = __webpack_require__(4); -var deep_1 = __webpack_require__(732); -var entry_1 = __webpack_require__(734); -var pathUtil = __webpack_require__(733); -var Reader = /** @class */ (function () { - function Reader(options) { - this.options = options; - this.micromatchOptions = this.getMicromatchOptions(); - this.entryFilter = new entry_1.default(options, this.micromatchOptions); - this.deepFilter = new deep_1.default(options, this.micromatchOptions); - } - /** - * Returns root path to scanner. - */ - Reader.prototype.getRootDirectory = function (task) { - return path.resolve(this.options.cwd, task.base); - }; - /** - * Returns options for reader. - */ - Reader.prototype.getReaderOptions = function (task) { - return { - basePath: task.base === '.' ? '' : task.base, - filter: this.entryFilter.getFilter(task.positive, task.negative), - deep: this.deepFilter.getFilter(task.positive, task.negative), - sep: '/' - }; - }; - /** - * Returns options for micromatch. - */ - Reader.prototype.getMicromatchOptions = function () { - return { - dot: this.options.dot, - nobrace: !this.options.brace, - noglobstar: !this.options.globstar, - noext: !this.options.extension, - nocase: !this.options.case, - matchBase: this.options.matchBase - }; - }; - /** - * Returns transformed entry. - */ - Reader.prototype.transform = function (entry) { - if (this.options.absolute) { - entry.path = pathUtil.makeAbsolute(this.options.cwd, entry.path); - } - if (this.options.markDirectories && entry.isDirectory()) { - entry.path += '/'; - } - var item = this.options.stats ? entry : entry.path; - if (this.options.transform === null) { - return item; - } - return this.options.transform(item); - }; - /** - * Returns true if error has ENOENT code. - */ - Reader.prototype.isEnoentCodeError = function (err) { - return err.code === 'ENOENT'; - }; - return Reader; -}()); -exports.default = Reader; + if (typeof options.timeout === 'number') { + const timeout = pTimeout(ret, options.timeout); + timeout.cancel = cancel; + return timeout; + } + return ret; +}; -/***/ }), -/* 732 */ -/***/ (function(module, exports, __webpack_require__) { +const pEvent = (emitter, event, options) => { + if (typeof options === 'function') { + options = {filter: options}; + } -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -var pathUtils = __webpack_require__(733); -var patternUtils = __webpack_require__(567); -var DeepFilter = /** @class */ (function () { - function DeepFilter(options, micromatchOptions) { - this.options = options; - this.micromatchOptions = micromatchOptions; - } - /** - * Returns filter for directories. - */ - DeepFilter.prototype.getFilter = function (positive, negative) { - var _this = this; - var maxPatternDepth = this.getMaxPatternDepth(positive); - var negativeRe = this.getNegativePatternsRe(negative); - return function (entry) { return _this.filter(entry, negativeRe, maxPatternDepth); }; - }; - /** - * Returns max depth of the provided patterns. - */ - DeepFilter.prototype.getMaxPatternDepth = function (patterns) { - var globstar = patterns.some(patternUtils.hasGlobStar); - return globstar ? Infinity : patternUtils.getMaxNaivePatternsDepth(patterns); - }; - /** - * Returns RegExp's for patterns that can affect the depth of reading. - */ - DeepFilter.prototype.getNegativePatternsRe = function (patterns) { - var affectDepthOfReadingPatterns = patterns.filter(patternUtils.isAffectDepthOfReadingPattern); - return patternUtils.convertPatternsToRe(affectDepthOfReadingPatterns, this.micromatchOptions); - }; - /** - * Returns «true» for directory that should be read. - */ - DeepFilter.prototype.filter = function (entry, negativeRe, maxPatternDepth) { - if (this.isSkippedByDeepOption(entry.depth)) { - return false; - } - if (this.isSkippedByMaxPatternDepth(entry.depth, maxPatternDepth)) { - return false; - } - if (this.isSkippedSymlinkedDirectory(entry)) { - return false; - } - if (this.isSkippedDotDirectory(entry)) { - return false; - } - return this.isSkippedByNegativePatterns(entry, negativeRe); - }; - /** - * Returns «true» when the «deep» option is disabled or number and depth of the entry is greater that the option value. - */ - DeepFilter.prototype.isSkippedByDeepOption = function (entryDepth) { - return !this.options.deep || (typeof this.options.deep === 'number' && entryDepth >= this.options.deep); - }; - /** - * Returns «true» when depth parameter is not an Infinity and entry depth greater that the parameter value. - */ - DeepFilter.prototype.isSkippedByMaxPatternDepth = function (entryDepth, maxPatternDepth) { - return maxPatternDepth !== Infinity && entryDepth >= maxPatternDepth; - }; - /** - * Returns «true» for symlinked directory if the «followSymlinkedDirectories» option is disabled. - */ - DeepFilter.prototype.isSkippedSymlinkedDirectory = function (entry) { - return !this.options.followSymlinkedDirectories && entry.isSymbolicLink(); - }; - /** - * Returns «true» for a directory whose name starts with a period if «dot» option is disabled. - */ - DeepFilter.prototype.isSkippedDotDirectory = function (entry) { - return !this.options.dot && pathUtils.isDotDirectory(entry.path); - }; - /** - * Returns «true» for a directory whose path math to any negative pattern. - */ - DeepFilter.prototype.isSkippedByNegativePatterns = function (entry, negativeRe) { - return !patternUtils.matchAny(entry.path, negativeRe); - }; - return DeepFilter; -}()); -exports.default = DeepFilter; + options = { + ...options, + count: 1, + resolveImmediately: false + }; + const arrayPromise = multiple(emitter, event, options); + const promise = arrayPromise.then(array => array[0]); // eslint-disable-line promise/prefer-await-to-then + promise.cancel = arrayPromise.cancel; -/***/ }), -/* 733 */ -/***/ (function(module, exports, __webpack_require__) { + return promise; +}; -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -var path = __webpack_require__(4); -/** - * Returns «true» if the last partial of the path starting with a period. - */ -function isDotDirectory(filepath) { - return path.basename(filepath).startsWith('.'); -} -exports.isDotDirectory = isDotDirectory; -/** - * Convert a windows-like path to a unix-style path. - */ -function normalize(filepath) { - return filepath.replace(/\\/g, '/'); -} -exports.normalize = normalize; -/** - * Returns normalized absolute path of provided filepath. - */ -function makeAbsolute(cwd, filepath) { - return normalize(path.resolve(cwd, filepath)); -} -exports.makeAbsolute = makeAbsolute; +module.exports = pEvent; +// TODO: Remove this for the next major release +module.exports.default = pEvent; +module.exports.multiple = multiple; -/***/ }), -/* 734 */ -/***/ (function(module, exports, __webpack_require__) { +module.exports.iterator = (emitter, event, options) => { + if (typeof options === 'function') { + options = {filter: options}; + } -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -var pathUtils = __webpack_require__(733); -var patternUtils = __webpack_require__(567); -var EntryFilter = /** @class */ (function () { - function EntryFilter(options, micromatchOptions) { - this.options = options; - this.micromatchOptions = micromatchOptions; - this.index = new Map(); - } - /** - * Returns filter for directories. - */ - EntryFilter.prototype.getFilter = function (positive, negative) { - var _this = this; - var positiveRe = patternUtils.convertPatternsToRe(positive, this.micromatchOptions); - var negativeRe = patternUtils.convertPatternsToRe(negative, this.micromatchOptions); - return function (entry) { return _this.filter(entry, positiveRe, negativeRe); }; - }; - /** - * Returns true if entry must be added to result. - */ - EntryFilter.prototype.filter = function (entry, positiveRe, negativeRe) { - // Exclude duplicate results - if (this.options.unique) { - if (this.isDuplicateEntry(entry)) { - return false; - } - this.createIndexRecord(entry); - } - // Filter files and directories by options - if (this.onlyFileFilter(entry) || this.onlyDirectoryFilter(entry)) { - return false; - } - if (this.isSkippedByAbsoluteNegativePatterns(entry, negativeRe)) { - return false; - } - return this.isMatchToPatterns(entry.path, positiveRe) && !this.isMatchToPatterns(entry.path, negativeRe); - }; - /** - * Return true if the entry already has in the cross reader index. - */ - EntryFilter.prototype.isDuplicateEntry = function (entry) { - return this.index.has(entry.path); - }; - /** - * Create record in the cross reader index. - */ - EntryFilter.prototype.createIndexRecord = function (entry) { - this.index.set(entry.path, undefined); - }; - /** - * Returns true for non-files if the «onlyFiles» option is enabled. - */ - EntryFilter.prototype.onlyFileFilter = function (entry) { - return this.options.onlyFiles && !entry.isFile(); - }; - /** - * Returns true for non-directories if the «onlyDirectories» option is enabled. - */ - EntryFilter.prototype.onlyDirectoryFilter = function (entry) { - return this.options.onlyDirectories && !entry.isDirectory(); - }; - /** - * Return true when `absolute` option is enabled and matched to the negative patterns. - */ - EntryFilter.prototype.isSkippedByAbsoluteNegativePatterns = function (entry, negativeRe) { - if (!this.options.absolute) { - return false; - } - var fullpath = pathUtils.makeAbsolute(this.options.cwd, entry.path); - return this.isMatchToPatterns(fullpath, negativeRe); - }; - /** - * Return true when entry match to provided patterns. - * - * First, just trying to apply patterns to the path. - * Second, trying to apply patterns to the path with final slash (need to micromatch to support «directory/**» patterns). - */ - EntryFilter.prototype.isMatchToPatterns = function (filepath, patternsRe) { - return patternUtils.matchAny(filepath, patternsRe) || patternUtils.matchAny(filepath + '/', patternsRe); - }; - return EntryFilter; -}()); -exports.default = EntryFilter; + // Allow multiple events + const events = normalizeEvents(event); + + options = { + rejectionEvents: ['error'], + resolutionEvents: [], + limit: Infinity, + multiArgs: false, + ...options + }; + + const {limit} = options; + const isValidLimit = limit >= 0 && (limit === Infinity || Number.isInteger(limit)); + if (!isValidLimit) { + throw new TypeError('The `limit` option should be a non-negative integer or Infinity'); + } + + if (limit === 0) { + // Return an empty async iterator to avoid any further cost + return { + [Symbol.asyncIterator]() { + return this; + }, + async next() { + return { + done: true, + value: undefined + }; + } + }; + } + const {addListener, removeListener} = normalizeEmitter(emitter); -/***/ }), -/* 735 */ -/***/ (function(module, exports, __webpack_require__) { + let isDone = false; + let error; + let hasPendingError = false; + const nextQueue = []; + const valueQueue = []; + let eventCount = 0; + let isLimitReached = false; -"use strict"; - -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var stream = __webpack_require__(173); -var fsStat = __webpack_require__(736); -var fs_1 = __webpack_require__(740); -var FileSystemStream = /** @class */ (function (_super) { - __extends(FileSystemStream, _super); - function FileSystemStream() { - return _super !== null && _super.apply(this, arguments) || this; - } - /** - * Use stream API to read entries for Task. - */ - FileSystemStream.prototype.read = function (patterns, filter) { - var _this = this; - var filepaths = patterns.map(this.getFullEntryPath, this); - var transform = new stream.Transform({ objectMode: true }); - transform._transform = function (index, _enc, done) { - return _this.getEntry(filepaths[index], patterns[index]).then(function (entry) { - if (entry !== null && filter(entry)) { - transform.push(entry); - } - if (index === filepaths.length - 1) { - transform.end(); - } - done(); - }); - }; - for (var i = 0; i < filepaths.length; i++) { - transform.write(i); - } - return transform; - }; - /** - * Return entry for the provided path. - */ - FileSystemStream.prototype.getEntry = function (filepath, pattern) { - var _this = this; - return this.getStat(filepath) - .then(function (stat) { return _this.makeEntry(stat, pattern); }) - .catch(function () { return null; }); - }; - /** - * Return fs.Stats for the provided path. - */ - FileSystemStream.prototype.getStat = function (filepath) { - return fsStat.stat(filepath, { throwErrorOnBrokenSymlinks: false }); - }; - return FileSystemStream; -}(fs_1.default)); -exports.default = FileSystemStream; + const valueHandler = (...args) => { + eventCount++; + isLimitReached = eventCount === limit; + const value = options.multiArgs ? args : args[0]; -/***/ }), -/* 736 */ -/***/ (function(module, exports, __webpack_require__) { + if (nextQueue.length > 0) { + const {resolve} = nextQueue.shift(); -"use strict"; + resolve({done: false, value}); -Object.defineProperty(exports, "__esModule", { value: true }); -const optionsManager = __webpack_require__(737); -const statProvider = __webpack_require__(739); -/** - * Asynchronous API. - */ -function stat(path, opts) { - return new Promise((resolve, reject) => { - statProvider.async(path, optionsManager.prepare(opts), (err, stats) => err ? reject(err) : resolve(stats)); - }); -} -exports.stat = stat; -function statCallback(path, optsOrCallback, callback) { - if (typeof optsOrCallback === 'function') { - callback = optsOrCallback; /* tslint:disable-line: no-parameter-reassignment */ - optsOrCallback = undefined; /* tslint:disable-line: no-parameter-reassignment */ - } - if (typeof callback === 'undefined') { - throw new TypeError('The "callback" argument must be of type Function.'); - } - statProvider.async(path, optionsManager.prepare(optsOrCallback), callback); -} -exports.statCallback = statCallback; -/** - * Synchronous API. - */ -function statSync(path, opts) { - return statProvider.sync(path, optionsManager.prepare(opts)); -} -exports.statSync = statSync; + if (isLimitReached) { + cancel(); + } + return; + } -/***/ }), -/* 737 */ -/***/ (function(module, exports, __webpack_require__) { + valueQueue.push(value); -"use strict"; + if (isLimitReached) { + cancel(); + } + }; -Object.defineProperty(exports, "__esModule", { value: true }); -const fsAdapter = __webpack_require__(738); -function prepare(opts) { - const options = Object.assign({ - fs: fsAdapter.getFileSystemAdapter(opts ? opts.fs : undefined), - throwErrorOnBrokenSymlinks: true, - followSymlinks: true - }, opts); - return options; -} -exports.prepare = prepare; + const cancel = () => { + isDone = true; + for (const event of events) { + removeListener(event, valueHandler); + } + for (const rejectionEvent of options.rejectionEvents) { + removeListener(rejectionEvent, rejectHandler); + } -/***/ }), -/* 738 */ -/***/ (function(module, exports, __webpack_require__) { + for (const resolutionEvent of options.resolutionEvents) { + removeListener(resolutionEvent, resolveHandler); + } -"use strict"; + while (nextQueue.length > 0) { + const {resolve} = nextQueue.shift(); + resolve({done: true, value: undefined}); + } + }; -Object.defineProperty(exports, "__esModule", { value: true }); -const fs = __webpack_require__(132); -exports.FILE_SYSTEM_ADAPTER = { - lstat: fs.lstat, - stat: fs.stat, - lstatSync: fs.lstatSync, - statSync: fs.statSync -}; -function getFileSystemAdapter(fsMethods) { - if (!fsMethods) { - return exports.FILE_SYSTEM_ADAPTER; - } - return Object.assign({}, exports.FILE_SYSTEM_ADAPTER, fsMethods); -} -exports.getFileSystemAdapter = getFileSystemAdapter; + const rejectHandler = (...args) => { + error = options.multiArgs ? args : args[0]; + if (nextQueue.length > 0) { + const {reject} = nextQueue.shift(); + reject(error); + } else { + hasPendingError = true; + } -/***/ }), -/* 739 */ -/***/ (function(module, exports, __webpack_require__) { + cancel(); + }; -"use strict"; + const resolveHandler = (...args) => { + const value = options.multiArgs ? args : args[0]; -Object.defineProperty(exports, "__esModule", { value: true }); -function sync(path, options) { - const lstat = options.fs.lstatSync(path); - if (!isFollowedSymlink(lstat, options)) { - return lstat; - } - try { - const stat = options.fs.statSync(path); - stat.isSymbolicLink = () => true; - return stat; - } - catch (err) { - if (!options.throwErrorOnBrokenSymlinks) { - return lstat; - } - throw err; - } -} -exports.sync = sync; -function async(path, options, callback) { - options.fs.lstat(path, (err0, lstat) => { - if (err0) { - return callback(err0, undefined); - } - if (!isFollowedSymlink(lstat, options)) { - return callback(null, lstat); - } - options.fs.stat(path, (err1, stat) => { - if (err1) { - return options.throwErrorOnBrokenSymlinks ? callback(err1) : callback(null, lstat); - } - stat.isSymbolicLink = () => true; - callback(null, stat); - }); - }); -} -exports.async = async; -/** - * Returns `true` for followed symlink. - */ -function isFollowedSymlink(stat, options) { - return stat.isSymbolicLink() && options.followSymlinks; -} -exports.isFollowedSymlink = isFollowedSymlink; + if (options.filter && !options.filter(value)) { + return; + } + if (nextQueue.length > 0) { + const {resolve} = nextQueue.shift(); + resolve({done: true, value}); + } else { + valueQueue.push(value); + } -/***/ }), -/* 740 */ -/***/ (function(module, exports, __webpack_require__) { + cancel(); + }; -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -var path = __webpack_require__(4); -var FileSystem = /** @class */ (function () { - function FileSystem(options) { - this.options = options; - } - /** - * Return full path to entry. - */ - FileSystem.prototype.getFullEntryPath = function (filepath) { - return path.resolve(this.options.cwd, filepath); - }; - /** - * Return an implementation of the Entry interface. - */ - FileSystem.prototype.makeEntry = function (stat, pattern) { - stat.path = pattern; - stat.depth = pattern.split('/').length; - return stat; - }; - return FileSystem; -}()); -exports.default = FileSystem; + for (const event of events) { + addListener(event, valueHandler); + } + for (const rejectionEvent of options.rejectionEvents) { + addListener(rejectionEvent, rejectHandler); + } -/***/ }), -/* 741 */ -/***/ (function(module, exports, __webpack_require__) { + for (const resolutionEvent of options.resolutionEvents) { + addListener(resolutionEvent, resolveHandler); + } -"use strict"; - -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var stream = __webpack_require__(173); -var readdir = __webpack_require__(718); -var reader_1 = __webpack_require__(731); -var fs_stream_1 = __webpack_require__(735); -var TransformStream = /** @class */ (function (_super) { - __extends(TransformStream, _super); - function TransformStream(reader) { - var _this = _super.call(this, { objectMode: true }) || this; - _this.reader = reader; - return _this; - } - TransformStream.prototype._transform = function (entry, _encoding, callback) { - callback(null, this.reader.transform(entry)); - }; - return TransformStream; -}(stream.Transform)); -var ReaderStream = /** @class */ (function (_super) { - __extends(ReaderStream, _super); - function ReaderStream() { - return _super !== null && _super.apply(this, arguments) || this; - } - Object.defineProperty(ReaderStream.prototype, "fsAdapter", { - /** - * Returns FileSystem adapter. - */ - get: function () { - return new fs_stream_1.default(this.options); - }, - enumerable: true, - configurable: true - }); - /** - * Use stream API to read entries for Task. - */ - ReaderStream.prototype.read = function (task) { - var _this = this; - var root = this.getRootDirectory(task); - var options = this.getReaderOptions(task); - var transform = new TransformStream(this); - var readable = this.api(root, task, options); - return readable - .on('error', function (err) { return _this.isEnoentCodeError(err) ? null : transform.emit('error', err); }) - .pipe(transform); - }; - /** - * Returns founded paths. - */ - ReaderStream.prototype.api = function (root, task, options) { - if (task.dynamic) { - return this.dynamicApi(root, options); - } - return this.staticApi(task, options); - }; - /** - * Api for dynamic tasks. - */ - ReaderStream.prototype.dynamicApi = function (root, options) { - return readdir.readdirStreamStat(root, options); - }; - /** - * Api for static tasks. - */ - ReaderStream.prototype.staticApi = function (task, options) { - return this.fsAdapter.read(task.patterns, options.filter); - }; - return ReaderStream; -}(reader_1.default)); -exports.default = ReaderStream; + return { + [symbolAsyncIterator]() { + return this; + }, + async next() { + if (valueQueue.length > 0) { + const value = valueQueue.shift(); + return { + done: isDone && valueQueue.length === 0 && !isLimitReached, + value + }; + } + + if (hasPendingError) { + hasPendingError = false; + throw error; + } + + if (isDone) { + return { + done: true, + value: undefined + }; + } + + return new Promise((resolve, reject) => nextQueue.push({resolve, reject})); + }, + async return(value) { + cancel(); + return { + done: isDone, + value + }; + } + }; +}; /***/ }), -/* 742 */ +/* 762 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var readdir = __webpack_require__(718); -var reader_1 = __webpack_require__(731); -var fs_sync_1 = __webpack_require__(743); -var ReaderSync = /** @class */ (function (_super) { - __extends(ReaderSync, _super); - function ReaderSync() { - return _super !== null && _super.apply(this, arguments) || this; - } - Object.defineProperty(ReaderSync.prototype, "fsAdapter", { - /** - * Returns FileSystem adapter. - */ - get: function () { - return new fs_sync_1.default(this.options); - }, - enumerable: true, - configurable: true - }); - /** - * Use sync API to read entries for Task. - */ - ReaderSync.prototype.read = function (task) { - var root = this.getRootDirectory(task); - var options = this.getReaderOptions(task); - try { - var entries = this.api(root, task, options); - return entries.map(this.transform, this); - } - catch (err) { - if (this.isEnoentCodeError(err)) { - return []; - } - throw err; - } - }; - /** - * Returns founded paths. - */ - ReaderSync.prototype.api = function (root, task, options) { - if (task.dynamic) { - return this.dynamicApi(root, options); - } - return this.staticApi(task, options); - }; - /** - * Api for dynamic tasks. - */ - ReaderSync.prototype.dynamicApi = function (root, options) { - return readdir.readdirSyncStat(root, options); - }; - /** - * Api for static tasks. - */ - ReaderSync.prototype.staticApi = function (task, options) { - return this.fsAdapter.read(task.patterns, options.filter); - }; - return ReaderSync; -}(reader_1.default)); -exports.default = ReaderSync; + +const pFinally = __webpack_require__(763); + +class TimeoutError extends Error { + constructor(message) { + super(message); + this.name = 'TimeoutError'; + } +} + +module.exports = (promise, ms, fallback) => new Promise((resolve, reject) => { + if (typeof ms !== 'number' || ms < 0) { + throw new TypeError('Expected `ms` to be a positive number'); + } + + const timer = setTimeout(() => { + if (typeof fallback === 'function') { + try { + resolve(fallback()); + } catch (err) { + reject(err); + } + return; + } + + const message = typeof fallback === 'string' ? fallback : `Promise timed out after ${ms} milliseconds`; + const err = fallback instanceof Error ? fallback : new TimeoutError(message); + + if (typeof promise.cancel === 'function') { + promise.cancel(); + } + + reject(err); + }, ms); + + pFinally( + promise.then(resolve, reject), + () => { + clearTimeout(timer); + } + ); +}); + +module.exports.TimeoutError = TimeoutError; /***/ }), -/* 743 */ +/* 763 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -var fsStat = __webpack_require__(736); -var fs_1 = __webpack_require__(740); -var FileSystemSync = /** @class */ (function (_super) { - __extends(FileSystemSync, _super); - function FileSystemSync() { - return _super !== null && _super.apply(this, arguments) || this; - } - /** - * Use sync API to read entries for Task. - */ - FileSystemSync.prototype.read = function (patterns, filter) { - var _this = this; - var entries = []; - patterns.forEach(function (pattern) { - var filepath = _this.getFullEntryPath(pattern); - var entry = _this.getEntry(filepath, pattern); - if (entry === null || !filter(entry)) { - return; - } - entries.push(entry); - }); - return entries; - }; - /** - * Return entry for the provided path. - */ - FileSystemSync.prototype.getEntry = function (filepath, pattern) { - try { - var stat = this.getStat(filepath); - return this.makeEntry(stat, pattern); - } - catch (err) { - return null; - } - }; - /** - * Return fs.Stats for the provided path. - */ - FileSystemSync.prototype.getStat = function (filepath) { - return fsStat.statSync(filepath, { throwErrorOnBrokenSymlinks: false }); - }; - return FileSystemSync; -}(fs_1.default)); -exports.default = FileSystemSync; + +module.exports = (promise, onFinally) => { + onFinally = onFinally || (() => {}); + + return promise.then( + val => new Promise(resolve => { + resolve(onFinally()); + }).then(() => val), + err => new Promise(resolve => { + resolve(onFinally()); + }).then(() => { + throw err; + }) + ); +}; /***/ }), -/* 744 */ +/* 764 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -/** - * Flatten nested arrays (max depth is 2) into a non-nested array of non-array items. - */ -function flatten(items) { - return items.reduce(function (collection, item) { return [].concat(collection, item); }, []); -} -exports.flatten = flatten; + +const NestedError = __webpack_require__(765); + +class CpFileError extends NestedError { + constructor(message, nested) { + super(message, nested); + Object.assign(this, nested); + this.name = 'CpFileError'; + } +} + +module.exports = CpFileError; /***/ }), -/* 745 */ +/* 765 */ /***/ (function(module, exports, __webpack_require__) { -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -var merge2 = __webpack_require__(243); -/** - * Merge multiple streams and propagate their errors into one stream in parallel. - */ -function merge(streams) { - var mergedStream = merge2(streams); - streams.forEach(function (stream) { - stream.on('error', function (err) { return mergedStream.emit('error', err); }); - }); - return mergedStream; -} -exports.merge = merge; +var inherits = __webpack_require__(113).inherits; + +var NestedError = function (message, nested) { + this.nested = nested; + + if (message instanceof Error) { + nested = message; + } else if (typeof message !== 'undefined') { + Object.defineProperty(this, 'message', { + value: message, + writable: true, + enumerable: false, + configurable: true + }); + } + + Error.captureStackTrace(this, this.constructor); + var oldStackDescriptor = Object.getOwnPropertyDescriptor(this, 'stack'); + var stackDescriptor = buildStackDescriptor(oldStackDescriptor, nested); + Object.defineProperty(this, 'stack', stackDescriptor); +}; + +function buildStackDescriptor(oldStackDescriptor, nested) { + if (oldStackDescriptor.get) { + return { + get: function () { + var stack = oldStackDescriptor.get.call(this); + return buildCombinedStacks(stack, this.nested); + } + }; + } else { + var stack = oldStackDescriptor.value; + return { + value: buildCombinedStacks(stack, nested) + }; + } +} + +function buildCombinedStacks(stack, nested) { + if (nested) { + stack += '\nCaused By: ' + nested.stack; + } + return stack; +} + +inherits(NestedError, Error); +NestedError.prototype.name = 'NestedError'; + + +module.exports = NestedError; /***/ }), -/* 746 */ +/* 766 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const path = __webpack_require__(4); -const pathType = __webpack_require__(747); +const {promisify} = __webpack_require__(113); +const fs = __webpack_require__(233); +const makeDir = __webpack_require__(767); +const pEvent = __webpack_require__(761); +const CpFileError = __webpack_require__(764); -const getExtensions = extensions => extensions.length > 1 ? `{${extensions.join(',')}}` : extensions[0]; +const stat = promisify(fs.stat); +const lstat = promisify(fs.lstat); +const utimes = promisify(fs.utimes); +const chmod = promisify(fs.chmod); +const chown = promisify(fs.chown); -const getPath = (filepath, cwd) => { - const pth = filepath[0] === '!' ? filepath.slice(1) : filepath; - return path.isAbsolute(pth) ? pth : path.join(cwd, pth); -}; +exports.closeSync = fs.closeSync.bind(fs); +exports.createWriteStream = fs.createWriteStream.bind(fs); -const addExtensions = (file, extensions) => { - if (path.extname(file)) { - return `**/${file}`; +exports.createReadStream = async (path, options) => { + const read = fs.createReadStream(path, options); + + try { + await pEvent(read, ['readable', 'end']); + } catch (error) { + throw new CpFileError(`Cannot read from \`${path}\`: ${error.message}`, error); } - return `**/${file}.${getExtensions(extensions)}`; + return read; }; -const getGlob = (dir, opts) => { - if (opts.files && !Array.isArray(opts.files)) { - throw new TypeError(`Expected \`files\` to be of type \`Array\` but received type \`${typeof opts.files}\``); - } +exports.stat = path => stat(path).catch(error => { + throw new CpFileError(`Cannot stat path \`${path}\`: ${error.message}`, error); +}); - if (opts.extensions && !Array.isArray(opts.extensions)) { - throw new TypeError(`Expected \`extensions\` to be of type \`Array\` but received type \`${typeof opts.extensions}\``); - } +exports.lstat = path => lstat(path).catch(error => { + throw new CpFileError(`lstat \`${path}\` failed: ${error.message}`, error); +}); - if (opts.files && opts.extensions) { - return opts.files.map(x => path.join(dir, addExtensions(x, opts.extensions))); - } +exports.utimes = (path, atime, mtime) => utimes(path, atime, mtime).catch(error => { + throw new CpFileError(`utimes \`${path}\` failed: ${error.message}`, error); +}); - if (opts.files) { - return opts.files.map(x => path.join(dir, `**/${x}`)); - } +exports.chmod = (path, mode) => chmod(path, mode).catch(error => { + throw new CpFileError(`chmod \`${path}\` failed: ${error.message}`, error); +}); - if (opts.extensions) { - return [path.join(dir, `**/*.${getExtensions(opts.extensions)}`)]; - } +exports.chown = (path, uid, gid) => chown(path, uid, gid).catch(error => { + throw new CpFileError(`chown \`${path}\` failed: ${error.message}`, error); +}); - return [path.join(dir, '**')]; +exports.statSync = path => { + try { + return fs.statSync(path); + } catch (error) { + throw new CpFileError(`stat \`${path}\` failed: ${error.message}`, error); + } }; -module.exports = (input, opts) => { - opts = Object.assign({cwd: process.cwd()}, opts); +exports.utimesSync = (path, atime, mtime) => { + try { + return fs.utimesSync(path, atime, mtime); + } catch (error) { + throw new CpFileError(`utimes \`${path}\` failed: ${error.message}`, error); + } +}; - if (typeof opts.cwd !== 'string') { - return Promise.reject(new TypeError(`Expected \`cwd\` to be of type \`string\` but received type \`${typeof opts.cwd}\``)); +exports.chmodSync = (path, mode) => { + try { + return fs.chmodSync(path, mode); + } catch (error) { + throw new CpFileError(`chmod \`${path}\` failed: ${error.message}`, error); } +}; - return Promise.all([].concat(input).map(x => pathType.dir(getPath(x, opts.cwd)) - .then(isDir => isDir ? getGlob(x, opts) : x))) - .then(globs => [].concat.apply([], globs)); +exports.chownSync = (path, uid, gid) => { + try { + return fs.chownSync(path, uid, gid); + } catch (error) { + throw new CpFileError(`chown \`${path}\` failed: ${error.message}`, error); + } }; -module.exports.sync = (input, opts) => { - opts = Object.assign({cwd: process.cwd()}, opts); +exports.makeDir = path => makeDir(path, {fs}).catch(error => { + throw new CpFileError(`Cannot create directory \`${path}\`: ${error.message}`, error); +}); - if (typeof opts.cwd !== 'string') { - throw new TypeError(`Expected \`cwd\` to be of type \`string\` but received type \`${typeof opts.cwd}\``); +exports.makeDirSync = path => { + try { + makeDir.sync(path, {fs}); + } catch (error) { + throw new CpFileError(`Cannot create directory \`${path}\`: ${error.message}`, error); } +}; - const globs = [].concat(input).map(x => pathType.dirSync(getPath(x, opts.cwd)) ? getGlob(x, opts) : x); - return [].concat.apply([], globs); +exports.copyFileSync = (source, destination, flags) => { + try { + fs.copyFileSync(source, destination, flags); + } catch (error) { + throw new CpFileError(`Cannot copy from \`${source}\` to \`${destination}\`: ${error.message}`, error); + } }; /***/ }), -/* 747 */ +/* 767 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(132); -const pify = __webpack_require__(748); +const path = __webpack_require__(4); +const {promisify} = __webpack_require__(113); +const semver = __webpack_require__(768); -function type(fn, fn2, fp) { - if (typeof fp !== 'string') { - return Promise.reject(new TypeError(`Expected a string, got ${typeof fp}`)); +const useNativeRecursiveOption = semver.satisfies(process.version, '>=10.12.0'); + +// https://github.com/nodejs/node/issues/8987 +// https://github.com/libuv/libuv/pull/1088 +const checkPath = pth => { + if (process.platform === 'win32') { + const pathHasInvalidWinCharacters = /[<>:"|?*]/.test(pth.replace(path.parse(pth).root, '')); + + if (pathHasInvalidWinCharacters) { + const error = new Error(`Path contains invalid characters: ${pth}`); + error.code = 'EINVAL'; + throw error; + } } +}; - return pify(fs[fn])(fp) - .then(stats => stats[fn2]()) - .catch(err => { - if (err.code === 'ENOENT') { - return false; - } +const processOptions = options => { + // https://github.com/sindresorhus/make-dir/issues/18 + const defaults = { + mode: 0o777, + fs + }; - throw err; + return { + ...defaults, + ...options + }; +}; + +const permissionError = pth => { + // This replicates the exception of `fs.mkdir` with native the + // `recusive` option when run on an invalid drive under Windows. + const error = new Error(`operation not permitted, mkdir '${pth}'`); + error.code = 'EPERM'; + error.errno = -4048; + error.path = pth; + error.syscall = 'mkdir'; + return error; +}; + +const makeDir = async (input, options) => { + checkPath(input); + options = processOptions(options); + + const mkdir = promisify(options.fs.mkdir); + const stat = promisify(options.fs.stat); + + if (useNativeRecursiveOption && options.fs.mkdir === fs.mkdir) { + const pth = path.resolve(input); + + await mkdir(pth, { + mode: options.mode, + recursive: true }); -} -function typeSync(fn, fn2, fp) { - if (typeof fp !== 'string') { - throw new TypeError(`Expected a string, got ${typeof fp}`); + return pth; } - try { - return fs[fn](fp)[fn2](); - } catch (err) { - if (err.code === 'ENOENT') { - return false; + const make = async pth => { + try { + await mkdir(pth, options.mode); + + return pth; + } catch (error) { + if (error.code === 'EPERM') { + throw error; + } + + if (error.code === 'ENOENT') { + if (path.dirname(pth) === pth) { + throw permissionError(pth); + } + + if (error.message.includes('null bytes')) { + throw error; + } + + await make(path.dirname(pth)); + + return make(pth); + } + + try { + const stats = await stat(pth); + if (!stats.isDirectory()) { + throw new Error('The path is not a directory'); + } + } catch (_) { + throw error; + } + + return pth; } + }; - throw err; + return make(path.resolve(input)); +}; + +module.exports = makeDir; + +module.exports.sync = (input, options) => { + checkPath(input); + options = processOptions(options); + + if (useNativeRecursiveOption && options.fs.mkdirSync === fs.mkdirSync) { + const pth = path.resolve(input); + + fs.mkdirSync(pth, { + mode: options.mode, + recursive: true + }); + + return pth; } -} -exports.file = type.bind(null, 'stat', 'isFile'); -exports.dir = type.bind(null, 'stat', 'isDirectory'); -exports.symlink = type.bind(null, 'lstat', 'isSymbolicLink'); -exports.fileSync = typeSync.bind(null, 'statSync', 'isFile'); -exports.dirSync = typeSync.bind(null, 'statSync', 'isDirectory'); -exports.symlinkSync = typeSync.bind(null, 'lstatSync', 'isSymbolicLink'); + const make = pth => { + try { + options.fs.mkdirSync(pth, options.mode); + } catch (error) { + if (error.code === 'EPERM') { + throw error; + } + + if (error.code === 'ENOENT') { + if (path.dirname(pth) === pth) { + throw permissionError(pth); + } + + if (error.message.includes('null bytes')) { + throw error; + } + + make(path.dirname(pth)); + return make(pth); + } + + try { + if (!options.fs.statSync(pth).isDirectory()) { + throw new Error('The path is not a directory'); + } + } catch (_) { + throw error; + } + } + + return pth; + }; + + return make(path.resolve(input)); +}; /***/ }), -/* 748 */ -/***/ (function(module, exports, __webpack_require__) { +/* 768 */ +/***/ (function(module, exports) { -"use strict"; +exports = module.exports = SemVer + +var debug +/* istanbul ignore next */ +if (typeof process === 'object' && + process.env && + process.env.NODE_DEBUG && + /\bsemver\b/i.test(process.env.NODE_DEBUG)) { + debug = function () { + var args = Array.prototype.slice.call(arguments, 0) + args.unshift('SEMVER') + console.log.apply(console, args) + } +} else { + debug = function () {} +} +// Note: this is the semver.org version of the spec that it implements +// Not necessarily the package version of this code. +exports.SEMVER_SPEC_VERSION = '2.0.0' -const processFn = (fn, opts) => function () { - const P = opts.promiseModule; - const args = new Array(arguments.length); +var MAX_LENGTH = 256 +var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || + /* istanbul ignore next */ 9007199254740991 + +// Max safe segment length for coercion. +var MAX_SAFE_COMPONENT_LENGTH = 16 + +// The actual regexps go on exports.re +var re = exports.re = [] +var src = exports.src = [] +var t = exports.tokens = {} +var R = 0 + +function tok (n) { + t[n] = R++ +} + +// The following Regular Expressions can be used for tokenizing, +// validating, and parsing SemVer version strings. + +// ## Numeric Identifier +// A single `0`, or a non-zero digit followed by zero or more digits. + +tok('NUMERICIDENTIFIER') +src[t.NUMERICIDENTIFIER] = '0|[1-9]\\d*' +tok('NUMERICIDENTIFIERLOOSE') +src[t.NUMERICIDENTIFIERLOOSE] = '[0-9]+' + +// ## Non-numeric Identifier +// Zero or more digits, followed by a letter or hyphen, and then zero or +// more letters, digits, or hyphens. + +tok('NONNUMERICIDENTIFIER') +src[t.NONNUMERICIDENTIFIER] = '\\d*[a-zA-Z-][a-zA-Z0-9-]*' - for (let i = 0; i < arguments.length; i++) { - args[i] = arguments[i]; - } +// ## Main Version +// Three dot-separated numeric identifiers. - return new P((resolve, reject) => { - if (opts.errorFirst) { - args.push(function (err, result) { - if (opts.multiArgs) { - const results = new Array(arguments.length - 1); +tok('MAINVERSION') +src[t.MAINVERSION] = '(' + src[t.NUMERICIDENTIFIER] + ')\\.' + + '(' + src[t.NUMERICIDENTIFIER] + ')\\.' + + '(' + src[t.NUMERICIDENTIFIER] + ')' - for (let i = 1; i < arguments.length; i++) { - results[i - 1] = arguments[i]; - } +tok('MAINVERSIONLOOSE') +src[t.MAINVERSIONLOOSE] = '(' + src[t.NUMERICIDENTIFIERLOOSE] + ')\\.' + + '(' + src[t.NUMERICIDENTIFIERLOOSE] + ')\\.' + + '(' + src[t.NUMERICIDENTIFIERLOOSE] + ')' - if (err) { - results.unshift(err); - reject(results); - } else { - resolve(results); - } - } else if (err) { - reject(err); - } else { - resolve(result); - } - }); - } else { - args.push(function (result) { - if (opts.multiArgs) { - const results = new Array(arguments.length - 1); +// ## Pre-release Version Identifier +// A numeric identifier, or a non-numeric identifier. - for (let i = 0; i < arguments.length; i++) { - results[i] = arguments[i]; - } +tok('PRERELEASEIDENTIFIER') +src[t.PRERELEASEIDENTIFIER] = '(?:' + src[t.NUMERICIDENTIFIER] + + '|' + src[t.NONNUMERICIDENTIFIER] + ')' - resolve(results); - } else { - resolve(result); - } - }); - } +tok('PRERELEASEIDENTIFIERLOOSE') +src[t.PRERELEASEIDENTIFIERLOOSE] = '(?:' + src[t.NUMERICIDENTIFIERLOOSE] + + '|' + src[t.NONNUMERICIDENTIFIER] + ')' - fn.apply(this, args); - }); -}; +// ## Pre-release Version +// Hyphen, followed by one or more dot-separated pre-release version +// identifiers. -module.exports = (obj, opts) => { - opts = Object.assign({ - exclude: [/.+(Sync|Stream)$/], - errorFirst: true, - promiseModule: Promise - }, opts); +tok('PRERELEASE') +src[t.PRERELEASE] = '(?:-(' + src[t.PRERELEASEIDENTIFIER] + + '(?:\\.' + src[t.PRERELEASEIDENTIFIER] + ')*))' - const filter = key => { - const match = pattern => typeof pattern === 'string' ? key === pattern : pattern.test(key); - return opts.include ? opts.include.some(match) : !opts.exclude.some(match); - }; +tok('PRERELEASELOOSE') +src[t.PRERELEASELOOSE] = '(?:-?(' + src[t.PRERELEASEIDENTIFIERLOOSE] + + '(?:\\.' + src[t.PRERELEASEIDENTIFIERLOOSE] + ')*))' - let ret; - if (typeof obj === 'function') { - ret = function () { - if (opts.excludeMain) { - return obj.apply(this, arguments); - } +// ## Build Metadata Identifier +// Any combination of digits, letters, or hyphens. - return processFn(obj, opts).apply(this, arguments); - }; - } else { - ret = Object.create(Object.getPrototypeOf(obj)); - } +tok('BUILDIDENTIFIER') +src[t.BUILDIDENTIFIER] = '[0-9A-Za-z-]+' - for (const key in obj) { // eslint-disable-line guard-for-in - const x = obj[key]; - ret[key] = typeof x === 'function' && filter(key) ? processFn(x, opts) : x; - } +// ## Build Metadata +// Plus sign, followed by one or more period-separated build metadata +// identifiers. - return ret; -}; +tok('BUILD') +src[t.BUILD] = '(?:\\+(' + src[t.BUILDIDENTIFIER] + + '(?:\\.' + src[t.BUILDIDENTIFIER] + ')*))' +// ## Full Version String +// A main version, followed optionally by a pre-release version and +// build metadata. -/***/ }), -/* 749 */ -/***/ (function(module, exports, __webpack_require__) { +// Note that the only major, minor, patch, and pre-release sections of +// the version string are capturing groups. The build metadata is not a +// capturing group, because it should not ever be used in version +// comparison. -"use strict"; +tok('FULL') +tok('FULLPLAIN') +src[t.FULLPLAIN] = 'v?' + src[t.MAINVERSION] + + src[t.PRERELEASE] + '?' + + src[t.BUILD] + '?' -const fs = __webpack_require__(132); -const path = __webpack_require__(4); -const fastGlob = __webpack_require__(563); -const gitIgnore = __webpack_require__(750); -const pify = __webpack_require__(404); -const slash = __webpack_require__(751); +src[t.FULL] = '^' + src[t.FULLPLAIN] + '$' -const DEFAULT_IGNORE = [ - '**/node_modules/**', - '**/bower_components/**', - '**/flow-typed/**', - '**/coverage/**', - '**/.git' -]; +// like full, but allows v1.2.3 and =1.2.3, which people do sometimes. +// also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty +// common in the npm registry. +tok('LOOSEPLAIN') +src[t.LOOSEPLAIN] = '[v=\\s]*' + src[t.MAINVERSIONLOOSE] + + src[t.PRERELEASELOOSE] + '?' + + src[t.BUILD] + '?' -const readFileP = pify(fs.readFile); +tok('LOOSE') +src[t.LOOSE] = '^' + src[t.LOOSEPLAIN] + '$' -const mapGitIgnorePatternTo = base => ignore => { - if (ignore.startsWith('!')) { - return '!' + path.posix.join(base, ignore.slice(1)); - } +tok('GTLT') +src[t.GTLT] = '((?:<|>)?=?)' - return path.posix.join(base, ignore); -}; +// Something like "2.*" or "1.2.x". +// Note that "x.x" is a valid xRange identifer, meaning "any version" +// Only the first item is strictly required. +tok('XRANGEIDENTIFIERLOOSE') +src[t.XRANGEIDENTIFIERLOOSE] = src[t.NUMERICIDENTIFIERLOOSE] + '|x|X|\\*' +tok('XRANGEIDENTIFIER') +src[t.XRANGEIDENTIFIER] = src[t.NUMERICIDENTIFIER] + '|x|X|\\*' -const parseGitIgnore = (content, options) => { - const base = slash(path.relative(options.cwd, path.dirname(options.fileName))); +tok('XRANGEPLAIN') +src[t.XRANGEPLAIN] = '[v=\\s]*(' + src[t.XRANGEIDENTIFIER] + ')' + + '(?:\\.(' + src[t.XRANGEIDENTIFIER] + ')' + + '(?:\\.(' + src[t.XRANGEIDENTIFIER] + ')' + + '(?:' + src[t.PRERELEASE] + ')?' + + src[t.BUILD] + '?' + + ')?)?' - return content - .split(/\r?\n/) - .filter(Boolean) - .filter(line => line.charAt(0) !== '#') - .map(mapGitIgnorePatternTo(base)); -}; +tok('XRANGEPLAINLOOSE') +src[t.XRANGEPLAINLOOSE] = '[v=\\s]*(' + src[t.XRANGEIDENTIFIERLOOSE] + ')' + + '(?:\\.(' + src[t.XRANGEIDENTIFIERLOOSE] + ')' + + '(?:\\.(' + src[t.XRANGEIDENTIFIERLOOSE] + ')' + + '(?:' + src[t.PRERELEASELOOSE] + ')?' + + src[t.BUILD] + '?' + + ')?)?' -const reduceIgnore = files => { - return files.reduce((ignores, file) => { - ignores.add(parseGitIgnore(file.content, { - cwd: file.cwd, - fileName: file.filePath - })); - return ignores; - }, gitIgnore()); -}; +tok('XRANGE') +src[t.XRANGE] = '^' + src[t.GTLT] + '\\s*' + src[t.XRANGEPLAIN] + '$' +tok('XRANGELOOSE') +src[t.XRANGELOOSE] = '^' + src[t.GTLT] + '\\s*' + src[t.XRANGEPLAINLOOSE] + '$' -const getIsIgnoredPredecate = (ignores, cwd) => { - return p => ignores.ignores(slash(path.relative(cwd, p))); -}; +// Coercion. +// Extract anything that could conceivably be a part of a valid semver +tok('COERCE') +src[t.COERCE] = '(^|[^\\d])' + + '(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '})' + + '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + + '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + + '(?:$|[^\\d])' +tok('COERCERTL') +re[t.COERCERTL] = new RegExp(src[t.COERCE], 'g') -const getFile = (file, cwd) => { - const filePath = path.join(cwd, file); - return readFileP(filePath, 'utf8') - .then(content => ({ - content, - cwd, - filePath - })); -}; +// Tilde ranges. +// Meaning is "reasonably at or greater than" +tok('LONETILDE') +src[t.LONETILDE] = '(?:~>?)' -const getFileSync = (file, cwd) => { - const filePath = path.join(cwd, file); - const content = fs.readFileSync(filePath, 'utf8'); +tok('TILDETRIM') +src[t.TILDETRIM] = '(\\s*)' + src[t.LONETILDE] + '\\s+' +re[t.TILDETRIM] = new RegExp(src[t.TILDETRIM], 'g') +var tildeTrimReplace = '$1~' - return { - content, - cwd, - filePath - }; -}; +tok('TILDE') +src[t.TILDE] = '^' + src[t.LONETILDE] + src[t.XRANGEPLAIN] + '$' +tok('TILDELOOSE') +src[t.TILDELOOSE] = '^' + src[t.LONETILDE] + src[t.XRANGEPLAINLOOSE] + '$' -const normalizeOptions = (options = {}) => { - const ignore = options.ignore || []; - const cwd = options.cwd || process.cwd(); - return {ignore, cwd}; -}; +// Caret ranges. +// Meaning is "at least and backwards compatible with" +tok('LONECARET') +src[t.LONECARET] = '(?:\\^)' -module.exports = options => { - options = normalizeOptions(options); +tok('CARETTRIM') +src[t.CARETTRIM] = '(\\s*)' + src[t.LONECARET] + '\\s+' +re[t.CARETTRIM] = new RegExp(src[t.CARETTRIM], 'g') +var caretTrimReplace = '$1^' - return fastGlob('**/.gitignore', { - ignore: DEFAULT_IGNORE.concat(options.ignore), - cwd: options.cwd - }) - .then(paths => Promise.all(paths.map(file => getFile(file, options.cwd)))) - .then(files => reduceIgnore(files)) - .then(ignores => getIsIgnoredPredecate(ignores, options.cwd)); -}; +tok('CARET') +src[t.CARET] = '^' + src[t.LONECARET] + src[t.XRANGEPLAIN] + '$' +tok('CARETLOOSE') +src[t.CARETLOOSE] = '^' + src[t.LONECARET] + src[t.XRANGEPLAINLOOSE] + '$' -module.exports.sync = options => { - options = normalizeOptions(options); +// A simple gt/lt/eq thing, or just "" to indicate "any version" +tok('COMPARATORLOOSE') +src[t.COMPARATORLOOSE] = '^' + src[t.GTLT] + '\\s*(' + src[t.LOOSEPLAIN] + ')$|^$' +tok('COMPARATOR') +src[t.COMPARATOR] = '^' + src[t.GTLT] + '\\s*(' + src[t.FULLPLAIN] + ')$|^$' - const paths = fastGlob.sync('**/.gitignore', { - ignore: DEFAULT_IGNORE.concat(options.ignore), - cwd: options.cwd - }); - const files = paths.map(file => getFileSync(file, options.cwd)); - const ignores = reduceIgnore(files); +// An expression to strip any whitespace between the gtlt and the thing +// it modifies, so that `> 1.2.3` ==> `>1.2.3` +tok('COMPARATORTRIM') +src[t.COMPARATORTRIM] = '(\\s*)' + src[t.GTLT] + + '\\s*(' + src[t.LOOSEPLAIN] + '|' + src[t.XRANGEPLAIN] + ')' - return getIsIgnoredPredecate(ignores, options.cwd); -}; +// this one has to use the /g flag +re[t.COMPARATORTRIM] = new RegExp(src[t.COMPARATORTRIM], 'g') +var comparatorTrimReplace = '$1$2$3' + +// Something like `1.2.3 - 1.2.4` +// Note that these all use the loose form, because they'll be +// checked against either the strict or loose comparator form +// later. +tok('HYPHENRANGE') +src[t.HYPHENRANGE] = '^\\s*(' + src[t.XRANGEPLAIN] + ')' + + '\\s+-\\s+' + + '(' + src[t.XRANGEPLAIN] + ')' + + '\\s*$' +tok('HYPHENRANGELOOSE') +src[t.HYPHENRANGELOOSE] = '^\\s*(' + src[t.XRANGEPLAINLOOSE] + ')' + + '\\s+-\\s+' + + '(' + src[t.XRANGEPLAINLOOSE] + ')' + + '\\s*$' -/***/ }), -/* 750 */ -/***/ (function(module, exports) { +// Star ranges basically just allow anything at all. +tok('STAR') +src[t.STAR] = '(<|>)?=?\\s*\\*' -// A simple implementation of make-array -function make_array (subject) { - return Array.isArray(subject) - ? subject - : [subject] +// Compile to actual regexp objects. +// All are flag-free, unless they were created above with a flag. +for (var i = 0; i < R; i++) { + debug(i, src[i]) + if (!re[i]) { + re[i] = new RegExp(src[i]) + } } -const REGEX_BLANK_LINE = /^\s+$/ -const REGEX_LEADING_EXCAPED_EXCLAMATION = /^\\!/ -const REGEX_LEADING_EXCAPED_HASH = /^\\#/ -const SLASH = '/' -const KEY_IGNORE = typeof Symbol !== 'undefined' - ? Symbol.for('node-ignore') - /* istanbul ignore next */ - : 'node-ignore' +exports.parse = parse +function parse (version, options) { + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false + } + } -const define = (object, key, value) => - Object.defineProperty(object, key, {value}) + if (version instanceof SemVer) { + return version + } -const REGEX_REGEXP_RANGE = /([0-z])-([0-z])/g + if (typeof version !== 'string') { + return null + } -// Sanitize the range of a regular expression -// The cases are complicated, see test cases for details -const sanitizeRange = range => range.replace( - REGEX_REGEXP_RANGE, - (match, from, to) => from.charCodeAt(0) <= to.charCodeAt(0) - ? match - // Invalid range (out of order) which is ok for gitignore rules but - // fatal for JavaScript regular expression, so eliminate it. - : '' -) + if (version.length > MAX_LENGTH) { + return null + } -// > If the pattern ends with a slash, -// > it is removed for the purpose of the following description, -// > but it would only find a match with a directory. -// > In other words, foo/ will match a directory foo and paths underneath it, -// > but will not match a regular file or a symbolic link foo -// > (this is consistent with the way how pathspec works in general in Git). -// '`foo/`' will not match regular file '`foo`' or symbolic link '`foo`' -// -> ignore-rules will not deal with it, because it costs extra `fs.stat` call -// you could use option `mark: true` with `glob` + var r = options.loose ? re[t.LOOSE] : re[t.FULL] + if (!r.test(version)) { + return null + } -// '`foo/`' should not continue with the '`..`' -const DEFAULT_REPLACER_PREFIX = [ + try { + return new SemVer(version, options) + } catch (er) { + return null + } +} - // > Trailing spaces are ignored unless they are quoted with backslash ("\") - [ - // (a\ ) -> (a ) - // (a ) -> (a) - // (a \ ) -> (a ) - /\\?\s+$/, - match => match.indexOf('\\') === 0 - ? ' ' - : '' - ], +exports.valid = valid +function valid (version, options) { + var v = parse(version, options) + return v ? v.version : null +} - // replace (\ ) with ' ' - [ - /\\\s/g, - () => ' ' - ], +exports.clean = clean +function clean (version, options) { + var s = parse(version.trim().replace(/^[=v]+/, ''), options) + return s ? s.version : null +} - // Escape metacharacters - // which is written down by users but means special for regular expressions. +exports.SemVer = SemVer - // > There are 12 characters with special meanings: - // > - the backslash \, - // > - the caret ^, - // > - the dollar sign $, - // > - the period or dot ., - // > - the vertical bar or pipe symbol |, - // > - the question mark ?, - // > - the asterisk or star *, - // > - the plus sign +, - // > - the opening parenthesis (, - // > - the closing parenthesis ), - // > - and the opening square bracket [, - // > - the opening curly brace {, - // > These special characters are often called "metacharacters". - [ - /[\\^$.|*+(){]/g, - match => `\\${match}` - ], +function SemVer (version, options) { + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false + } + } + if (version instanceof SemVer) { + if (version.loose === options.loose) { + return version + } else { + version = version.version + } + } else if (typeof version !== 'string') { + throw new TypeError('Invalid Version: ' + version) + } - [ - // > [abc] matches any character inside the brackets - // > (in this case a, b, or c); - /\[([^\]/]*)($|\])/g, - (match, p1, p2) => p2 === ']' - ? `[${sanitizeRange(p1)}]` - : `\\${match}` - ], + if (version.length > MAX_LENGTH) { + throw new TypeError('version is longer than ' + MAX_LENGTH + ' characters') + } - [ - // > a question mark (?) matches a single character - /(?!\\)\?/g, - () => '[^/]' - ], + if (!(this instanceof SemVer)) { + return new SemVer(version, options) + } - // leading slash - [ + debug('SemVer', version, options) + this.options = options + this.loose = !!options.loose - // > A leading slash matches the beginning of the pathname. - // > For example, "/*.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c". - // A leading slash matches the beginning of the pathname - /^\//, - () => '^' - ], + var m = version.trim().match(options.loose ? re[t.LOOSE] : re[t.FULL]) - // replace special metacharacter slash after the leading slash - [ - /\//g, - () => '\\/' - ], + if (!m) { + throw new TypeError('Invalid Version: ' + version) + } - [ - // > A leading "**" followed by a slash means match in all directories. - // > For example, "**/foo" matches file or directory "foo" anywhere, - // > the same as pattern "foo". - // > "**/foo/bar" matches file or directory "bar" anywhere that is directly - // > under directory "foo". - // Notice that the '*'s have been replaced as '\\*' - /^\^*\\\*\\\*\\\//, + this.raw = version - // '**/foo' <-> 'foo' - () => '^(?:.*\\/)?' - ] -] + // these are actually numbers + this.major = +m[1] + this.minor = +m[2] + this.patch = +m[3] -const DEFAULT_REPLACER_SUFFIX = [ - // starting - [ - // there will be no leading '/' - // (which has been replaced by section "leading slash") - // If starts with '**', adding a '^' to the regular expression also works - /^(?=[^^])/, - function startingReplacer () { - return !/\/(?!$)/.test(this) - // > If the pattern does not contain a slash /, - // > Git treats it as a shell glob pattern - // Actually, if there is only a trailing slash, - // git also treats it as a shell glob pattern - ? '(?:^|\\/)' + if (this.major > MAX_SAFE_INTEGER || this.major < 0) { + throw new TypeError('Invalid major version') + } + + if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) { + throw new TypeError('Invalid minor version') + } - // > Otherwise, Git treats the pattern as a shell glob suitable for - // > consumption by fnmatch(3) - : '^' - } - ], + if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) { + throw new TypeError('Invalid patch version') + } - // two globstars - [ - // Use lookahead assertions so that we could match more than one `'/**'` - /\\\/\\\*\\\*(?=\\\/|$)/g, + // numberify any prerelease numeric ids + if (!m[4]) { + this.prerelease = [] + } else { + this.prerelease = m[4].split('.').map(function (id) { + if (/^[0-9]+$/.test(id)) { + var num = +id + if (num >= 0 && num < MAX_SAFE_INTEGER) { + return num + } + } + return id + }) + } - // Zero, one or several directories - // should not use '*', or it will be replaced by the next replacer + this.build = m[5] ? m[5].split('.') : [] + this.format() +} - // Check if it is not the last `'/**'` - (match, index, str) => index + 6 < str.length +SemVer.prototype.format = function () { + this.version = this.major + '.' + this.minor + '.' + this.patch + if (this.prerelease.length) { + this.version += '-' + this.prerelease.join('.') + } + return this.version +} - // case: /**/ - // > A slash followed by two consecutive asterisks then a slash matches - // > zero or more directories. - // > For example, "a/**/b" matches "a/b", "a/x/b", "a/x/y/b" and so on. - // '/**/' - ? '(?:\\/[^\\/]+)*' +SemVer.prototype.toString = function () { + return this.version +} - // case: /** - // > A trailing `"/**"` matches everything inside. +SemVer.prototype.compare = function (other) { + debug('SemVer.compare', this.version, this.options, other) + if (!(other instanceof SemVer)) { + other = new SemVer(other, this.options) + } - // #21: everything inside but it should not include the current folder - : '\\/.+' - ], + return this.compareMain(other) || this.comparePre(other) +} - // intermediate wildcards - [ - // Never replace escaped '*' - // ignore rule '\*' will match the path '*' +SemVer.prototype.compareMain = function (other) { + if (!(other instanceof SemVer)) { + other = new SemVer(other, this.options) + } - // 'abc.*/' -> go - // 'abc.*' -> skip this rule - /(^|[^\\]+)\\\*(?=.+)/g, + return compareIdentifiers(this.major, other.major) || + compareIdentifiers(this.minor, other.minor) || + compareIdentifiers(this.patch, other.patch) +} - // '*.js' matches '.js' - // '*.js' doesn't match 'abc' - (match, p1) => `${p1}[^\\/]*` - ], +SemVer.prototype.comparePre = function (other) { + if (!(other instanceof SemVer)) { + other = new SemVer(other, this.options) + } - // trailing wildcard - [ - /(\^|\\\/)?\\\*$/, - (match, p1) => { - const prefix = p1 - // '\^': - // '/*' does not match '' - // '/*' does not match everything + // NOT having a prerelease is > having one + if (this.prerelease.length && !other.prerelease.length) { + return -1 + } else if (!this.prerelease.length && other.prerelease.length) { + return 1 + } else if (!this.prerelease.length && !other.prerelease.length) { + return 0 + } - // '\\\/': - // 'abc/*' does not match 'abc/' - ? `${p1}[^/]+` + var i = 0 + do { + var a = this.prerelease[i] + var b = other.prerelease[i] + debug('prerelease compare', i, a, b) + if (a === undefined && b === undefined) { + return 0 + } else if (b === undefined) { + return 1 + } else if (a === undefined) { + return -1 + } else if (a === b) { + continue + } else { + return compareIdentifiers(a, b) + } + } while (++i) +} - // 'a*' matches 'a' - // 'a*' matches 'aa' - : '[^/]*' +SemVer.prototype.compareBuild = function (other) { + if (!(other instanceof SemVer)) { + other = new SemVer(other, this.options) + } - return `${prefix}(?=$|\\/$)` + var i = 0 + do { + var a = this.build[i] + var b = other.build[i] + debug('prerelease compare', i, a, b) + if (a === undefined && b === undefined) { + return 0 + } else if (b === undefined) { + return 1 + } else if (a === undefined) { + return -1 + } else if (a === b) { + continue + } else { + return compareIdentifiers(a, b) } - ], + } while (++i) +} - [ - // unescape - /\\\\\\/g, - () => '\\' - ] -] +// preminor will bump the version up to the next minor release, and immediately +// down to pre-release. premajor and prepatch work the same way. +SemVer.prototype.inc = function (release, identifier) { + switch (release) { + case 'premajor': + this.prerelease.length = 0 + this.patch = 0 + this.minor = 0 + this.major++ + this.inc('pre', identifier) + break + case 'preminor': + this.prerelease.length = 0 + this.patch = 0 + this.minor++ + this.inc('pre', identifier) + break + case 'prepatch': + // If this is already a prerelease, it will bump to the next version + // drop any prereleases that might already exist, since they are not + // relevant at this point. + this.prerelease.length = 0 + this.inc('patch', identifier) + this.inc('pre', identifier) + break + // If the input is a non-prerelease version, this acts the same as + // prepatch. + case 'prerelease': + if (this.prerelease.length === 0) { + this.inc('patch', identifier) + } + this.inc('pre', identifier) + break -const POSITIVE_REPLACERS = [ - ...DEFAULT_REPLACER_PREFIX, + case 'major': + // If this is a pre-major version, bump up to the same major version. + // Otherwise increment major. + // 1.0.0-5 bumps to 1.0.0 + // 1.1.0 bumps to 2.0.0 + if (this.minor !== 0 || + this.patch !== 0 || + this.prerelease.length === 0) { + this.major++ + } + this.minor = 0 + this.patch = 0 + this.prerelease = [] + break + case 'minor': + // If this is a pre-minor version, bump up to the same minor version. + // Otherwise increment minor. + // 1.2.0-5 bumps to 1.2.0 + // 1.2.1 bumps to 1.3.0 + if (this.patch !== 0 || this.prerelease.length === 0) { + this.minor++ + } + this.patch = 0 + this.prerelease = [] + break + case 'patch': + // If this is not a pre-release version, it will increment the patch. + // If it is a pre-release it will bump up to the same patch version. + // 1.2.0-5 patches to 1.2.0 + // 1.2.0 patches to 1.2.1 + if (this.prerelease.length === 0) { + this.patch++ + } + this.prerelease = [] + break + // This probably shouldn't be used publicly. + // 1.0.0 "pre" would become 1.0.0-0 which is the wrong direction. + case 'pre': + if (this.prerelease.length === 0) { + this.prerelease = [0] + } else { + var i = this.prerelease.length + while (--i >= 0) { + if (typeof this.prerelease[i] === 'number') { + this.prerelease[i]++ + i = -2 + } + } + if (i === -1) { + // didn't increment anything + this.prerelease.push(0) + } + } + if (identifier) { + // 1.2.0-beta.1 bumps to 1.2.0-beta.2, + // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0 + if (this.prerelease[0] === identifier) { + if (isNaN(this.prerelease[1])) { + this.prerelease = [identifier, 0] + } + } else { + this.prerelease = [identifier, 0] + } + } + break - // 'f' - // matches - // - /f(end) - // - /f/ - // - (start)f(end) - // - (start)f/ - // doesn't match - // - oof - // - foo - // pseudo: - // -> (^|/)f(/|$) + default: + throw new Error('invalid increment argument: ' + release) + } + this.format() + this.raw = this.version + return this +} - // ending - [ - // 'js' will not match 'js.' - // 'ab' will not match 'abc' - /(?:[^*/])$/, +exports.inc = inc +function inc (version, release, loose, identifier) { + if (typeof (loose) === 'string') { + identifier = loose + loose = undefined + } - // 'js*' will not match 'a.js' - // 'js/' will not match 'a.js' - // 'js' will match 'a.js' and 'a.js/' - match => `${match}(?=$|\\/)` - ], + try { + return new SemVer(version, loose).inc(release, identifier).version + } catch (er) { + return null + } +} - ...DEFAULT_REPLACER_SUFFIX -] +exports.diff = diff +function diff (version1, version2) { + if (eq(version1, version2)) { + return null + } else { + var v1 = parse(version1) + var v2 = parse(version2) + var prefix = '' + if (v1.prerelease.length || v2.prerelease.length) { + prefix = 'pre' + var defaultResult = 'prerelease' + } + for (var key in v1) { + if (key === 'major' || key === 'minor' || key === 'patch') { + if (v1[key] !== v2[key]) { + return prefix + key + } + } + } + return defaultResult // may be undefined + } +} -const NEGATIVE_REPLACERS = [ - ...DEFAULT_REPLACER_PREFIX, +exports.compareIdentifiers = compareIdentifiers - // #24, #38 - // The MISSING rule of [gitignore docs](https://git-scm.com/docs/gitignore) - // A negative pattern without a trailing wildcard should not - // re-include the things inside that directory. +var numeric = /^[0-9]+$/ +function compareIdentifiers (a, b) { + var anum = numeric.test(a) + var bnum = numeric.test(b) - // eg: - // ['node_modules/*', '!node_modules'] - // should ignore `node_modules/a.js` - [ - /(?:[^*])$/, - match => `${match}(?=$|\\/$)` - ], + if (anum && bnum) { + a = +a + b = +b + } - ...DEFAULT_REPLACER_SUFFIX -] + return a === b ? 0 + : (anum && !bnum) ? -1 + : (bnum && !anum) ? 1 + : a < b ? -1 + : 1 +} -// A simple cache, because an ignore rule only has only one certain meaning -const cache = Object.create(null) +exports.rcompareIdentifiers = rcompareIdentifiers +function rcompareIdentifiers (a, b) { + return compareIdentifiers(b, a) +} -// @param {pattern} -const make_regex = (pattern, negative, ignorecase) => { - const r = cache[pattern] - if (r) { - return r - } +exports.major = major +function major (a, loose) { + return new SemVer(a, loose).major +} - const replacers = negative - ? NEGATIVE_REPLACERS - : POSITIVE_REPLACERS +exports.minor = minor +function minor (a, loose) { + return new SemVer(a, loose).minor +} - const source = replacers.reduce( - (prev, current) => prev.replace(current[0], current[1].bind(pattern)), - pattern - ) +exports.patch = patch +function patch (a, loose) { + return new SemVer(a, loose).patch +} - return cache[pattern] = ignorecase - ? new RegExp(source, 'i') - : new RegExp(source) +exports.compare = compare +function compare (a, b, loose) { + return new SemVer(a, loose).compare(new SemVer(b, loose)) } -// > A blank line matches no files, so it can serve as a separator for readability. -const checkPattern = pattern => pattern - && typeof pattern === 'string' - && !REGEX_BLANK_LINE.test(pattern) +exports.compareLoose = compareLoose +function compareLoose (a, b) { + return compare(a, b, true) +} - // > A line starting with # serves as a comment. - && pattern.indexOf('#') !== 0 +exports.compareBuild = compareBuild +function compareBuild (a, b, loose) { + var versionA = new SemVer(a, loose) + var versionB = new SemVer(b, loose) + return versionA.compare(versionB) || versionA.compareBuild(versionB) +} -const createRule = (pattern, ignorecase) => { - const origin = pattern - let negative = false +exports.rcompare = rcompare +function rcompare (a, b, loose) { + return compare(b, a, loose) +} - // > An optional prefix "!" which negates the pattern; - if (pattern.indexOf('!') === 0) { - negative = true - pattern = pattern.substr(1) - } +exports.sort = sort +function sort (list, loose) { + return list.sort(function (a, b) { + return exports.compareBuild(a, b, loose) + }) +} - pattern = pattern - // > Put a backslash ("\") in front of the first "!" for patterns that - // > begin with a literal "!", for example, `"\!important!.txt"`. - .replace(REGEX_LEADING_EXCAPED_EXCLAMATION, '!') - // > Put a backslash ("\") in front of the first hash for patterns that - // > begin with a hash. - .replace(REGEX_LEADING_EXCAPED_HASH, '#') +exports.rsort = rsort +function rsort (list, loose) { + return list.sort(function (a, b) { + return exports.compareBuild(b, a, loose) + }) +} - const regex = make_regex(pattern, negative, ignorecase) +exports.gt = gt +function gt (a, b, loose) { + return compare(a, b, loose) > 0 +} - return { - origin, - pattern, - negative, - regex - } +exports.lt = lt +function lt (a, b, loose) { + return compare(a, b, loose) < 0 } -class IgnoreBase { - constructor ({ - ignorecase = true - } = {}) { - this._rules = [] - this._ignorecase = ignorecase - define(this, KEY_IGNORE, true) - this._initCache() - } +exports.eq = eq +function eq (a, b, loose) { + return compare(a, b, loose) === 0 +} - _initCache () { - this._cache = Object.create(null) - } +exports.neq = neq +function neq (a, b, loose) { + return compare(a, b, loose) !== 0 +} - // @param {Array.|string|Ignore} pattern - add (pattern) { - this._added = false +exports.gte = gte +function gte (a, b, loose) { + return compare(a, b, loose) >= 0 +} - if (typeof pattern === 'string') { - pattern = pattern.split(/\r?\n/g) - } +exports.lte = lte +function lte (a, b, loose) { + return compare(a, b, loose) <= 0 +} - make_array(pattern).forEach(this._addPattern, this) +exports.cmp = cmp +function cmp (a, op, b, loose) { + switch (op) { + case '===': + if (typeof a === 'object') + a = a.version + if (typeof b === 'object') + b = b.version + return a === b - // Some rules have just added to the ignore, - // making the behavior changed. - if (this._added) { - this._initCache() - } + case '!==': + if (typeof a === 'object') + a = a.version + if (typeof b === 'object') + b = b.version + return a !== b - return this - } + case '': + case '=': + case '==': + return eq(a, b, loose) - // legacy - addPattern (pattern) { - return this.add(pattern) - } + case '!=': + return neq(a, b, loose) - _addPattern (pattern) { - // #32 - if (pattern && pattern[KEY_IGNORE]) { - this._rules = this._rules.concat(pattern._rules) - this._added = true - return - } + case '>': + return gt(a, b, loose) - if (checkPattern(pattern)) { - const rule = createRule(pattern, this._ignorecase) - this._added = true - this._rules.push(rule) - } - } + case '>=': + return gte(a, b, loose) - filter (paths) { - return make_array(paths).filter(path => this._filter(path)) - } + case '<': + return lt(a, b, loose) - createFilter () { - return path => this._filter(path) - } + case '<=': + return lte(a, b, loose) - ignores (path) { - return !this._filter(path) + default: + throw new TypeError('Invalid operator: ' + op) } +} - // @returns `Boolean` true if the `path` is NOT ignored - _filter (path, slices) { - if (!path) { - return false - } - - if (path in this._cache) { - return this._cache[path] +exports.Comparator = Comparator +function Comparator (comp, options) { + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false } + } - if (!slices) { - // path/to/a.js - // ['path', 'to', 'a.js'] - slices = path.split(SLASH) + if (comp instanceof Comparator) { + if (comp.loose === !!options.loose) { + return comp + } else { + comp = comp.value } + } - slices.pop() + if (!(this instanceof Comparator)) { + return new Comparator(comp, options) + } - return this._cache[path] = slices.length - // > It is not possible to re-include a file if a parent directory of - // > that file is excluded. - // If the path contains a parent directory, check the parent first - ? this._filter(slices.join(SLASH) + SLASH, slices) - && this._test(path) + debug('comparator', comp, options) + this.options = options + this.loose = !!options.loose + this.parse(comp) - // Or only test the path - : this._test(path) + if (this.semver === ANY) { + this.value = '' + } else { + this.value = this.operator + this.semver.version } - // @returns {Boolean} true if a file is NOT ignored - _test (path) { - // Explicitly define variable type by setting matched to `0` - let matched = 0 + debug('comp', this) +} - this._rules.forEach(rule => { - // if matched = true, then we only test negative rules - // if matched = false, then we test non-negative rules - if (!(matched ^ rule.negative)) { - matched = rule.negative ^ rule.regex.test(path) - } - }) +var ANY = {} +Comparator.prototype.parse = function (comp) { + var r = this.options.loose ? re[t.COMPARATORLOOSE] : re[t.COMPARATOR] + var m = comp.match(r) - return !matched + if (!m) { + throw new TypeError('Invalid comparator: ' + comp) } -} - -// Windows -// -------------------------------------------------------------- -/* istanbul ignore if */ -if ( - // Detect `process` so that it can run in browsers. - typeof process !== 'undefined' - && ( - process.env && process.env.IGNORE_TEST_WIN32 - || process.platform === 'win32' - ) -) { - const filter = IgnoreBase.prototype._filter - /* eslint no-control-regex: "off" */ - const make_posix = str => /^\\\\\?\\/.test(str) - || /[^\x00-\x80]+/.test(str) - ? str - : str.replace(/\\/g, '/') + this.operator = m[1] !== undefined ? m[1] : '' + if (this.operator === '=') { + this.operator = '' + } - IgnoreBase.prototype._filter = function filterWin32 (path, slices) { - path = make_posix(path) - return filter.call(this, path, slices) + // if it literally is just '>' or '' then allow anything. + if (!m[2]) { + this.semver = ANY + } else { + this.semver = new SemVer(m[2], this.options.loose) } } -module.exports = options => new IgnoreBase(options) - - -/***/ }), -/* 751 */ -/***/ (function(module, exports, __webpack_require__) { +Comparator.prototype.toString = function () { + return this.value +} -"use strict"; +Comparator.prototype.test = function (version) { + debug('Comparator.test', version, this.options.loose) -module.exports = input => { - const isExtendedLengthPath = /^\\\\\?\\/.test(input); - const hasNonAscii = /[^\u0000-\u0080]+/.test(input); // eslint-disable-line no-control-regex + if (this.semver === ANY || version === ANY) { + return true + } - if (isExtendedLengthPath || hasNonAscii) { - return input; - } + if (typeof version === 'string') { + try { + version = new SemVer(version, this.options) + } catch (er) { + return false + } + } - return input.replace(/\\/g, '/'); -}; + return cmp(version, this.operator, this.semver, this.options) +} +Comparator.prototype.intersects = function (comp, options) { + if (!(comp instanceof Comparator)) { + throw new TypeError('a Comparator is required') + } -/***/ }), -/* 752 */ -/***/ (function(module, exports, __webpack_require__) { + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false + } + } -"use strict"; -/*! - * has-glob - * - * Copyright (c) 2015, Jon Schlinkert. - * Licensed under the MIT License. - */ + var rangeTmp + if (this.operator === '') { + if (this.value === '') { + return true + } + rangeTmp = new Range(comp.value, options) + return satisfies(this.value, rangeTmp, options) + } else if (comp.operator === '') { + if (comp.value === '') { + return true + } + rangeTmp = new Range(this.value, options) + return satisfies(comp.semver, rangeTmp, options) + } + var sameDirectionIncreasing = + (this.operator === '>=' || this.operator === '>') && + (comp.operator === '>=' || comp.operator === '>') + var sameDirectionDecreasing = + (this.operator === '<=' || this.operator === '<') && + (comp.operator === '<=' || comp.operator === '<') + var sameSemVer = this.semver.version === comp.semver.version + var differentDirectionsInclusive = + (this.operator === '>=' || this.operator === '<=') && + (comp.operator === '>=' || comp.operator === '<=') + var oppositeDirectionsLessThan = + cmp(this.semver, '<', comp.semver, options) && + ((this.operator === '>=' || this.operator === '>') && + (comp.operator === '<=' || comp.operator === '<')) + var oppositeDirectionsGreaterThan = + cmp(this.semver, '>', comp.semver, options) && + ((this.operator === '<=' || this.operator === '<') && + (comp.operator === '>=' || comp.operator === '>')) -var isGlob = __webpack_require__(753); + return sameDirectionIncreasing || sameDirectionDecreasing || + (sameSemVer && differentDirectionsInclusive) || + oppositeDirectionsLessThan || oppositeDirectionsGreaterThan +} -module.exports = function hasGlob(val) { - if (val == null) return false; - if (typeof val === 'string') { - return isGlob(val); - } - if (Array.isArray(val)) { - var len = val.length; - while (len--) { - if (isGlob(val[len])) { - return true; - } +exports.Range = Range +function Range (range, options) { + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false } } - return false; -}; - - -/***/ }), -/* 753 */ -/***/ (function(module, exports, __webpack_require__) { -/*! - * is-glob - * - * Copyright (c) 2014-2016, Jon Schlinkert. - * Licensed under the MIT License. - */ + if (range instanceof Range) { + if (range.loose === !!options.loose && + range.includePrerelease === !!options.includePrerelease) { + return range + } else { + return new Range(range.raw, options) + } + } -var isExtglob = __webpack_require__(267); + if (range instanceof Comparator) { + return new Range(range.value, options) + } -module.exports = function isGlob(str) { - if (typeof str !== 'string' || str === '') { - return false; + if (!(this instanceof Range)) { + return new Range(range, options) } - if (isExtglob(str)) return true; + this.options = options + this.loose = !!options.loose + this.includePrerelease = !!options.includePrerelease - var regex = /(\\).|([*?]|\[.*\]|\{.*\}|\(.*\|.*\)|^!)/; - var match; + // First, split based on boolean or || + this.raw = range + this.set = range.split(/\s*\|\|\s*/).map(function (range) { + return this.parseRange(range.trim()) + }, this).filter(function (c) { + // throw out any that are not relevant for whatever reason + return c.length + }) - while ((match = regex.exec(str))) { - if (match[2]) return true; - str = str.slice(match.index + match[0].length); + if (!this.set.length) { + throw new TypeError('Invalid SemVer Range: ' + range) } - return false; -}; + this.format() +} -/***/ }), -/* 754 */ -/***/ (function(module, exports, __webpack_require__) { +Range.prototype.format = function () { + this.range = this.set.map(function (comps) { + return comps.join(' ').trim() + }).join('||').trim() + return this.range +} -"use strict"; +Range.prototype.toString = function () { + return this.range +} -const path = __webpack_require__(4); -const {constants: fsConstants} = __webpack_require__(132); -const pEvent = __webpack_require__(755); -const CpFileError = __webpack_require__(758); -const fs = __webpack_require__(760); -const ProgressEmitter = __webpack_require__(763); +Range.prototype.parseRange = function (range) { + var loose = this.options.loose + range = range.trim() + // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4` + var hr = loose ? re[t.HYPHENRANGELOOSE] : re[t.HYPHENRANGE] + range = range.replace(hr, hyphenReplace) + debug('hyphen replace', range) + // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5` + range = range.replace(re[t.COMPARATORTRIM], comparatorTrimReplace) + debug('comparator trim', range, re[t.COMPARATORTRIM]) -const cpFileAsync = async (source, destination, options, progressEmitter) => { - let readError; - const stat = await fs.stat(source); - progressEmitter.size = stat.size; + // `~ 1.2.3` => `~1.2.3` + range = range.replace(re[t.TILDETRIM], tildeTrimReplace) - const read = await fs.createReadStream(source); - await fs.makeDir(path.dirname(destination)); - const write = fs.createWriteStream(destination, {flags: options.overwrite ? 'w' : 'wx'}); - read.on('data', () => { - progressEmitter.written = write.bytesWritten; - }); - read.once('error', error => { - readError = new CpFileError(`Cannot read from \`${source}\`: ${error.message}`, error); - write.end(); - }); + // `^ 1.2.3` => `^1.2.3` + range = range.replace(re[t.CARETTRIM], caretTrimReplace) - let updateStats = false; - try { - const writePromise = pEvent(write, 'close'); - read.pipe(write); - await writePromise; - progressEmitter.written = progressEmitter.size; - updateStats = true; - } catch (error) { - if (options.overwrite || error.code !== 'EEXIST') { - throw new CpFileError(`Cannot write to \`${destination}\`: ${error.message}`, error); - } - } + // normalize spaces + range = range.split(/\s+/).join(' ') - if (readError) { - throw readError; - } + // At this point, the range is completely trimmed and + // ready to be split into comparators. - if (updateStats) { - const stats = await fs.lstat(source); + var compRe = loose ? re[t.COMPARATORLOOSE] : re[t.COMPARATOR] + var set = range.split(' ').map(function (comp) { + return parseComparator(comp, this.options) + }, this).join(' ').split(/\s+/) + if (this.options.loose) { + // in loose mode, throw out any that are not valid comparators + set = set.filter(function (comp) { + return !!comp.match(compRe) + }) + } + set = set.map(function (comp) { + return new Comparator(comp, this.options) + }, this) - return Promise.all([ - fs.utimes(destination, stats.atime, stats.mtime), - fs.chmod(destination, stats.mode), - fs.chown(destination, stats.uid, stats.gid) - ]); - } -}; + return set +} -const cpFile = (source, destination, options) => { - if (!source || !destination) { - return Promise.reject(new CpFileError('`source` and `destination` required')); - } +Range.prototype.intersects = function (range, options) { + if (!(range instanceof Range)) { + throw new TypeError('a Range is required') + } - options = { - overwrite: true, - ...options - }; + return this.set.some(function (thisComparators) { + return ( + isSatisfiable(thisComparators, options) && + range.set.some(function (rangeComparators) { + return ( + isSatisfiable(rangeComparators, options) && + thisComparators.every(function (thisComparator) { + return rangeComparators.every(function (rangeComparator) { + return thisComparator.intersects(rangeComparator, options) + }) + }) + ) + }) + ) + }) +} - const progressEmitter = new ProgressEmitter(path.resolve(source), path.resolve(destination)); - const promise = cpFileAsync(source, destination, options, progressEmitter); - promise.on = (...args) => { - progressEmitter.on(...args); - return promise; - }; +// take a set of comparators and determine whether there +// exists a version which can satisfy it +function isSatisfiable (comparators, options) { + var result = true + var remainingComparators = comparators.slice() + var testComparator = remainingComparators.pop() - return promise; -}; + while (result && remainingComparators.length) { + result = remainingComparators.every(function (otherComparator) { + return testComparator.intersects(otherComparator, options) + }) -module.exports = cpFile; + testComparator = remainingComparators.pop() + } -const checkSourceIsFile = (stat, source) => { - if (stat.isDirectory()) { - throw Object.assign(new CpFileError(`EISDIR: illegal operation on a directory '${source}'`), { - errno: -21, - code: 'EISDIR', - source - }); - } -}; + return result +} -const fixupAttributes = (destination, stat) => { - fs.chmodSync(destination, stat.mode); - fs.chownSync(destination, stat.uid, stat.gid); -}; +// Mostly just for testing and legacy API reasons +exports.toComparators = toComparators +function toComparators (range, options) { + return new Range(range, options).set.map(function (comp) { + return comp.map(function (c) { + return c.value + }).join(' ').trim().split(' ') + }) +} -module.exports.sync = (source, destination, options) => { - if (!source || !destination) { - throw new CpFileError('`source` and `destination` required'); - } +// comprised of xranges, tildes, stars, and gtlt's at this point. +// already replaced the hyphen ranges +// turn into a set of JUST comparators. +function parseComparator (comp, options) { + debug('comp', comp, options) + comp = replaceCarets(comp, options) + debug('caret', comp) + comp = replaceTildes(comp, options) + debug('tildes', comp) + comp = replaceXRanges(comp, options) + debug('xrange', comp) + comp = replaceStars(comp, options) + debug('stars', comp) + return comp +} - options = { - overwrite: true, - ...options - }; +function isX (id) { + return !id || id.toLowerCase() === 'x' || id === '*' +} - const stat = fs.statSync(source); - checkSourceIsFile(stat, source); - fs.makeDirSync(path.dirname(destination)); +// ~, ~> --> * (any, kinda silly) +// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0 +// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0 +// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0 +// ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0 +// ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0 +function replaceTildes (comp, options) { + return comp.trim().split(/\s+/).map(function (comp) { + return replaceTilde(comp, options) + }).join(' ') +} - const flags = options.overwrite ? null : fsConstants.COPYFILE_EXCL; - try { - fs.copyFileSync(source, destination, flags); - } catch (error) { - if (!options.overwrite && error.code === 'EEXIST') { - return; - } +function replaceTilde (comp, options) { + var r = options.loose ? re[t.TILDELOOSE] : re[t.TILDE] + return comp.replace(r, function (_, M, m, p, pr) { + debug('tilde', comp, _, M, m, p, pr) + var ret + + if (isX(M)) { + ret = '' + } else if (isX(m)) { + ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' + } else if (isX(p)) { + // ~1.2 == >=1.2.0 <1.3.0 + ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' + } else if (pr) { + debug('replaceTilde pr', pr) + ret = '>=' + M + '.' + m + '.' + p + '-' + pr + + ' <' + M + '.' + (+m + 1) + '.0' + } else { + // ~1.2.3 == >=1.2.3 <1.3.0 + ret = '>=' + M + '.' + m + '.' + p + + ' <' + M + '.' + (+m + 1) + '.0' + } - throw error; - } + debug('tilde return', ret) + return ret + }) +} - fs.utimesSync(destination, stat.atime, stat.mtime); - fixupAttributes(destination, stat); -}; +// ^ --> * (any, kinda silly) +// ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0 +// ^2.0, ^2.0.x --> >=2.0.0 <3.0.0 +// ^1.2, ^1.2.x --> >=1.2.0 <2.0.0 +// ^1.2.3 --> >=1.2.3 <2.0.0 +// ^1.2.0 --> >=1.2.0 <2.0.0 +function replaceCarets (comp, options) { + return comp.trim().split(/\s+/).map(function (comp) { + return replaceCaret(comp, options) + }).join(' ') +} +function replaceCaret (comp, options) { + debug('caret', comp, options) + var r = options.loose ? re[t.CARETLOOSE] : re[t.CARET] + return comp.replace(r, function (_, M, m, p, pr) { + debug('caret', comp, _, M, m, p, pr) + var ret -/***/ }), -/* 755 */ -/***/ (function(module, exports, __webpack_require__) { + if (isX(M)) { + ret = '' + } else if (isX(m)) { + ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' + } else if (isX(p)) { + if (M === '0') { + ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' + } else { + ret = '>=' + M + '.' + m + '.0 <' + (+M + 1) + '.0.0' + } + } else if (pr) { + debug('replaceCaret pr', pr) + if (M === '0') { + if (m === '0') { + ret = '>=' + M + '.' + m + '.' + p + '-' + pr + + ' <' + M + '.' + m + '.' + (+p + 1) + } else { + ret = '>=' + M + '.' + m + '.' + p + '-' + pr + + ' <' + M + '.' + (+m + 1) + '.0' + } + } else { + ret = '>=' + M + '.' + m + '.' + p + '-' + pr + + ' <' + (+M + 1) + '.0.0' + } + } else { + debug('no pr') + if (M === '0') { + if (m === '0') { + ret = '>=' + M + '.' + m + '.' + p + + ' <' + M + '.' + m + '.' + (+p + 1) + } else { + ret = '>=' + M + '.' + m + '.' + p + + ' <' + M + '.' + (+m + 1) + '.0' + } + } else { + ret = '>=' + M + '.' + m + '.' + p + + ' <' + (+M + 1) + '.0.0' + } + } -"use strict"; + debug('caret return', ret) + return ret + }) +} -const pTimeout = __webpack_require__(756); +function replaceXRanges (comp, options) { + debug('replaceXRanges', comp, options) + return comp.split(/\s+/).map(function (comp) { + return replaceXRange(comp, options) + }).join(' ') +} -const symbolAsyncIterator = Symbol.asyncIterator || '@@asyncIterator'; +function replaceXRange (comp, options) { + comp = comp.trim() + var r = options.loose ? re[t.XRANGELOOSE] : re[t.XRANGE] + return comp.replace(r, function (ret, gtlt, M, m, p, pr) { + debug('xRange', comp, ret, gtlt, M, m, p, pr) + var xM = isX(M) + var xm = xM || isX(m) + var xp = xm || isX(p) + var anyX = xp -const normalizeEmitter = emitter => { - const addListener = emitter.on || emitter.addListener || emitter.addEventListener; - const removeListener = emitter.off || emitter.removeListener || emitter.removeEventListener; + if (gtlt === '=' && anyX) { + gtlt = '' + } - if (!addListener || !removeListener) { - throw new TypeError('Emitter is not compatible'); - } + // if we're including prereleases in the match, then we need + // to fix this to -0, the lowest possible prerelease value + pr = options.includePrerelease ? '-0' : '' - return { - addListener: addListener.bind(emitter), - removeListener: removeListener.bind(emitter) - }; -}; + if (xM) { + if (gtlt === '>' || gtlt === '<') { + // nothing is allowed + ret = '<0.0.0-0' + } else { + // nothing is forbidden + ret = '*' + } + } else if (gtlt && anyX) { + // we know patch is an x, because we have any x at all. + // replace X with 0 + if (xm) { + m = 0 + } + p = 0 -const normalizeEvents = event => Array.isArray(event) ? event : [event]; + if (gtlt === '>') { + // >1 => >=2.0.0 + // >1.2 => >=1.3.0 + // >1.2.3 => >= 1.2.4 + gtlt = '>=' + if (xm) { + M = +M + 1 + m = 0 + p = 0 + } else { + m = +m + 1 + p = 0 + } + } else if (gtlt === '<=') { + // <=0.7.x is actually <0.8.0, since any 0.7.x should + // pass. Similarly, <=7.x is actually <8.0.0, etc. + gtlt = '<' + if (xm) { + M = +M + 1 + } else { + m = +m + 1 + } + } -const multiple = (emitter, event, options) => { - let cancel; - const ret = new Promise((resolve, reject) => { - options = { - rejectionEvents: ['error'], - multiArgs: false, - resolveImmediately: false, - ...options - }; + ret = gtlt + M + '.' + m + '.' + p + pr + } else if (xm) { + ret = '>=' + M + '.0.0' + pr + ' <' + (+M + 1) + '.0.0' + pr + } else if (xp) { + ret = '>=' + M + '.' + m + '.0' + pr + + ' <' + M + '.' + (+m + 1) + '.0' + pr + } - if (!(options.count >= 0 && (options.count === Infinity || Number.isInteger(options.count)))) { - throw new TypeError('The `count` option should be at least 0 or more'); - } + debug('xRange return', ret) - // Allow multiple events - const events = normalizeEvents(event); + return ret + }) +} - const items = []; - const {addListener, removeListener} = normalizeEmitter(emitter); +// Because * is AND-ed with everything else in the comparator, +// and '' means "any version", just remove the *s entirely. +function replaceStars (comp, options) { + debug('replaceStars', comp, options) + // Looseness is ignored here. star is always as loose as it gets! + return comp.trim().replace(re[t.STAR], '') +} - const onItem = (...args) => { - const value = options.multiArgs ? args : args[0]; +// This function is passed to string.replace(re[t.HYPHENRANGE]) +// M, m, patch, prerelease, build +// 1.2 - 3.4.5 => >=1.2.0 <=3.4.5 +// 1.2.3 - 3.4 => >=1.2.0 <3.5.0 Any 3.4.x will do +// 1.2 - 3.4 => >=1.2.0 <3.5.0 +function hyphenReplace ($0, + from, fM, fm, fp, fpr, fb, + to, tM, tm, tp, tpr, tb) { + if (isX(fM)) { + from = '' + } else if (isX(fm)) { + from = '>=' + fM + '.0.0' + } else if (isX(fp)) { + from = '>=' + fM + '.' + fm + '.0' + } else { + from = '>=' + from + } - if (options.filter && !options.filter(value)) { - return; - } + if (isX(tM)) { + to = '' + } else if (isX(tm)) { + to = '<' + (+tM + 1) + '.0.0' + } else if (isX(tp)) { + to = '<' + tM + '.' + (+tm + 1) + '.0' + } else if (tpr) { + to = '<=' + tM + '.' + tm + '.' + tp + '-' + tpr + } else { + to = '<=' + to + } - items.push(value); + return (from + ' ' + to).trim() +} - if (options.count === items.length) { - cancel(); - resolve(items); - } - }; +// if ANY of the sets match ALL of its comparators, then pass +Range.prototype.test = function (version) { + if (!version) { + return false + } - const rejectHandler = error => { - cancel(); - reject(error); - }; + if (typeof version === 'string') { + try { + version = new SemVer(version, this.options) + } catch (er) { + return false + } + } - cancel = () => { - for (const event of events) { - removeListener(event, onItem); - } + for (var i = 0; i < this.set.length; i++) { + if (testSet(this.set[i], version, this.options)) { + return true + } + } + return false +} - for (const rejectionEvent of options.rejectionEvents) { - removeListener(rejectionEvent, rejectHandler); - } - }; +function testSet (set, version, options) { + for (var i = 0; i < set.length; i++) { + if (!set[i].test(version)) { + return false + } + } - for (const event of events) { - addListener(event, onItem); - } + if (version.prerelease.length && !options.includePrerelease) { + // Find the set of versions that are allowed to have prereleases + // For example, ^1.2.3-pr.1 desugars to >=1.2.3-pr.1 <2.0.0 + // That should allow `1.2.3-pr.2` to pass. + // However, `1.2.4-alpha.notready` should NOT be allowed, + // even though it's within the range set by the comparators. + for (i = 0; i < set.length; i++) { + debug(set[i].semver) + if (set[i].semver === ANY) { + continue + } - for (const rejectionEvent of options.rejectionEvents) { - addListener(rejectionEvent, rejectHandler); - } + if (set[i].semver.prerelease.length > 0) { + var allowed = set[i].semver + if (allowed.major === version.major && + allowed.minor === version.minor && + allowed.patch === version.patch) { + return true + } + } + } - if (options.resolveImmediately) { - resolve(items); - } - }); + // Version has a -pre, but it's not one of the ones we like. + return false + } - ret.cancel = cancel; + return true +} - if (typeof options.timeout === 'number') { - const timeout = pTimeout(ret, options.timeout); - timeout.cancel = cancel; - return timeout; - } +exports.satisfies = satisfies +function satisfies (version, range, options) { + try { + range = new Range(range, options) + } catch (er) { + return false + } + return range.test(version) +} - return ret; -}; +exports.maxSatisfying = maxSatisfying +function maxSatisfying (versions, range, options) { + var max = null + var maxSV = null + try { + var rangeObj = new Range(range, options) + } catch (er) { + return null + } + versions.forEach(function (v) { + if (rangeObj.test(v)) { + // satisfies(v, range, options) + if (!max || maxSV.compare(v) === -1) { + // compare(max, v, true) + max = v + maxSV = new SemVer(max, options) + } + } + }) + return max +} -const pEvent = (emitter, event, options) => { - if (typeof options === 'function') { - options = {filter: options}; - } +exports.minSatisfying = minSatisfying +function minSatisfying (versions, range, options) { + var min = null + var minSV = null + try { + var rangeObj = new Range(range, options) + } catch (er) { + return null + } + versions.forEach(function (v) { + if (rangeObj.test(v)) { + // satisfies(v, range, options) + if (!min || minSV.compare(v) === 1) { + // compare(min, v, true) + min = v + minSV = new SemVer(min, options) + } + } + }) + return min +} - options = { - ...options, - count: 1, - resolveImmediately: false - }; +exports.minVersion = minVersion +function minVersion (range, loose) { + range = new Range(range, loose) - const arrayPromise = multiple(emitter, event, options); - const promise = arrayPromise.then(array => array[0]); // eslint-disable-line promise/prefer-await-to-then - promise.cancel = arrayPromise.cancel; + var minver = new SemVer('0.0.0') + if (range.test(minver)) { + return minver + } - return promise; -}; + minver = new SemVer('0.0.0-0') + if (range.test(minver)) { + return minver + } -module.exports = pEvent; -// TODO: Remove this for the next major release -module.exports.default = pEvent; + minver = null + for (var i = 0; i < range.set.length; ++i) { + var comparators = range.set[i] -module.exports.multiple = multiple; + comparators.forEach(function (comparator) { + // Clone to avoid manipulating the comparator's semver object. + var compver = new SemVer(comparator.semver.version) + switch (comparator.operator) { + case '>': + if (compver.prerelease.length === 0) { + compver.patch++ + } else { + compver.prerelease.push(0) + } + compver.raw = compver.format() + /* fallthrough */ + case '': + case '>=': + if (!minver || gt(minver, compver)) { + minver = compver + } + break + case '<': + case '<=': + /* Ignore maximum versions */ + break + /* istanbul ignore next */ + default: + throw new Error('Unexpected operation: ' + comparator.operator) + } + }) + } -module.exports.iterator = (emitter, event, options) => { - if (typeof options === 'function') { - options = {filter: options}; - } + if (minver && range.test(minver)) { + return minver + } - // Allow multiple events - const events = normalizeEvents(event); + return null +} - options = { - rejectionEvents: ['error'], - resolutionEvents: [], - limit: Infinity, - multiArgs: false, - ...options - }; +exports.validRange = validRange +function validRange (range, options) { + try { + // Return '*' instead of '' so that truthiness works. + // This will throw if it's invalid anyway + return new Range(range, options).range || '*' + } catch (er) { + return null + } +} - const {limit} = options; - const isValidLimit = limit >= 0 && (limit === Infinity || Number.isInteger(limit)); - if (!isValidLimit) { - throw new TypeError('The `limit` option should be a non-negative integer or Infinity'); - } +// Determine if version is less than all the versions possible in the range +exports.ltr = ltr +function ltr (version, range, options) { + return outside(version, range, '<', options) +} - if (limit === 0) { - // Return an empty async iterator to avoid any further cost - return { - [Symbol.asyncIterator]() { - return this; - }, - async next() { - return { - done: true, - value: undefined - }; - } - }; - } +// Determine if version is greater than all the versions possible in the range. +exports.gtr = gtr +function gtr (version, range, options) { + return outside(version, range, '>', options) +} - const {addListener, removeListener} = normalizeEmitter(emitter); +exports.outside = outside +function outside (version, range, hilo, options) { + version = new SemVer(version, options) + range = new Range(range, options) - let isDone = false; - let error; - let hasPendingError = false; - const nextQueue = []; - const valueQueue = []; - let eventCount = 0; - let isLimitReached = false; + var gtfn, ltefn, ltfn, comp, ecomp + switch (hilo) { + case '>': + gtfn = gt + ltefn = lte + ltfn = lt + comp = '>' + ecomp = '>=' + break + case '<': + gtfn = lt + ltefn = gte + ltfn = gt + comp = '<' + ecomp = '<=' + break + default: + throw new TypeError('Must provide a hilo val of "<" or ">"') + } - const valueHandler = (...args) => { - eventCount++; - isLimitReached = eventCount === limit; + // If it satisifes the range it is not outside + if (satisfies(version, range, options)) { + return false + } + + // From now on, variable terms are as if we're in "gtr" mode. + // but note that everything is flipped for the "ltr" function. - const value = options.multiArgs ? args : args[0]; + for (var i = 0; i < range.set.length; ++i) { + var comparators = range.set[i] - if (nextQueue.length > 0) { - const {resolve} = nextQueue.shift(); + var high = null + var low = null - resolve({done: false, value}); + comparators.forEach(function (comparator) { + if (comparator.semver === ANY) { + comparator = new Comparator('>=0.0.0') + } + high = high || comparator + low = low || comparator + if (gtfn(comparator.semver, high.semver, options)) { + high = comparator + } else if (ltfn(comparator.semver, low.semver, options)) { + low = comparator + } + }) - if (isLimitReached) { - cancel(); - } + // If the edge version comparator has a operator then our version + // isn't outside it + if (high.operator === comp || high.operator === ecomp) { + return false + } - return; - } + // If the lowest version comparator has an operator and our version + // is less than it then it isn't higher than the range + if ((!low.operator || low.operator === comp) && + ltefn(version, low.semver)) { + return false + } else if (low.operator === ecomp && ltfn(version, low.semver)) { + return false + } + } + return true +} - valueQueue.push(value); +exports.prerelease = prerelease +function prerelease (version, options) { + var parsed = parse(version, options) + return (parsed && parsed.prerelease.length) ? parsed.prerelease : null +} - if (isLimitReached) { - cancel(); - } - }; +exports.intersects = intersects +function intersects (r1, r2, options) { + r1 = new Range(r1, options) + r2 = new Range(r2, options) + return r1.intersects(r2) +} - const cancel = () => { - isDone = true; - for (const event of events) { - removeListener(event, valueHandler); - } +exports.coerce = coerce +function coerce (version, options) { + if (version instanceof SemVer) { + return version + } - for (const rejectionEvent of options.rejectionEvents) { - removeListener(rejectionEvent, rejectHandler); - } + if (typeof version === 'number') { + version = String(version) + } - for (const resolutionEvent of options.resolutionEvents) { - removeListener(resolutionEvent, resolveHandler); - } + if (typeof version !== 'string') { + return null + } - while (nextQueue.length > 0) { - const {resolve} = nextQueue.shift(); - resolve({done: true, value: undefined}); - } - }; + options = options || {} - const rejectHandler = (...args) => { - error = options.multiArgs ? args : args[0]; + var match = null + if (!options.rtl) { + match = version.match(re[t.COERCE]) + } else { + // Find the right-most coercible string that does not share + // a terminus with a more left-ward coercible string. + // Eg, '1.2.3.4' wants to coerce '2.3.4', not '3.4' or '4' + // + // Walk through the string checking with a /g regexp + // Manually set the index so as to pick up overlapping matches. + // Stop when we get a match that ends at the string end, since no + // coercible string can be more right-ward without the same terminus. + var next + while ((next = re[t.COERCERTL].exec(version)) && + (!match || match.index + match[0].length !== version.length) + ) { + if (!match || + next.index + next[0].length !== match.index + match[0].length) { + match = next + } + re[t.COERCERTL].lastIndex = next.index + next[1].length + next[2].length + } + // leave it in a clean state + re[t.COERCERTL].lastIndex = -1 + } - if (nextQueue.length > 0) { - const {reject} = nextQueue.shift(); - reject(error); - } else { - hasPendingError = true; - } + if (match === null) { + return null + } - cancel(); - }; + return parse(match[2] + + '.' + (match[3] || '0') + + '.' + (match[4] || '0'), options) +} - const resolveHandler = (...args) => { - const value = options.multiArgs ? args : args[0]; - if (options.filter && !options.filter(value)) { - return; - } +/***/ }), +/* 769 */ +/***/ (function(module, exports, __webpack_require__) { - if (nextQueue.length > 0) { - const {resolve} = nextQueue.shift(); - resolve({done: true, value}); - } else { - valueQueue.push(value); - } +"use strict"; - cancel(); - }; +const EventEmitter = __webpack_require__(164); - for (const event of events) { - addListener(event, valueHandler); - } +const written = new WeakMap(); - for (const rejectionEvent of options.rejectionEvents) { - addListener(rejectionEvent, rejectHandler); +class ProgressEmitter extends EventEmitter { + constructor(source, destination) { + super(); + this._source = source; + this._destination = destination; } - for (const resolutionEvent of options.resolutionEvents) { - addListener(resolutionEvent, resolveHandler); + set written(value) { + written.set(this, value); + this.emitProgress(); } - return { - [symbolAsyncIterator]() { - return this; - }, - async next() { - if (valueQueue.length > 0) { - const value = valueQueue.shift(); - return { - done: isDone && valueQueue.length === 0 && !isLimitReached, - value - }; - } - - if (hasPendingError) { - hasPendingError = false; - throw error; - } + get written() { + return written.get(this); + } - if (isDone) { - return { - done: true, - value: undefined - }; - } + emitProgress() { + const {size, written} = this; + this.emit('progress', { + src: this._source, + dest: this._destination, + size, + written, + percent: written === size ? 1 : written / size + }); + } +} - return new Promise((resolve, reject) => nextQueue.push({resolve, reject})); - }, - async return(value) { - cancel(); - return { - done: isDone, - value - }; - } - }; -}; +module.exports = ProgressEmitter; /***/ }), -/* 756 */ +/* 770 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const pFinally = __webpack_require__(757); -class TimeoutError extends Error { - constructor(message) { - super(message); - this.name = 'TimeoutError'; - } -} +const blacklist = [ + // # All + '^npm-debug\\.log$', // Error log for npm + '^\\..*\\.swp$', // Swap file for vim state -module.exports = (promise, ms, fallback) => new Promise((resolve, reject) => { - if (typeof ms !== 'number' || ms < 0) { - throw new TypeError('Expected `ms` to be a positive number'); - } + // # macOS + '^\\.DS_Store$', // Stores custom folder attributes + '^\\.AppleDouble$', // Stores additional file resources + '^\\.LSOverride$', // Contains the absolute path to the app to be used + '^Icon\\r$', // Custom Finder icon: http://superuser.com/questions/298785/icon-file-on-os-x-desktop + '^\\._.*', // Thumbnail + '^\\.Spotlight-V100(?:$|\\/)', // Directory that might appear on external disk + '\\.Trashes', // File that might appear on external disk + '^__MACOSX$', // Resource fork - const timer = setTimeout(() => { - if (typeof fallback === 'function') { - try { - resolve(fallback()); - } catch (err) { - reject(err); - } - return; - } + // # Linux + '~$', // Backup file - const message = typeof fallback === 'string' ? fallback : `Promise timed out after ${ms} milliseconds`; - const err = fallback instanceof Error ? fallback : new TimeoutError(message); + // # Windows + '^Thumbs\\.db$', // Image file cache + '^ehthumbs\\.db$', // Folder config file + '^Desktop\\.ini$', // Stores custom folder attributes + '@eaDir$' // Synology Diskstation "hidden" folder where the server stores thumbnails +]; - if (typeof promise.cancel === 'function') { - promise.cancel(); - } +exports.re = () => { + throw new Error('`junk.re` was renamed to `junk.regex`'); +}; - reject(err); - }, ms); +exports.regex = new RegExp(blacklist.join('|')); - pFinally( - promise.then(resolve, reject), - () => { - clearTimeout(timer); - } - ); -}); +exports.is = filename => exports.regex.test(filename); -module.exports.TimeoutError = TimeoutError; +exports.not = filename => !exports.is(filename); + +// TODO: Remove this for the next major release +exports.default = module.exports; /***/ }), -/* 757 */ +/* 771 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -module.exports = (promise, onFinally) => { - onFinally = onFinally || (() => {}); +const pMap = __webpack_require__(772); - return promise.then( - val => new Promise(resolve => { - resolve(onFinally()); - }).then(() => val), - err => new Promise(resolve => { - resolve(onFinally()); - }).then(() => { - throw err; - }) +const pFilter = async (iterable, filterer, options) => { + const values = await pMap( + iterable, + (element, index) => Promise.all([filterer(element, index), element]), + options ); + return values.filter(value => Boolean(value[0])).map(value => value[1]); }; - -/***/ }), -/* 758 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -const NestedError = __webpack_require__(759); - -class CpFileError extends NestedError { - constructor(message, nested) { - super(message, nested); - Object.assign(this, nested); - this.name = 'CpFileError'; - } -} - -module.exports = CpFileError; +module.exports = pFilter; +// TODO: Remove this for the next major release +module.exports.default = pFilter; /***/ }), -/* 759 */ +/* 772 */ /***/ (function(module, exports, __webpack_require__) { -var inherits = __webpack_require__(113).inherits; - -var NestedError = function (message, nested) { - this.nested = nested; +"use strict"; - if (message instanceof Error) { - nested = message; - } else if (typeof message !== 'undefined') { - Object.defineProperty(this, 'message', { - value: message, - writable: true, - enumerable: false, - configurable: true - }); - } - Error.captureStackTrace(this, this.constructor); - var oldStackDescriptor = Object.getOwnPropertyDescriptor(this, 'stack'); - var stackDescriptor = buildStackDescriptor(oldStackDescriptor, nested); - Object.defineProperty(this, 'stack', stackDescriptor); -}; +const pMap = (iterable, mapper, options) => new Promise((resolve, reject) => { + options = Object.assign({ + concurrency: Infinity + }, options); -function buildStackDescriptor(oldStackDescriptor, nested) { - if (oldStackDescriptor.get) { - return { - get: function () { - var stack = oldStackDescriptor.get.call(this); - return buildCombinedStacks(stack, this.nested); - } - }; - } else { - var stack = oldStackDescriptor.value; - return { - value: buildCombinedStacks(stack, nested) - }; - } -} + if (typeof mapper !== 'function') { + throw new TypeError('Mapper function is required'); + } -function buildCombinedStacks(stack, nested) { - if (nested) { - stack += '\nCaused By: ' + nested.stack; - } - return stack; -} + const {concurrency} = options; -inherits(NestedError, Error); -NestedError.prototype.name = 'NestedError'; + if (!(typeof concurrency === 'number' && concurrency >= 1)) { + throw new TypeError(`Expected \`concurrency\` to be a number from 1 and up, got \`${concurrency}\` (${typeof concurrency})`); + } + const ret = []; + const iterator = iterable[Symbol.iterator](); + let isRejected = false; + let isIterableDone = false; + let resolvingCount = 0; + let currentIndex = 0; -module.exports = NestedError; + const next = () => { + if (isRejected) { + return; + } + const nextItem = iterator.next(); + const i = currentIndex; + currentIndex++; -/***/ }), -/* 760 */ -/***/ (function(module, exports, __webpack_require__) { + if (nextItem.done) { + isIterableDone = true; -"use strict"; + if (resolvingCount === 0) { + resolve(ret); + } -const {promisify} = __webpack_require__(113); -const fs = __webpack_require__(233); -const makeDir = __webpack_require__(761); -const pEvent = __webpack_require__(755); -const CpFileError = __webpack_require__(758); + return; + } -const stat = promisify(fs.stat); -const lstat = promisify(fs.lstat); -const utimes = promisify(fs.utimes); -const chmod = promisify(fs.chmod); -const chown = promisify(fs.chown); + resolvingCount++; -exports.closeSync = fs.closeSync.bind(fs); -exports.createWriteStream = fs.createWriteStream.bind(fs); + Promise.resolve(nextItem.value) + .then(element => mapper(element, i)) + .then( + value => { + ret[i] = value; + resolvingCount--; + next(); + }, + error => { + isRejected = true; + reject(error); + } + ); + }; -exports.createReadStream = async (path, options) => { - const read = fs.createReadStream(path, options); + for (let i = 0; i < concurrency; i++) { + next(); - try { - await pEvent(read, ['readable', 'end']); - } catch (error) { - throw new CpFileError(`Cannot read from \`${path}\`: ${error.message}`, error); + if (isIterableDone) { + break; + } } - - return read; -}; - -exports.stat = path => stat(path).catch(error => { - throw new CpFileError(`Cannot stat path \`${path}\`: ${error.message}`, error); -}); - -exports.lstat = path => lstat(path).catch(error => { - throw new CpFileError(`lstat \`${path}\` failed: ${error.message}`, error); -}); - -exports.utimes = (path, atime, mtime) => utimes(path, atime, mtime).catch(error => { - throw new CpFileError(`utimes \`${path}\` failed: ${error.message}`, error); -}); - -exports.chmod = (path, mode) => chmod(path, mode).catch(error => { - throw new CpFileError(`chmod \`${path}\` failed: ${error.message}`, error); -}); - -exports.chown = (path, uid, gid) => chown(path, uid, gid).catch(error => { - throw new CpFileError(`chown \`${path}\` failed: ${error.message}`, error); }); -exports.statSync = path => { - try { - return fs.statSync(path); - } catch (error) { - throw new CpFileError(`stat \`${path}\` failed: ${error.message}`, error); - } -}; +module.exports = pMap; +// TODO: Remove this for the next major release +module.exports.default = pMap; -exports.utimesSync = (path, atime, mtime) => { - try { - return fs.utimesSync(path, atime, mtime); - } catch (error) { - throw new CpFileError(`utimes \`${path}\` failed: ${error.message}`, error); - } -}; -exports.chmodSync = (path, mode) => { - try { - return fs.chmodSync(path, mode); - } catch (error) { - throw new CpFileError(`chmod \`${path}\` failed: ${error.message}`, error); - } -}; +/***/ }), +/* 773 */ +/***/ (function(module, exports, __webpack_require__) { -exports.chownSync = (path, uid, gid) => { - try { - return fs.chownSync(path, uid, gid); - } catch (error) { - throw new CpFileError(`chown \`${path}\` failed: ${error.message}`, error); - } -}; +"use strict"; -exports.makeDir = path => makeDir(path, {fs}).catch(error => { - throw new CpFileError(`Cannot create directory \`${path}\`: ${error.message}`, error); -}); +const NestedError = __webpack_require__(765); -exports.makeDirSync = path => { - try { - makeDir.sync(path, {fs}); - } catch (error) { - throw new CpFileError(`Cannot create directory \`${path}\`: ${error.message}`, error); +class CpyError extends NestedError { + constructor(message, nested) { + super(message, nested); + Object.assign(this, nested); + this.name = 'CpyError'; } -}; +} -exports.copyFileSync = (source, destination, flags) => { - try { - fs.copyFileSync(source, destination, flags); - } catch (error) { - throw new CpFileError(`Cannot copy from \`${source}\` to \`${destination}\`: ${error.message}`, error); - } -}; +module.exports = CpyError; /***/ }), -/* 761 */ +/* 774 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(132); -const path = __webpack_require__(4); -const {promisify} = __webpack_require__(113); -const semver = __webpack_require__(762); +const arrayUnion = __webpack_require__(242); +const merge2 = __webpack_require__(243); +const fastGlob = __webpack_require__(775); +const dirGlob = __webpack_require__(332); +const gitignore = __webpack_require__(806); +const {FilterStream, UniqueStream} = __webpack_require__(807); -const useNativeRecursiveOption = semver.satisfies(process.version, '>=10.12.0'); +const DEFAULT_FILTER = () => false; -// https://github.com/nodejs/node/issues/8987 -// https://github.com/libuv/libuv/pull/1088 -const checkPath = pth => { - if (process.platform === 'win32') { - const pathHasInvalidWinCharacters = /[<>:"|?*]/.test(pth.replace(path.parse(pth).root, '')); +const isNegative = pattern => pattern[0] === '!'; - if (pathHasInvalidWinCharacters) { - const error = new Error(`Path contains invalid characters: ${pth}`); - error.code = 'EINVAL'; - throw error; - } +const assertPatternsInput = patterns => { + if (!patterns.every(pattern => typeof pattern === 'string')) { + throw new TypeError('Patterns must be a string or an array of strings'); } }; -const processOptions = options => { - // https://github.com/sindresorhus/make-dir/issues/18 - const defaults = { - mode: 0o777, - fs - }; +const checkCwdOption = (options = {}) => { + if (!options.cwd) { + return; + } - return { - ...defaults, - ...options - }; -}; + let stat; + try { + stat = fs.statSync(options.cwd); + } catch { + return; + } -const permissionError = pth => { - // This replicates the exception of `fs.mkdir` with native the - // `recusive` option when run on an invalid drive under Windows. - const error = new Error(`operation not permitted, mkdir '${pth}'`); - error.code = 'EPERM'; - error.errno = -4048; - error.path = pth; - error.syscall = 'mkdir'; - return error; + if (!stat.isDirectory()) { + throw new Error('The `cwd` option must be a path to a directory'); + } }; -const makeDir = async (input, options) => { - checkPath(input); - options = processOptions(options); +const getPathString = p => p.stats instanceof fs.Stats ? p.path : p; - const mkdir = promisify(options.fs.mkdir); - const stat = promisify(options.fs.stat); +const generateGlobTasks = (patterns, taskOptions) => { + patterns = arrayUnion([].concat(patterns)); + assertPatternsInput(patterns); + checkCwdOption(taskOptions); - if (useNativeRecursiveOption && options.fs.mkdir === fs.mkdir) { - const pth = path.resolve(input); + const globTasks = []; - await mkdir(pth, { - mode: options.mode, - recursive: true - }); + taskOptions = { + ignore: [], + expandDirectories: true, + ...taskOptions + }; - return pth; + for (const [index, pattern] of patterns.entries()) { + if (isNegative(pattern)) { + continue; + } + + const ignore = patterns + .slice(index) + .filter(pattern => isNegative(pattern)) + .map(pattern => pattern.slice(1)); + + const options = { + ...taskOptions, + ignore: taskOptions.ignore.concat(ignore) + }; + + globTasks.push({pattern, options}); } - const make = async pth => { - try { - await mkdir(pth, options.mode); + return globTasks; +}; - return pth; - } catch (error) { - if (error.code === 'EPERM') { - throw error; - } +const globDirs = (task, fn) => { + let options = {}; + if (task.options.cwd) { + options.cwd = task.options.cwd; + } - if (error.code === 'ENOENT') { - if (path.dirname(pth) === pth) { - throw permissionError(pth); - } + if (Array.isArray(task.options.expandDirectories)) { + options = { + ...options, + files: task.options.expandDirectories + }; + } else if (typeof task.options.expandDirectories === 'object') { + options = { + ...options, + ...task.options.expandDirectories + }; + } - if (error.message.includes('null bytes')) { - throw error; - } + return fn(task.pattern, options); +}; - await make(path.dirname(pth)); +const getPattern = (task, fn) => task.options.expandDirectories ? globDirs(task, fn) : [task.pattern]; - return make(pth); - } +const getFilterSync = options => { + return options && options.gitignore ? + gitignore.sync({cwd: options.cwd, ignore: options.ignore}) : + DEFAULT_FILTER; +}; - try { - const stats = await stat(pth); - if (!stats.isDirectory()) { - throw new Error('The path is not a directory'); - } - } catch (_) { - throw error; - } +const globToTask = task => glob => { + const {options} = task; + if (options.ignore && Array.isArray(options.ignore) && options.expandDirectories) { + options.ignore = dirGlob.sync(options.ignore); + } - return pth; - } + return { + pattern: glob, + options }; - - return make(path.resolve(input)); }; -module.exports = makeDir; +module.exports = async (patterns, options) => { + const globTasks = generateGlobTasks(patterns, options); -module.exports.sync = (input, options) => { - checkPath(input); - options = processOptions(options); + const getFilter = async () => { + return options && options.gitignore ? + gitignore({cwd: options.cwd, ignore: options.ignore}) : + DEFAULT_FILTER; + }; - if (useNativeRecursiveOption && options.fs.mkdirSync === fs.mkdirSync) { - const pth = path.resolve(input); + const getTasks = async () => { + const tasks = await Promise.all(globTasks.map(async task => { + const globs = await getPattern(task, dirGlob); + return Promise.all(globs.map(globToTask(task))); + })); - fs.mkdirSync(pth, { - mode: options.mode, - recursive: true - }); + return arrayUnion(...tasks); + }; - return pth; + const [filter, tasks] = await Promise.all([getFilter(), getTasks()]); + const paths = await Promise.all(tasks.map(task => fastGlob(task.pattern, task.options))); + + return arrayUnion(...paths).filter(path_ => !filter(getPathString(path_))); +}; + +module.exports.sync = (patterns, options) => { + const globTasks = generateGlobTasks(patterns, options); + + const tasks = []; + for (const task of globTasks) { + const newTask = getPattern(task, dirGlob.sync).map(globToTask(task)); + tasks.push(...newTask); } - const make = pth => { - try { - options.fs.mkdirSync(pth, options.mode); - } catch (error) { - if (error.code === 'EPERM') { - throw error; - } + const filter = getFilterSync(options); - if (error.code === 'ENOENT') { - if (path.dirname(pth) === pth) { - throw permissionError(pth); - } + let matches = []; + for (const task of tasks) { + matches = arrayUnion(matches, fastGlob.sync(task.pattern, task.options)); + } - if (error.message.includes('null bytes')) { - throw error; - } + return matches.filter(path_ => !filter(path_)); +}; - make(path.dirname(pth)); - return make(pth); - } +module.exports.stream = (patterns, options) => { + const globTasks = generateGlobTasks(patterns, options); - try { - if (!options.fs.statSync(pth).isDirectory()) { - throw new Error('The path is not a directory'); - } - } catch (_) { - throw error; - } - } + const tasks = []; + for (const task of globTasks) { + const newTask = getPattern(task, dirGlob.sync).map(globToTask(task)); + tasks.push(...newTask); + } - return pth; - }; + const filter = getFilterSync(options); + const filterStream = new FilterStream(p => !filter(p)); + const uniqueStream = new UniqueStream(); - return make(path.resolve(input)); + return merge2(tasks.map(task => fastGlob.stream(task.pattern, task.options))) + .pipe(filterStream) + .pipe(uniqueStream); }; +module.exports.generateGlobTasks = generateGlobTasks; + +module.exports.hasMagic = (patterns, options) => [] + .concat(patterns) + .some(pattern => fastGlob.isDynamicPattern(pattern, options)); + +module.exports.gitignore = gitignore; + /***/ }), -/* 762 */ -/***/ (function(module, exports) { +/* 775 */ +/***/ (function(module, exports, __webpack_require__) { -exports = module.exports = SemVer +"use strict"; + +const taskManager = __webpack_require__(776); +const async_1 = __webpack_require__(792); +const stream_1 = __webpack_require__(802); +const sync_1 = __webpack_require__(803); +const settings_1 = __webpack_require__(805); +const utils = __webpack_require__(777); +async function FastGlob(source, options) { + assertPatternsInput(source); + const works = getWorks(source, async_1.default, options); + const result = await Promise.all(works); + return utils.array.flatten(result); +} +// https://github.com/typescript-eslint/typescript-eslint/issues/60 +// eslint-disable-next-line no-redeclare +(function (FastGlob) { + function sync(source, options) { + assertPatternsInput(source); + const works = getWorks(source, sync_1.default, options); + return utils.array.flatten(works); + } + FastGlob.sync = sync; + function stream(source, options) { + assertPatternsInput(source); + const works = getWorks(source, stream_1.default, options); + /** + * The stream returned by the provider cannot work with an asynchronous iterator. + * To support asynchronous iterators, regardless of the number of tasks, we always multiplex streams. + * This affects performance (+25%). I don't see best solution right now. + */ + return utils.stream.merge(works); + } + FastGlob.stream = stream; + function generateTasks(source, options) { + assertPatternsInput(source); + const patterns = [].concat(source); + const settings = new settings_1.default(options); + return taskManager.generate(patterns, settings); + } + FastGlob.generateTasks = generateTasks; + function isDynamicPattern(source, options) { + assertPatternsInput(source); + const settings = new settings_1.default(options); + return utils.pattern.isDynamicPattern(source, settings); + } + FastGlob.isDynamicPattern = isDynamicPattern; + function escapePath(source) { + assertPatternsInput(source); + return utils.path.escape(source); + } + FastGlob.escapePath = escapePath; +})(FastGlob || (FastGlob = {})); +function getWorks(source, _Provider, options) { + const patterns = [].concat(source); + const settings = new settings_1.default(options); + const tasks = taskManager.generate(patterns, settings); + const provider = new _Provider(settings); + return tasks.map(provider.read, provider); +} +function assertPatternsInput(input) { + const source = [].concat(input); + const isValidSource = source.every((item) => utils.string.isString(item) && !utils.string.isEmpty(item)); + if (!isValidSource) { + throw new TypeError('Patterns must be a string (non empty) or an array of strings'); + } +} +module.exports = FastGlob; -var debug -/* istanbul ignore next */ -if (typeof process === 'object' && - process.env && - process.env.NODE_DEBUG && - /\bsemver\b/i.test(process.env.NODE_DEBUG)) { - debug = function () { - var args = Array.prototype.slice.call(arguments, 0) - args.unshift('SEMVER') - console.log.apply(console, args) - } -} else { - debug = function () {} -} -// Note: this is the semver.org version of the spec that it implements -// Not necessarily the package version of this code. -exports.SEMVER_SPEC_VERSION = '2.0.0' +/***/ }), +/* 776 */ +/***/ (function(module, exports, __webpack_require__) { -var MAX_LENGTH = 256 -var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || - /* istanbul ignore next */ 9007199254740991 +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.convertPatternGroupToTask = exports.convertPatternGroupsToTasks = exports.groupPatternsByBaseDirectory = exports.getNegativePatternsAsPositive = exports.getPositivePatterns = exports.convertPatternsToTasks = exports.generate = void 0; +const utils = __webpack_require__(777); +function generate(patterns, settings) { + const positivePatterns = getPositivePatterns(patterns); + const negativePatterns = getNegativePatternsAsPositive(patterns, settings.ignore); + const staticPatterns = positivePatterns.filter((pattern) => utils.pattern.isStaticPattern(pattern, settings)); + const dynamicPatterns = positivePatterns.filter((pattern) => utils.pattern.isDynamicPattern(pattern, settings)); + const staticTasks = convertPatternsToTasks(staticPatterns, negativePatterns, /* dynamic */ false); + const dynamicTasks = convertPatternsToTasks(dynamicPatterns, negativePatterns, /* dynamic */ true); + return staticTasks.concat(dynamicTasks); +} +exports.generate = generate; +function convertPatternsToTasks(positive, negative, dynamic) { + const positivePatternsGroup = groupPatternsByBaseDirectory(positive); + // When we have a global group – there is no reason to divide the patterns into independent tasks. + // In this case, the global task covers the rest. + if ('.' in positivePatternsGroup) { + const task = convertPatternGroupToTask('.', positive, negative, dynamic); + return [task]; + } + return convertPatternGroupsToTasks(positivePatternsGroup, negative, dynamic); +} +exports.convertPatternsToTasks = convertPatternsToTasks; +function getPositivePatterns(patterns) { + return utils.pattern.getPositivePatterns(patterns); +} +exports.getPositivePatterns = getPositivePatterns; +function getNegativePatternsAsPositive(patterns, ignore) { + const negative = utils.pattern.getNegativePatterns(patterns).concat(ignore); + const positive = negative.map(utils.pattern.convertToPositivePattern); + return positive; +} +exports.getNegativePatternsAsPositive = getNegativePatternsAsPositive; +function groupPatternsByBaseDirectory(patterns) { + const group = {}; + return patterns.reduce((collection, pattern) => { + const base = utils.pattern.getBaseDirectory(pattern); + if (base in collection) { + collection[base].push(pattern); + } + else { + collection[base] = [pattern]; + } + return collection; + }, group); +} +exports.groupPatternsByBaseDirectory = groupPatternsByBaseDirectory; +function convertPatternGroupsToTasks(positive, negative, dynamic) { + return Object.keys(positive).map((base) => { + return convertPatternGroupToTask(base, positive[base], negative, dynamic); + }); +} +exports.convertPatternGroupsToTasks = convertPatternGroupsToTasks; +function convertPatternGroupToTask(base, positive, negative, dynamic) { + return { + dynamic, + positive, + negative, + base, + patterns: [].concat(positive, negative.map(utils.pattern.convertToNegativePattern)) + }; +} +exports.convertPatternGroupToTask = convertPatternGroupToTask; -// Max safe segment length for coercion. -var MAX_SAFE_COMPONENT_LENGTH = 16 -// The actual regexps go on exports.re -var re = exports.re = [] -var src = exports.src = [] -var t = exports.tokens = {} -var R = 0 +/***/ }), +/* 777 */ +/***/ (function(module, exports, __webpack_require__) { -function tok (n) { - t[n] = R++ -} +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.string = exports.stream = exports.pattern = exports.path = exports.fs = exports.errno = exports.array = void 0; +const array = __webpack_require__(778); +exports.array = array; +const errno = __webpack_require__(779); +exports.errno = errno; +const fs = __webpack_require__(780); +exports.fs = fs; +const path = __webpack_require__(781); +exports.path = path; +const pattern = __webpack_require__(782); +exports.pattern = pattern; +const stream = __webpack_require__(790); +exports.stream = stream; +const string = __webpack_require__(791); +exports.string = string; -// The following Regular Expressions can be used for tokenizing, -// validating, and parsing SemVer version strings. -// ## Numeric Identifier -// A single `0`, or a non-zero digit followed by zero or more digits. +/***/ }), +/* 778 */ +/***/ (function(module, exports, __webpack_require__) { -tok('NUMERICIDENTIFIER') -src[t.NUMERICIDENTIFIER] = '0|[1-9]\\d*' -tok('NUMERICIDENTIFIERLOOSE') -src[t.NUMERICIDENTIFIERLOOSE] = '[0-9]+' +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.splitWhen = exports.flatten = void 0; +function flatten(items) { + return items.reduce((collection, item) => [].concat(collection, item), []); +} +exports.flatten = flatten; +function splitWhen(items, predicate) { + const result = [[]]; + let groupIndex = 0; + for (const item of items) { + if (predicate(item)) { + groupIndex++; + result[groupIndex] = []; + } + else { + result[groupIndex].push(item); + } + } + return result; +} +exports.splitWhen = splitWhen; -// ## Non-numeric Identifier -// Zero or more digits, followed by a letter or hyphen, and then zero or -// more letters, digits, or hyphens. -tok('NONNUMERICIDENTIFIER') -src[t.NONNUMERICIDENTIFIER] = '\\d*[a-zA-Z-][a-zA-Z0-9-]*' +/***/ }), +/* 779 */ +/***/ (function(module, exports, __webpack_require__) { -// ## Main Version -// Three dot-separated numeric identifiers. +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.isEnoentCodeError = void 0; +function isEnoentCodeError(error) { + return error.code === 'ENOENT'; +} +exports.isEnoentCodeError = isEnoentCodeError; + + +/***/ }), +/* 780 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.createDirentFromStats = void 0; +class DirentFromStats { + constructor(name, stats) { + this.name = name; + this.isBlockDevice = stats.isBlockDevice.bind(stats); + this.isCharacterDevice = stats.isCharacterDevice.bind(stats); + this.isDirectory = stats.isDirectory.bind(stats); + this.isFIFO = stats.isFIFO.bind(stats); + this.isFile = stats.isFile.bind(stats); + this.isSocket = stats.isSocket.bind(stats); + this.isSymbolicLink = stats.isSymbolicLink.bind(stats); + } +} +function createDirentFromStats(name, stats) { + return new DirentFromStats(name, stats); +} +exports.createDirentFromStats = createDirentFromStats; + + +/***/ }), +/* 781 */ +/***/ (function(module, exports, __webpack_require__) { -tok('MAINVERSION') -src[t.MAINVERSION] = '(' + src[t.NUMERICIDENTIFIER] + ')\\.' + - '(' + src[t.NUMERICIDENTIFIER] + ')\\.' + - '(' + src[t.NUMERICIDENTIFIER] + ')' +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.removeLeadingDotSegment = exports.escape = exports.makeAbsolute = exports.unixify = void 0; +const path = __webpack_require__(4); +const LEADING_DOT_SEGMENT_CHARACTERS_COUNT = 2; // ./ or .\\ +const UNESCAPED_GLOB_SYMBOLS_RE = /(\\?)([()*?[\]{|}]|^!|[!+@](?=\())/g; +/** + * Designed to work only with simple paths: `dir\\file`. + */ +function unixify(filepath) { + return filepath.replace(/\\/g, '/'); +} +exports.unixify = unixify; +function makeAbsolute(cwd, filepath) { + return path.resolve(cwd, filepath); +} +exports.makeAbsolute = makeAbsolute; +function escape(pattern) { + return pattern.replace(UNESCAPED_GLOB_SYMBOLS_RE, '\\$2'); +} +exports.escape = escape; +function removeLeadingDotSegment(entry) { + // We do not use `startsWith` because this is 10x slower than current implementation for some cases. + // eslint-disable-next-line @typescript-eslint/prefer-string-starts-ends-with + if (entry.charAt(0) === '.') { + const secondCharactery = entry.charAt(1); + if (secondCharactery === '/' || secondCharactery === '\\') { + return entry.slice(LEADING_DOT_SEGMENT_CHARACTERS_COUNT); + } + } + return entry; +} +exports.removeLeadingDotSegment = removeLeadingDotSegment; -tok('MAINVERSIONLOOSE') -src[t.MAINVERSIONLOOSE] = '(' + src[t.NUMERICIDENTIFIERLOOSE] + ')\\.' + - '(' + src[t.NUMERICIDENTIFIERLOOSE] + ')\\.' + - '(' + src[t.NUMERICIDENTIFIERLOOSE] + ')' -// ## Pre-release Version Identifier -// A numeric identifier, or a non-numeric identifier. +/***/ }), +/* 782 */ +/***/ (function(module, exports, __webpack_require__) { -tok('PRERELEASEIDENTIFIER') -src[t.PRERELEASEIDENTIFIER] = '(?:' + src[t.NUMERICIDENTIFIER] + - '|' + src[t.NONNUMERICIDENTIFIER] + ')' +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.matchAny = exports.convertPatternsToRe = exports.makeRe = exports.getPatternParts = exports.expandBraceExpansion = exports.expandPatternsWithBraceExpansion = exports.isAffectDepthOfReadingPattern = exports.endsWithSlashGlobStar = exports.hasGlobStar = exports.getBaseDirectory = exports.getPositivePatterns = exports.getNegativePatterns = exports.isPositivePattern = exports.isNegativePattern = exports.convertToNegativePattern = exports.convertToPositivePattern = exports.isDynamicPattern = exports.isStaticPattern = void 0; +const path = __webpack_require__(4); +const globParent = __webpack_require__(265); +const micromatch = __webpack_require__(783); +const picomatch = __webpack_require__(285); +const GLOBSTAR = '**'; +const ESCAPE_SYMBOL = '\\'; +const COMMON_GLOB_SYMBOLS_RE = /[*?]|^!/; +const REGEX_CHARACTER_CLASS_SYMBOLS_RE = /\[.*]/; +const REGEX_GROUP_SYMBOLS_RE = /(?:^|[^!*+?@])\(.*\|.*\)/; +const GLOB_EXTENSION_SYMBOLS_RE = /[!*+?@]\(.*\)/; +const BRACE_EXPANSIONS_SYMBOLS_RE = /{.*(?:,|\.\.).*}/; +function isStaticPattern(pattern, options = {}) { + return !isDynamicPattern(pattern, options); +} +exports.isStaticPattern = isStaticPattern; +function isDynamicPattern(pattern, options = {}) { + /** + * A special case with an empty string is necessary for matching patterns that start with a forward slash. + * An empty string cannot be a dynamic pattern. + * For example, the pattern `/lib/*` will be spread into parts: '', 'lib', '*'. + */ + if (pattern === '') { + return false; + } + /** + * When the `caseSensitiveMatch` option is disabled, all patterns must be marked as dynamic, because we cannot check + * filepath directly (without read directory). + */ + if (options.caseSensitiveMatch === false || pattern.includes(ESCAPE_SYMBOL)) { + return true; + } + if (COMMON_GLOB_SYMBOLS_RE.test(pattern) || REGEX_CHARACTER_CLASS_SYMBOLS_RE.test(pattern) || REGEX_GROUP_SYMBOLS_RE.test(pattern)) { + return true; + } + if (options.extglob !== false && GLOB_EXTENSION_SYMBOLS_RE.test(pattern)) { + return true; + } + if (options.braceExpansion !== false && BRACE_EXPANSIONS_SYMBOLS_RE.test(pattern)) { + return true; + } + return false; +} +exports.isDynamicPattern = isDynamicPattern; +function convertToPositivePattern(pattern) { + return isNegativePattern(pattern) ? pattern.slice(1) : pattern; +} +exports.convertToPositivePattern = convertToPositivePattern; +function convertToNegativePattern(pattern) { + return '!' + pattern; +} +exports.convertToNegativePattern = convertToNegativePattern; +function isNegativePattern(pattern) { + return pattern.startsWith('!') && pattern[1] !== '('; +} +exports.isNegativePattern = isNegativePattern; +function isPositivePattern(pattern) { + return !isNegativePattern(pattern); +} +exports.isPositivePattern = isPositivePattern; +function getNegativePatterns(patterns) { + return patterns.filter(isNegativePattern); +} +exports.getNegativePatterns = getNegativePatterns; +function getPositivePatterns(patterns) { + return patterns.filter(isPositivePattern); +} +exports.getPositivePatterns = getPositivePatterns; +function getBaseDirectory(pattern) { + return globParent(pattern, { flipBackslashes: false }); +} +exports.getBaseDirectory = getBaseDirectory; +function hasGlobStar(pattern) { + return pattern.includes(GLOBSTAR); +} +exports.hasGlobStar = hasGlobStar; +function endsWithSlashGlobStar(pattern) { + return pattern.endsWith('/' + GLOBSTAR); +} +exports.endsWithSlashGlobStar = endsWithSlashGlobStar; +function isAffectDepthOfReadingPattern(pattern) { + const basename = path.basename(pattern); + return endsWithSlashGlobStar(pattern) || isStaticPattern(basename); +} +exports.isAffectDepthOfReadingPattern = isAffectDepthOfReadingPattern; +function expandPatternsWithBraceExpansion(patterns) { + return patterns.reduce((collection, pattern) => { + return collection.concat(expandBraceExpansion(pattern)); + }, []); +} +exports.expandPatternsWithBraceExpansion = expandPatternsWithBraceExpansion; +function expandBraceExpansion(pattern) { + return micromatch.braces(pattern, { + expand: true, + nodupes: true + }); +} +exports.expandBraceExpansion = expandBraceExpansion; +function getPatternParts(pattern, options) { + let { parts } = picomatch.scan(pattern, Object.assign(Object.assign({}, options), { parts: true })); + /** + * The scan method returns an empty array in some cases. + * See micromatch/picomatch#58 for more details. + */ + if (parts.length === 0) { + parts = [pattern]; + } + /** + * The scan method does not return an empty part for the pattern with a forward slash. + * This is another part of micromatch/picomatch#58. + */ + if (parts[0].startsWith('/')) { + parts[0] = parts[0].slice(1); + parts.unshift(''); + } + return parts; +} +exports.getPatternParts = getPatternParts; +function makeRe(pattern, options) { + return micromatch.makeRe(pattern, options); +} +exports.makeRe = makeRe; +function convertPatternsToRe(patterns, options) { + return patterns.map((pattern) => makeRe(pattern, options)); +} +exports.convertPatternsToRe = convertPatternsToRe; +function matchAny(entry, patternsRe) { + return patternsRe.some((patternRe) => patternRe.test(entry)); +} +exports.matchAny = matchAny; -tok('PRERELEASEIDENTIFIERLOOSE') -src[t.PRERELEASEIDENTIFIERLOOSE] = '(?:' + src[t.NUMERICIDENTIFIERLOOSE] + - '|' + src[t.NONNUMERICIDENTIFIER] + ')' -// ## Pre-release Version -// Hyphen, followed by one or more dot-separated pre-release version -// identifiers. +/***/ }), +/* 783 */ +/***/ (function(module, exports, __webpack_require__) { -tok('PRERELEASE') -src[t.PRERELEASE] = '(?:-(' + src[t.PRERELEASEIDENTIFIER] + - '(?:\\.' + src[t.PRERELEASEIDENTIFIER] + ')*))' +"use strict"; -tok('PRERELEASELOOSE') -src[t.PRERELEASELOOSE] = '(?:-?(' + src[t.PRERELEASEIDENTIFIERLOOSE] + - '(?:\\.' + src[t.PRERELEASEIDENTIFIERLOOSE] + ')*))' -// ## Build Metadata Identifier -// Any combination of digits, letters, or hyphens. +const util = __webpack_require__(113); +const braces = __webpack_require__(269); +const picomatch = __webpack_require__(784); +const utils = __webpack_require__(787); +const isEmptyString = val => val === '' || val === './'; -tok('BUILDIDENTIFIER') -src[t.BUILDIDENTIFIER] = '[0-9A-Za-z-]+' +/** + * Returns an array of strings that match one or more glob patterns. + * + * ```js + * const mm = require('micromatch'); + * // mm(list, patterns[, options]); + * + * console.log(mm(['a.js', 'a.txt'], ['*.js'])); + * //=> [ 'a.js' ] + * ``` + * @param {String|Array} `list` List of strings to match. + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `options` See available [options](#options) + * @return {Array} Returns an array of matches + * @summary false + * @api public + */ -// ## Build Metadata -// Plus sign, followed by one or more period-separated build metadata -// identifiers. +const micromatch = (list, patterns, options) => { + patterns = [].concat(patterns); + list = [].concat(list); -tok('BUILD') -src[t.BUILD] = '(?:\\+(' + src[t.BUILDIDENTIFIER] + - '(?:\\.' + src[t.BUILDIDENTIFIER] + ')*))' + let omit = new Set(); + let keep = new Set(); + let items = new Set(); + let negatives = 0; -// ## Full Version String -// A main version, followed optionally by a pre-release version and -// build metadata. + let onResult = state => { + items.add(state.output); + if (options && options.onResult) { + options.onResult(state); + } + }; -// Note that the only major, minor, patch, and pre-release sections of -// the version string are capturing groups. The build metadata is not a -// capturing group, because it should not ever be used in version -// comparison. + for (let i = 0; i < patterns.length; i++) { + let isMatch = picomatch(String(patterns[i]), { ...options, onResult }, true); + let negated = isMatch.state.negated || isMatch.state.negatedExtglob; + if (negated) negatives++; -tok('FULL') -tok('FULLPLAIN') -src[t.FULLPLAIN] = 'v?' + src[t.MAINVERSION] + - src[t.PRERELEASE] + '?' + - src[t.BUILD] + '?' + for (let item of list) { + let matched = isMatch(item, true); -src[t.FULL] = '^' + src[t.FULLPLAIN] + '$' + let match = negated ? !matched.isMatch : matched.isMatch; + if (!match) continue; -// like full, but allows v1.2.3 and =1.2.3, which people do sometimes. -// also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty -// common in the npm registry. -tok('LOOSEPLAIN') -src[t.LOOSEPLAIN] = '[v=\\s]*' + src[t.MAINVERSIONLOOSE] + - src[t.PRERELEASELOOSE] + '?' + - src[t.BUILD] + '?' + if (negated) { + omit.add(matched.output); + } else { + omit.delete(matched.output); + keep.add(matched.output); + } + } + } -tok('LOOSE') -src[t.LOOSE] = '^' + src[t.LOOSEPLAIN] + '$' + let result = negatives === patterns.length ? [...items] : [...keep]; + let matches = result.filter(item => !omit.has(item)); -tok('GTLT') -src[t.GTLT] = '((?:<|>)?=?)' + if (options && matches.length === 0) { + if (options.failglob === true) { + throw new Error(`No matches found for "${patterns.join(', ')}"`); + } -// Something like "2.*" or "1.2.x". -// Note that "x.x" is a valid xRange identifer, meaning "any version" -// Only the first item is strictly required. -tok('XRANGEIDENTIFIERLOOSE') -src[t.XRANGEIDENTIFIERLOOSE] = src[t.NUMERICIDENTIFIERLOOSE] + '|x|X|\\*' -tok('XRANGEIDENTIFIER') -src[t.XRANGEIDENTIFIER] = src[t.NUMERICIDENTIFIER] + '|x|X|\\*' + if (options.nonull === true || options.nullglob === true) { + return options.unescape ? patterns.map(p => p.replace(/\\/g, '')) : patterns; + } + } -tok('XRANGEPLAIN') -src[t.XRANGEPLAIN] = '[v=\\s]*(' + src[t.XRANGEIDENTIFIER] + ')' + - '(?:\\.(' + src[t.XRANGEIDENTIFIER] + ')' + - '(?:\\.(' + src[t.XRANGEIDENTIFIER] + ')' + - '(?:' + src[t.PRERELEASE] + ')?' + - src[t.BUILD] + '?' + - ')?)?' + return matches; +}; -tok('XRANGEPLAINLOOSE') -src[t.XRANGEPLAINLOOSE] = '[v=\\s]*(' + src[t.XRANGEIDENTIFIERLOOSE] + ')' + - '(?:\\.(' + src[t.XRANGEIDENTIFIERLOOSE] + ')' + - '(?:\\.(' + src[t.XRANGEIDENTIFIERLOOSE] + ')' + - '(?:' + src[t.PRERELEASELOOSE] + ')?' + - src[t.BUILD] + '?' + - ')?)?' +/** + * Backwards compatibility + */ -tok('XRANGE') -src[t.XRANGE] = '^' + src[t.GTLT] + '\\s*' + src[t.XRANGEPLAIN] + '$' -tok('XRANGELOOSE') -src[t.XRANGELOOSE] = '^' + src[t.GTLT] + '\\s*' + src[t.XRANGEPLAINLOOSE] + '$' +micromatch.match = micromatch; -// Coercion. -// Extract anything that could conceivably be a part of a valid semver -tok('COERCE') -src[t.COERCE] = '(^|[^\\d])' + - '(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '})' + - '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + - '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + - '(?:$|[^\\d])' -tok('COERCERTL') -re[t.COERCERTL] = new RegExp(src[t.COERCE], 'g') +/** + * Returns a matcher function from the given glob `pattern` and `options`. + * The returned function takes a string to match as its only argument and returns + * true if the string is a match. + * + * ```js + * const mm = require('micromatch'); + * // mm.matcher(pattern[, options]); + * + * const isMatch = mm.matcher('*.!(*a)'); + * console.log(isMatch('a.a')); //=> false + * console.log(isMatch('a.b')); //=> true + * ``` + * @param {String} `pattern` Glob pattern + * @param {Object} `options` + * @return {Function} Returns a matcher function. + * @api public + */ -// Tilde ranges. -// Meaning is "reasonably at or greater than" -tok('LONETILDE') -src[t.LONETILDE] = '(?:~>?)' +micromatch.matcher = (pattern, options) => picomatch(pattern, options); -tok('TILDETRIM') -src[t.TILDETRIM] = '(\\s*)' + src[t.LONETILDE] + '\\s+' -re[t.TILDETRIM] = new RegExp(src[t.TILDETRIM], 'g') -var tildeTrimReplace = '$1~' +/** + * Returns true if **any** of the given glob `patterns` match the specified `string`. + * + * ```js + * const mm = require('micromatch'); + * // mm.isMatch(string, patterns[, options]); + * + * console.log(mm.isMatch('a.a', ['b.*', '*.a'])); //=> true + * console.log(mm.isMatch('a.a', 'b.*')); //=> false + * ``` + * @param {String} `str` The string to test. + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `[options]` See available [options](#options). + * @return {Boolean} Returns true if any patterns match `str` + * @api public + */ -tok('TILDE') -src[t.TILDE] = '^' + src[t.LONETILDE] + src[t.XRANGEPLAIN] + '$' -tok('TILDELOOSE') -src[t.TILDELOOSE] = '^' + src[t.LONETILDE] + src[t.XRANGEPLAINLOOSE] + '$' +micromatch.isMatch = (str, patterns, options) => picomatch(patterns, options)(str); -// Caret ranges. -// Meaning is "at least and backwards compatible with" -tok('LONECARET') -src[t.LONECARET] = '(?:\\^)' +/** + * Backwards compatibility + */ -tok('CARETTRIM') -src[t.CARETTRIM] = '(\\s*)' + src[t.LONECARET] + '\\s+' -re[t.CARETTRIM] = new RegExp(src[t.CARETTRIM], 'g') -var caretTrimReplace = '$1^' +micromatch.any = micromatch.isMatch; -tok('CARET') -src[t.CARET] = '^' + src[t.LONECARET] + src[t.XRANGEPLAIN] + '$' -tok('CARETLOOSE') -src[t.CARETLOOSE] = '^' + src[t.LONECARET] + src[t.XRANGEPLAINLOOSE] + '$' +/** + * Returns a list of strings that _**do not match any**_ of the given `patterns`. + * + * ```js + * const mm = require('micromatch'); + * // mm.not(list, patterns[, options]); + * + * console.log(mm.not(['a.a', 'b.b', 'c.c'], '*.a')); + * //=> ['b.b', 'c.c'] + * ``` + * @param {Array} `list` Array of strings to match. + * @param {String|Array} `patterns` One or more glob pattern to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Array} Returns an array of strings that **do not match** the given patterns. + * @api public + */ -// A simple gt/lt/eq thing, or just "" to indicate "any version" -tok('COMPARATORLOOSE') -src[t.COMPARATORLOOSE] = '^' + src[t.GTLT] + '\\s*(' + src[t.LOOSEPLAIN] + ')$|^$' -tok('COMPARATOR') -src[t.COMPARATOR] = '^' + src[t.GTLT] + '\\s*(' + src[t.FULLPLAIN] + ')$|^$' +micromatch.not = (list, patterns, options = {}) => { + patterns = [].concat(patterns).map(String); + let result = new Set(); + let items = []; -// An expression to strip any whitespace between the gtlt and the thing -// it modifies, so that `> 1.2.3` ==> `>1.2.3` -tok('COMPARATORTRIM') -src[t.COMPARATORTRIM] = '(\\s*)' + src[t.GTLT] + - '\\s*(' + src[t.LOOSEPLAIN] + '|' + src[t.XRANGEPLAIN] + ')' + let onResult = state => { + if (options.onResult) options.onResult(state); + items.push(state.output); + }; -// this one has to use the /g flag -re[t.COMPARATORTRIM] = new RegExp(src[t.COMPARATORTRIM], 'g') -var comparatorTrimReplace = '$1$2$3' + let matches = micromatch(list, patterns, { ...options, onResult }); -// Something like `1.2.3 - 1.2.4` -// Note that these all use the loose form, because they'll be -// checked against either the strict or loose comparator form -// later. -tok('HYPHENRANGE') -src[t.HYPHENRANGE] = '^\\s*(' + src[t.XRANGEPLAIN] + ')' + - '\\s+-\\s+' + - '(' + src[t.XRANGEPLAIN] + ')' + - '\\s*$' + for (let item of items) { + if (!matches.includes(item)) { + result.add(item); + } + } + return [...result]; +}; -tok('HYPHENRANGELOOSE') -src[t.HYPHENRANGELOOSE] = '^\\s*(' + src[t.XRANGEPLAINLOOSE] + ')' + - '\\s+-\\s+' + - '(' + src[t.XRANGEPLAINLOOSE] + ')' + - '\\s*$' +/** + * Returns true if the given `string` contains the given pattern. Similar + * to [.isMatch](#isMatch) but the pattern can match any part of the string. + * + * ```js + * var mm = require('micromatch'); + * // mm.contains(string, pattern[, options]); + * + * console.log(mm.contains('aa/bb/cc', '*b')); + * //=> true + * console.log(mm.contains('aa/bb/cc', '*d')); + * //=> false + * ``` + * @param {String} `str` The string to match. + * @param {String|Array} `patterns` Glob pattern to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Boolean} Returns true if any of the patterns matches any part of `str`. + * @api public + */ -// Star ranges basically just allow anything at all. -tok('STAR') -src[t.STAR] = '(<|>)?=?\\s*\\*' +micromatch.contains = (str, pattern, options) => { + if (typeof str !== 'string') { + throw new TypeError(`Expected a string: "${util.inspect(str)}"`); + } -// Compile to actual regexp objects. -// All are flag-free, unless they were created above with a flag. -for (var i = 0; i < R; i++) { - debug(i, src[i]) - if (!re[i]) { - re[i] = new RegExp(src[i]) + if (Array.isArray(pattern)) { + return pattern.some(p => micromatch.contains(str, p, options)); } -} -exports.parse = parse -function parse (version, options) { - if (!options || typeof options !== 'object') { - options = { - loose: !!options, - includePrerelease: false + if (typeof pattern === 'string') { + if (isEmptyString(str) || isEmptyString(pattern)) { + return false; } - } - if (version instanceof SemVer) { - return version + if (str.includes(pattern) || (str.startsWith('./') && str.slice(2).includes(pattern))) { + return true; + } } - if (typeof version !== 'string') { - return null + return micromatch.isMatch(str, pattern, { ...options, contains: true }); +}; + +/** + * Filter the keys of the given object with the given `glob` pattern + * and `options`. Does not attempt to match nested keys. If you need this feature, + * use [glob-object][] instead. + * + * ```js + * const mm = require('micromatch'); + * // mm.matchKeys(object, patterns[, options]); + * + * const obj = { aa: 'a', ab: 'b', ac: 'c' }; + * console.log(mm.matchKeys(obj, '*b')); + * //=> { ab: 'b' } + * ``` + * @param {Object} `object` The object with keys to filter. + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Object} Returns an object with only keys that match the given patterns. + * @api public + */ + +micromatch.matchKeys = (obj, patterns, options) => { + if (!utils.isObject(obj)) { + throw new TypeError('Expected the first argument to be an object'); } + let keys = micromatch(Object.keys(obj), patterns, options); + let res = {}; + for (let key of keys) res[key] = obj[key]; + return res; +}; - if (version.length > MAX_LENGTH) { - return null - } +/** + * Returns true if some of the strings in the given `list` match any of the given glob `patterns`. + * + * ```js + * const mm = require('micromatch'); + * // mm.some(list, patterns[, options]); + * + * console.log(mm.some(['foo.js', 'bar.js'], ['*.js', '!foo.js'])); + * // true + * console.log(mm.some(['foo.js'], ['*.js', '!foo.js'])); + * // false + * ``` + * @param {String|Array} `list` The string or array of strings to test. Returns as soon as the first match is found. + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Boolean} Returns true if any `patterns` matches any of the strings in `list` + * @api public + */ - var r = options.loose ? re[t.LOOSE] : re[t.FULL] - if (!r.test(version)) { - return null - } +micromatch.some = (list, patterns, options) => { + let items = [].concat(list); - try { - return new SemVer(version, options) - } catch (er) { - return null + for (let pattern of [].concat(patterns)) { + let isMatch = picomatch(String(pattern), options); + if (items.some(item => isMatch(item))) { + return true; + } } -} - -exports.valid = valid -function valid (version, options) { - var v = parse(version, options) - return v ? v.version : null -} + return false; +}; -exports.clean = clean -function clean (version, options) { - var s = parse(version.trim().replace(/^[=v]+/, ''), options) - return s ? s.version : null -} +/** + * Returns true if every string in the given `list` matches + * any of the given glob `patterns`. + * + * ```js + * const mm = require('micromatch'); + * // mm.every(list, patterns[, options]); + * + * console.log(mm.every('foo.js', ['foo.js'])); + * // true + * console.log(mm.every(['foo.js', 'bar.js'], ['*.js'])); + * // true + * console.log(mm.every(['foo.js', 'bar.js'], ['*.js', '!foo.js'])); + * // false + * console.log(mm.every(['foo.js'], ['*.js', '!foo.js'])); + * // false + * ``` + * @param {String|Array} `list` The string or array of strings to test. + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Boolean} Returns true if all `patterns` matches all of the strings in `list` + * @api public + */ -exports.SemVer = SemVer +micromatch.every = (list, patterns, options) => { + let items = [].concat(list); -function SemVer (version, options) { - if (!options || typeof options !== 'object') { - options = { - loose: !!options, - includePrerelease: false - } - } - if (version instanceof SemVer) { - if (version.loose === options.loose) { - return version - } else { - version = version.version + for (let pattern of [].concat(patterns)) { + let isMatch = picomatch(String(pattern), options); + if (!items.every(item => isMatch(item))) { + return false; } - } else if (typeof version !== 'string') { - throw new TypeError('Invalid Version: ' + version) } + return true; +}; - if (version.length > MAX_LENGTH) { - throw new TypeError('version is longer than ' + MAX_LENGTH + ' characters') - } +/** + * Returns true if **all** of the given `patterns` match + * the specified string. + * + * ```js + * const mm = require('micromatch'); + * // mm.all(string, patterns[, options]); + * + * console.log(mm.all('foo.js', ['foo.js'])); + * // true + * + * console.log(mm.all('foo.js', ['*.js', '!foo.js'])); + * // false + * + * console.log(mm.all('foo.js', ['*.js', 'foo.js'])); + * // true + * + * console.log(mm.all('foo.js', ['*.js', 'f*', '*o*', '*o.js'])); + * // true + * ``` + * @param {String|Array} `str` The string to test. + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Boolean} Returns true if any patterns match `str` + * @api public + */ - if (!(this instanceof SemVer)) { - return new SemVer(version, options) +micromatch.all = (str, patterns, options) => { + if (typeof str !== 'string') { + throw new TypeError(`Expected a string: "${util.inspect(str)}"`); } - debug('SemVer', version, options) - this.options = options - this.loose = !!options.loose + return [].concat(patterns).every(p => picomatch(p, options)(str)); +}; - var m = version.trim().match(options.loose ? re[t.LOOSE] : re[t.FULL]) +/** + * Returns an array of matches captured by `pattern` in `string, or `null` if the pattern did not match. + * + * ```js + * const mm = require('micromatch'); + * // mm.capture(pattern, string[, options]); + * + * console.log(mm.capture('test/*.js', 'test/foo.js')); + * //=> ['foo'] + * console.log(mm.capture('test/*.js', 'foo/bar.css')); + * //=> null + * ``` + * @param {String} `glob` Glob pattern to use for matching. + * @param {String} `input` String to match + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Array|null} Returns an array of captures if the input matches the glob pattern, otherwise `null`. + * @api public + */ - if (!m) { - throw new TypeError('Invalid Version: ' + version) +micromatch.capture = (glob, input, options) => { + let posix = utils.isWindows(options); + let regex = picomatch.makeRe(String(glob), { ...options, capture: true }); + let match = regex.exec(posix ? utils.toPosixSlashes(input) : input); + + if (match) { + return match.slice(1).map(v => v === void 0 ? '' : v); } +}; - this.raw = version +/** + * Create a regular expression from the given glob `pattern`. + * + * ```js + * const mm = require('micromatch'); + * // mm.makeRe(pattern[, options]); + * + * console.log(mm.makeRe('*.js')); + * //=> /^(?:(\.[\\\/])?(?!\.)(?=.)[^\/]*?\.js)$/ + * ``` + * @param {String} `pattern` A glob pattern to convert to regex. + * @param {Object} `options` + * @return {RegExp} Returns a regex created from the given pattern. + * @api public + */ - // these are actually numbers - this.major = +m[1] - this.minor = +m[2] - this.patch = +m[3] +micromatch.makeRe = (...args) => picomatch.makeRe(...args); - if (this.major > MAX_SAFE_INTEGER || this.major < 0) { - throw new TypeError('Invalid major version') - } +/** + * Scan a glob pattern to separate the pattern into segments. Used + * by the [split](#split) method. + * + * ```js + * const mm = require('micromatch'); + * const state = mm.scan(pattern[, options]); + * ``` + * @param {String} `pattern` + * @param {Object} `options` + * @return {Object} Returns an object with + * @api public + */ - if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) { - throw new TypeError('Invalid minor version') - } +micromatch.scan = (...args) => picomatch.scan(...args); - if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) { - throw new TypeError('Invalid patch version') - } +/** + * Parse a glob pattern to create the source string for a regular + * expression. + * + * ```js + * const mm = require('micromatch'); + * const state = mm(pattern[, options]); + * ``` + * @param {String} `glob` + * @param {Object} `options` + * @return {Object} Returns an object with useful properties and output to be used as regex source string. + * @api public + */ - // numberify any prerelease numeric ids - if (!m[4]) { - this.prerelease = [] - } else { - this.prerelease = m[4].split('.').map(function (id) { - if (/^[0-9]+$/.test(id)) { - var num = +id - if (num >= 0 && num < MAX_SAFE_INTEGER) { - return num - } - } - return id - }) +micromatch.parse = (patterns, options) => { + let res = []; + for (let pattern of [].concat(patterns || [])) { + for (let str of braces(String(pattern), options)) { + res.push(picomatch.parse(str, options)); + } } + return res; +}; - this.build = m[5] ? m[5].split('.') : [] - this.format() -} +/** + * Process the given brace `pattern`. + * + * ```js + * const { braces } = require('micromatch'); + * console.log(braces('foo/{a,b,c}/bar')); + * //=> [ 'foo/(a|b|c)/bar' ] + * + * console.log(braces('foo/{a,b,c}/bar', { expand: true })); + * //=> [ 'foo/a/bar', 'foo/b/bar', 'foo/c/bar' ] + * ``` + * @param {String} `pattern` String with brace pattern to process. + * @param {Object} `options` Any [options](#options) to change how expansion is performed. See the [braces][] library for all available options. + * @return {Array} + * @api public + */ -SemVer.prototype.format = function () { - this.version = this.major + '.' + this.minor + '.' + this.patch - if (this.prerelease.length) { - this.version += '-' + this.prerelease.join('.') +micromatch.braces = (pattern, options) => { + if (typeof pattern !== 'string') throw new TypeError('Expected a string'); + if ((options && options.nobrace === true) || !/\{.*\}/.test(pattern)) { + return [pattern]; } - return this.version -} + return braces(pattern, options); +}; -SemVer.prototype.toString = function () { - return this.version -} +/** + * Expand braces + */ -SemVer.prototype.compare = function (other) { - debug('SemVer.compare', this.version, this.options, other) - if (!(other instanceof SemVer)) { - other = new SemVer(other, this.options) - } +micromatch.braceExpand = (pattern, options) => { + if (typeof pattern !== 'string') throw new TypeError('Expected a string'); + return micromatch.braces(pattern, { ...options, expand: true }); +}; - return this.compareMain(other) || this.comparePre(other) -} +/** + * Expose micromatch + */ -SemVer.prototype.compareMain = function (other) { - if (!(other instanceof SemVer)) { - other = new SemVer(other, this.options) - } +module.exports = micromatch; - return compareIdentifiers(this.major, other.major) || - compareIdentifiers(this.minor, other.minor) || - compareIdentifiers(this.patch, other.patch) -} -SemVer.prototype.comparePre = function (other) { - if (!(other instanceof SemVer)) { - other = new SemVer(other, this.options) - } +/***/ }), +/* 784 */ +/***/ (function(module, exports, __webpack_require__) { - // NOT having a prerelease is > having one - if (this.prerelease.length && !other.prerelease.length) { - return -1 - } else if (!this.prerelease.length && other.prerelease.length) { - return 1 - } else if (!this.prerelease.length && !other.prerelease.length) { - return 0 - } +"use strict"; - var i = 0 - do { - var a = this.prerelease[i] - var b = other.prerelease[i] - debug('prerelease compare', i, a, b) - if (a === undefined && b === undefined) { - return 0 - } else if (b === undefined) { - return 1 - } else if (a === undefined) { - return -1 - } else if (a === b) { - continue - } else { - return compareIdentifiers(a, b) - } - } while (++i) -} -SemVer.prototype.compareBuild = function (other) { - if (!(other instanceof SemVer)) { - other = new SemVer(other, this.options) - } +module.exports = __webpack_require__(785); - var i = 0 - do { - var a = this.build[i] - var b = other.build[i] - debug('prerelease compare', i, a, b) - if (a === undefined && b === undefined) { - return 0 - } else if (b === undefined) { - return 1 - } else if (a === undefined) { - return -1 - } else if (a === b) { - continue - } else { - return compareIdentifiers(a, b) - } - } while (++i) -} -// preminor will bump the version up to the next minor release, and immediately -// down to pre-release. premajor and prepatch work the same way. -SemVer.prototype.inc = function (release, identifier) { - switch (release) { - case 'premajor': - this.prerelease.length = 0 - this.patch = 0 - this.minor = 0 - this.major++ - this.inc('pre', identifier) - break - case 'preminor': - this.prerelease.length = 0 - this.patch = 0 - this.minor++ - this.inc('pre', identifier) - break - case 'prepatch': - // If this is already a prerelease, it will bump to the next version - // drop any prereleases that might already exist, since they are not - // relevant at this point. - this.prerelease.length = 0 - this.inc('patch', identifier) - this.inc('pre', identifier) - break - // If the input is a non-prerelease version, this acts the same as - // prepatch. - case 'prerelease': - if (this.prerelease.length === 0) { - this.inc('patch', identifier) - } - this.inc('pre', identifier) - break +/***/ }), +/* 785 */ +/***/ (function(module, exports, __webpack_require__) { - case 'major': - // If this is a pre-major version, bump up to the same major version. - // Otherwise increment major. - // 1.0.0-5 bumps to 1.0.0 - // 1.1.0 bumps to 2.0.0 - if (this.minor !== 0 || - this.patch !== 0 || - this.prerelease.length === 0) { - this.major++ - } - this.minor = 0 - this.patch = 0 - this.prerelease = [] - break - case 'minor': - // If this is a pre-minor version, bump up to the same minor version. - // Otherwise increment minor. - // 1.2.0-5 bumps to 1.2.0 - // 1.2.1 bumps to 1.3.0 - if (this.patch !== 0 || this.prerelease.length === 0) { - this.minor++ - } - this.patch = 0 - this.prerelease = [] - break - case 'patch': - // If this is not a pre-release version, it will increment the patch. - // If it is a pre-release it will bump up to the same patch version. - // 1.2.0-5 patches to 1.2.0 - // 1.2.0 patches to 1.2.1 - if (this.prerelease.length === 0) { - this.patch++ - } - this.prerelease = [] - break - // This probably shouldn't be used publicly. - // 1.0.0 "pre" would become 1.0.0-0 which is the wrong direction. - case 'pre': - if (this.prerelease.length === 0) { - this.prerelease = [0] - } else { - var i = this.prerelease.length - while (--i >= 0) { - if (typeof this.prerelease[i] === 'number') { - this.prerelease[i]++ - i = -2 - } - } - if (i === -1) { - // didn't increment anything - this.prerelease.push(0) - } - } - if (identifier) { - // 1.2.0-beta.1 bumps to 1.2.0-beta.2, - // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0 - if (this.prerelease[0] === identifier) { - if (isNaN(this.prerelease[1])) { - this.prerelease = [identifier, 0] - } - } else { - this.prerelease = [identifier, 0] - } - } - break +"use strict"; - default: - throw new Error('invalid increment argument: ' + release) - } - this.format() - this.raw = this.version - return this -} -exports.inc = inc -function inc (version, release, loose, identifier) { - if (typeof (loose) === 'string') { - identifier = loose - loose = undefined - } +const path = __webpack_require__(4); +const scan = __webpack_require__(786); +const parse = __webpack_require__(789); +const utils = __webpack_require__(787); +const constants = __webpack_require__(788); +const isObject = val => val && typeof val === 'object' && !Array.isArray(val); - try { - return new SemVer(version, loose).inc(release, identifier).version - } catch (er) { - return null - } -} +/** + * Creates a matcher function from one or more glob patterns. The + * returned function takes a string to match as its first argument, + * and returns true if the string is a match. The returned matcher + * function also takes a boolean as the second argument that, when true, + * returns an object with additional information. + * + * ```js + * const picomatch = require('picomatch'); + * // picomatch(glob[, options]); + * + * const isMatch = picomatch('*.!(*a)'); + * console.log(isMatch('a.a')); //=> false + * console.log(isMatch('a.b')); //=> true + * ``` + * @name picomatch + * @param {String|Array} `globs` One or more glob patterns. + * @param {Object=} `options` + * @return {Function=} Returns a matcher function. + * @api public + */ -exports.diff = diff -function diff (version1, version2) { - if (eq(version1, version2)) { - return null - } else { - var v1 = parse(version1) - var v2 = parse(version2) - var prefix = '' - if (v1.prerelease.length || v2.prerelease.length) { - prefix = 'pre' - var defaultResult = 'prerelease' - } - for (var key in v1) { - if (key === 'major' || key === 'minor' || key === 'patch') { - if (v1[key] !== v2[key]) { - return prefix + key - } +const picomatch = (glob, options, returnState = false) => { + if (Array.isArray(glob)) { + const fns = glob.map(input => picomatch(input, options, returnState)); + const arrayMatcher = str => { + for (const isMatch of fns) { + const state = isMatch(str); + if (state) return state; } - } - return defaultResult // may be undefined + return false; + }; + return arrayMatcher; } -} - -exports.compareIdentifiers = compareIdentifiers -var numeric = /^[0-9]+$/ -function compareIdentifiers (a, b) { - var anum = numeric.test(a) - var bnum = numeric.test(b) + const isState = isObject(glob) && glob.tokens && glob.input; - if (anum && bnum) { - a = +a - b = +b + if (glob === '' || (typeof glob !== 'string' && !isState)) { + throw new TypeError('Expected pattern to be a non-empty string'); } - return a === b ? 0 - : (anum && !bnum) ? -1 - : (bnum && !anum) ? 1 - : a < b ? -1 - : 1 -} + const opts = options || {}; + const posix = utils.isWindows(options); + const regex = isState + ? picomatch.compileRe(glob, options) + : picomatch.makeRe(glob, options, false, true); -exports.rcompareIdentifiers = rcompareIdentifiers -function rcompareIdentifiers (a, b) { - return compareIdentifiers(b, a) -} + const state = regex.state; + delete regex.state; -exports.major = major -function major (a, loose) { - return new SemVer(a, loose).major -} + let isIgnored = () => false; + if (opts.ignore) { + const ignoreOpts = { ...options, ignore: null, onMatch: null, onResult: null }; + isIgnored = picomatch(opts.ignore, ignoreOpts, returnState); + } -exports.minor = minor -function minor (a, loose) { - return new SemVer(a, loose).minor -} + const matcher = (input, returnObject = false) => { + const { isMatch, match, output } = picomatch.test(input, regex, options, { glob, posix }); + const result = { glob, state, regex, posix, input, output, match, isMatch }; -exports.patch = patch -function patch (a, loose) { - return new SemVer(a, loose).patch -} + if (typeof opts.onResult === 'function') { + opts.onResult(result); + } -exports.compare = compare -function compare (a, b, loose) { - return new SemVer(a, loose).compare(new SemVer(b, loose)) -} + if (isMatch === false) { + result.isMatch = false; + return returnObject ? result : false; + } -exports.compareLoose = compareLoose -function compareLoose (a, b) { - return compare(a, b, true) -} + if (isIgnored(input)) { + if (typeof opts.onIgnore === 'function') { + opts.onIgnore(result); + } + result.isMatch = false; + return returnObject ? result : false; + } -exports.compareBuild = compareBuild -function compareBuild (a, b, loose) { - var versionA = new SemVer(a, loose) - var versionB = new SemVer(b, loose) - return versionA.compare(versionB) || versionA.compareBuild(versionB) -} + if (typeof opts.onMatch === 'function') { + opts.onMatch(result); + } + return returnObject ? result : true; + }; -exports.rcompare = rcompare -function rcompare (a, b, loose) { - return compare(b, a, loose) -} + if (returnState) { + matcher.state = state; + } -exports.sort = sort -function sort (list, loose) { - return list.sort(function (a, b) { - return exports.compareBuild(a, b, loose) - }) -} + return matcher; +}; -exports.rsort = rsort -function rsort (list, loose) { - return list.sort(function (a, b) { - return exports.compareBuild(b, a, loose) - }) -} +/** + * Test `input` with the given `regex`. This is used by the main + * `picomatch()` function to test the input string. + * + * ```js + * const picomatch = require('picomatch'); + * // picomatch.test(input, regex[, options]); + * + * console.log(picomatch.test('foo/bar', /^(?:([^/]*?)\/([^/]*?))$/)); + * // { isMatch: true, match: [ 'foo/', 'foo', 'bar' ], output: 'foo/bar' } + * ``` + * @param {String} `input` String to test. + * @param {RegExp} `regex` + * @return {Object} Returns an object with matching info. + * @api public + */ -exports.gt = gt -function gt (a, b, loose) { - return compare(a, b, loose) > 0 -} +picomatch.test = (input, regex, options, { glob, posix } = {}) => { + if (typeof input !== 'string') { + throw new TypeError('Expected input to be a string'); + } -exports.lt = lt -function lt (a, b, loose) { - return compare(a, b, loose) < 0 -} + if (input === '') { + return { isMatch: false, output: '' }; + } -exports.eq = eq -function eq (a, b, loose) { - return compare(a, b, loose) === 0 -} + const opts = options || {}; + const format = opts.format || (posix ? utils.toPosixSlashes : null); + let match = input === glob; + let output = (match && format) ? format(input) : input; -exports.neq = neq -function neq (a, b, loose) { - return compare(a, b, loose) !== 0 -} + if (match === false) { + output = format ? format(input) : input; + match = output === glob; + } -exports.gte = gte -function gte (a, b, loose) { - return compare(a, b, loose) >= 0 -} + if (match === false || opts.capture === true) { + if (opts.matchBase === true || opts.basename === true) { + match = picomatch.matchBase(input, regex, options, posix); + } else { + match = regex.exec(output); + } + } -exports.lte = lte -function lte (a, b, loose) { - return compare(a, b, loose) <= 0 -} + return { isMatch: Boolean(match), match, output }; +}; -exports.cmp = cmp -function cmp (a, op, b, loose) { - switch (op) { - case '===': - if (typeof a === 'object') - a = a.version - if (typeof b === 'object') - b = b.version - return a === b +/** + * Match the basename of a filepath. + * + * ```js + * const picomatch = require('picomatch'); + * // picomatch.matchBase(input, glob[, options]); + * console.log(picomatch.matchBase('foo/bar.js', '*.js'); // true + * ``` + * @param {String} `input` String to test. + * @param {RegExp|String} `glob` Glob pattern or regex created by [.makeRe](#makeRe). + * @return {Boolean} + * @api public + */ - case '!==': - if (typeof a === 'object') - a = a.version - if (typeof b === 'object') - b = b.version - return a !== b +picomatch.matchBase = (input, glob, options, posix = utils.isWindows(options)) => { + const regex = glob instanceof RegExp ? glob : picomatch.makeRe(glob, options); + return regex.test(path.basename(input)); +}; - case '': - case '=': - case '==': - return eq(a, b, loose) +/** + * Returns true if **any** of the given glob `patterns` match the specified `string`. + * + * ```js + * const picomatch = require('picomatch'); + * // picomatch.isMatch(string, patterns[, options]); + * + * console.log(picomatch.isMatch('a.a', ['b.*', '*.a'])); //=> true + * console.log(picomatch.isMatch('a.a', 'b.*')); //=> false + * ``` + * @param {String|Array} str The string to test. + * @param {String|Array} patterns One or more glob patterns to use for matching. + * @param {Object} [options] See available [options](#options). + * @return {Boolean} Returns true if any patterns match `str` + * @api public + */ - case '!=': - return neq(a, b, loose) +picomatch.isMatch = (str, patterns, options) => picomatch(patterns, options)(str); - case '>': - return gt(a, b, loose) +/** + * Parse a glob pattern to create the source string for a regular + * expression. + * + * ```js + * const picomatch = require('picomatch'); + * const result = picomatch.parse(pattern[, options]); + * ``` + * @param {String} `pattern` + * @param {Object} `options` + * @return {Object} Returns an object with useful properties and output to be used as a regex source string. + * @api public + */ - case '>=': - return gte(a, b, loose) +picomatch.parse = (pattern, options) => { + if (Array.isArray(pattern)) return pattern.map(p => picomatch.parse(p, options)); + return parse(pattern, { ...options, fastpaths: false }); +}; - case '<': - return lt(a, b, loose) +/** + * Scan a glob pattern to separate the pattern into segments. + * + * ```js + * const picomatch = require('picomatch'); + * // picomatch.scan(input[, options]); + * + * const result = picomatch.scan('!./foo/*.js'); + * console.log(result); + * { prefix: '!./', + * input: '!./foo/*.js', + * start: 3, + * base: 'foo', + * glob: '*.js', + * isBrace: false, + * isBracket: false, + * isGlob: true, + * isExtglob: false, + * isGlobstar: false, + * negated: true } + * ``` + * @param {String} `input` Glob pattern to scan. + * @param {Object} `options` + * @return {Object} Returns an object with + * @api public + */ - case '<=': - return lte(a, b, loose) +picomatch.scan = (input, options) => scan(input, options); - default: - throw new TypeError('Invalid operator: ' + op) - } -} +/** + * Compile a regular expression from the `state` object returned by the + * [parse()](#parse) method. + * + * @param {Object} `state` + * @param {Object} `options` + * @param {Boolean} `returnOutput` Intended for implementors, this argument allows you to return the raw output from the parser. + * @param {Boolean} `returnState` Adds the state to a `state` property on the returned regex. Useful for implementors and debugging. + * @return {RegExp} + * @api public + */ -exports.Comparator = Comparator -function Comparator (comp, options) { - if (!options || typeof options !== 'object') { - options = { - loose: !!options, - includePrerelease: false - } +picomatch.compileRe = (state, options, returnOutput = false, returnState = false) => { + if (returnOutput === true) { + return state.output; } - if (comp instanceof Comparator) { - if (comp.loose === !!options.loose) { - return comp - } else { - comp = comp.value - } - } + const opts = options || {}; + const prepend = opts.contains ? '' : '^'; + const append = opts.contains ? '' : '$'; - if (!(this instanceof Comparator)) { - return new Comparator(comp, options) + let source = `${prepend}(?:${state.output})${append}`; + if (state && state.negated === true) { + source = `^(?!${source}).*$`; } - debug('comparator', comp, options) - this.options = options - this.loose = !!options.loose - this.parse(comp) - - if (this.semver === ANY) { - this.value = '' - } else { - this.value = this.operator + this.semver.version + const regex = picomatch.toRegex(source, options); + if (returnState === true) { + regex.state = state; } - debug('comp', this) -} + return regex; +}; -var ANY = {} -Comparator.prototype.parse = function (comp) { - var r = this.options.loose ? re[t.COMPARATORLOOSE] : re[t.COMPARATOR] - var m = comp.match(r) +/** + * Create a regular expression from a parsed glob pattern. + * + * ```js + * const picomatch = require('picomatch'); + * const state = picomatch.parse('*.js'); + * // picomatch.compileRe(state[, options]); + * + * console.log(picomatch.compileRe(state)); + * //=> /^(?:(?!\.)(?=.)[^/]*?\.js)$/ + * ``` + * @param {String} `state` The object returned from the `.parse` method. + * @param {Object} `options` + * @param {Boolean} `returnOutput` Implementors may use this argument to return the compiled output, instead of a regular expression. This is not exposed on the options to prevent end-users from mutating the result. + * @param {Boolean} `returnState` Implementors may use this argument to return the state from the parsed glob with the returned regular expression. + * @return {RegExp} Returns a regex created from the given pattern. + * @api public + */ - if (!m) { - throw new TypeError('Invalid comparator: ' + comp) +picomatch.makeRe = (input, options = {}, returnOutput = false, returnState = false) => { + if (!input || typeof input !== 'string') { + throw new TypeError('Expected a non-empty string'); } - this.operator = m[1] !== undefined ? m[1] : '' - if (this.operator === '=') { - this.operator = '' + let parsed = { negated: false, fastpaths: true }; + + if (options.fastpaths !== false && (input[0] === '.' || input[0] === '*')) { + parsed.output = parse.fastpaths(input, options); } - // if it literally is just '>' or '' then allow anything. - if (!m[2]) { - this.semver = ANY - } else { - this.semver = new SemVer(m[2], this.options.loose) + if (!parsed.output) { + parsed = parse(input, options); } -} -Comparator.prototype.toString = function () { - return this.value -} + return picomatch.compileRe(parsed, options, returnOutput, returnState); +}; -Comparator.prototype.test = function (version) { - debug('Comparator.test', version, this.options.loose) +/** + * Create a regular expression from the given regex source string. + * + * ```js + * const picomatch = require('picomatch'); + * // picomatch.toRegex(source[, options]); + * + * const { output } = picomatch.parse('*.js'); + * console.log(picomatch.toRegex(output)); + * //=> /^(?:(?!\.)(?=.)[^/]*?\.js)$/ + * ``` + * @param {String} `source` Regular expression source string. + * @param {Object} `options` + * @return {RegExp} + * @api public + */ - if (this.semver === ANY || version === ANY) { - return true +picomatch.toRegex = (source, options) => { + try { + const opts = options || {}; + return new RegExp(source, opts.flags || (opts.nocase ? 'i' : '')); + } catch (err) { + if (options && options.debug === true) throw err; + return /$^/; } +}; - if (typeof version === 'string') { - try { - version = new SemVer(version, this.options) - } catch (er) { - return false - } - } +/** + * Picomatch constants. + * @return {Object} + */ - return cmp(version, this.operator, this.semver, this.options) -} +picomatch.constants = constants; -Comparator.prototype.intersects = function (comp, options) { - if (!(comp instanceof Comparator)) { - throw new TypeError('a Comparator is required') - } +/** + * Expose "picomatch" + */ - if (!options || typeof options !== 'object') { - options = { - loose: !!options, - includePrerelease: false - } - } +module.exports = picomatch; - var rangeTmp - if (this.operator === '') { - if (this.value === '') { - return true - } - rangeTmp = new Range(comp.value, options) - return satisfies(this.value, rangeTmp, options) - } else if (comp.operator === '') { - if (comp.value === '') { - return true - } - rangeTmp = new Range(this.value, options) - return satisfies(comp.semver, rangeTmp, options) - } +/***/ }), +/* 786 */ +/***/ (function(module, exports, __webpack_require__) { - var sameDirectionIncreasing = - (this.operator === '>=' || this.operator === '>') && - (comp.operator === '>=' || comp.operator === '>') - var sameDirectionDecreasing = - (this.operator === '<=' || this.operator === '<') && - (comp.operator === '<=' || comp.operator === '<') - var sameSemVer = this.semver.version === comp.semver.version - var differentDirectionsInclusive = - (this.operator === '>=' || this.operator === '<=') && - (comp.operator === '>=' || comp.operator === '<=') - var oppositeDirectionsLessThan = - cmp(this.semver, '<', comp.semver, options) && - ((this.operator === '>=' || this.operator === '>') && - (comp.operator === '<=' || comp.operator === '<')) - var oppositeDirectionsGreaterThan = - cmp(this.semver, '>', comp.semver, options) && - ((this.operator === '<=' || this.operator === '<') && - (comp.operator === '>=' || comp.operator === '>')) +"use strict"; - return sameDirectionIncreasing || sameDirectionDecreasing || - (sameSemVer && differentDirectionsInclusive) || - oppositeDirectionsLessThan || oppositeDirectionsGreaterThan -} -exports.Range = Range -function Range (range, options) { - if (!options || typeof options !== 'object') { - options = { - loose: !!options, - includePrerelease: false - } - } +const utils = __webpack_require__(787); +const { + CHAR_ASTERISK, /* * */ + CHAR_AT, /* @ */ + CHAR_BACKWARD_SLASH, /* \ */ + CHAR_COMMA, /* , */ + CHAR_DOT, /* . */ + CHAR_EXCLAMATION_MARK, /* ! */ + CHAR_FORWARD_SLASH, /* / */ + CHAR_LEFT_CURLY_BRACE, /* { */ + CHAR_LEFT_PARENTHESES, /* ( */ + CHAR_LEFT_SQUARE_BRACKET, /* [ */ + CHAR_PLUS, /* + */ + CHAR_QUESTION_MARK, /* ? */ + CHAR_RIGHT_CURLY_BRACE, /* } */ + CHAR_RIGHT_PARENTHESES, /* ) */ + CHAR_RIGHT_SQUARE_BRACKET /* ] */ +} = __webpack_require__(788); + +const isPathSeparator = code => { + return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH; +}; - if (range instanceof Range) { - if (range.loose === !!options.loose && - range.includePrerelease === !!options.includePrerelease) { - return range - } else { - return new Range(range.raw, options) - } +const depth = token => { + if (token.isPrefix !== true) { + token.depth = token.isGlobstar ? Infinity : 1; } +}; - if (range instanceof Comparator) { - return new Range(range.value, options) - } +/** + * Quickly scans a glob pattern and returns an object with a handful of + * useful properties, like `isGlob`, `path` (the leading non-glob, if it exists), + * `glob` (the actual pattern), `negated` (true if the path starts with `!` but not + * with `!(`) and `negatedExtglob` (true if the path starts with `!(`). + * + * ```js + * const pm = require('picomatch'); + * console.log(pm.scan('foo/bar/*.js')); + * { isGlob: true, input: 'foo/bar/*.js', base: 'foo/bar', glob: '*.js' } + * ``` + * @param {String} `str` + * @param {Object} `options` + * @return {Object} Returns an object with tokens and regex source string. + * @api public + */ - if (!(this instanceof Range)) { - return new Range(range, options) - } +const scan = (input, options) => { + const opts = options || {}; - this.options = options - this.loose = !!options.loose - this.includePrerelease = !!options.includePrerelease + const length = input.length - 1; + const scanToEnd = opts.parts === true || opts.scanToEnd === true; + const slashes = []; + const tokens = []; + const parts = []; - // First, split based on boolean or || - this.raw = range - this.set = range.split(/\s*\|\|\s*/).map(function (range) { - return this.parseRange(range.trim()) - }, this).filter(function (c) { - // throw out any that are not relevant for whatever reason - return c.length - }) + let str = input; + let index = -1; + let start = 0; + let lastIndex = 0; + let isBrace = false; + let isBracket = false; + let isGlob = false; + let isExtglob = false; + let isGlobstar = false; + let braceEscaped = false; + let backslashes = false; + let negated = false; + let negatedExtglob = false; + let finished = false; + let braces = 0; + let prev; + let code; + let token = { value: '', depth: 0, isGlob: false }; - if (!this.set.length) { - throw new TypeError('Invalid SemVer Range: ' + range) - } + const eos = () => index >= length; + const peek = () => str.charCodeAt(index + 1); + const advance = () => { + prev = code; + return str.charCodeAt(++index); + }; - this.format() -} + while (index < length) { + code = advance(); + let next; -Range.prototype.format = function () { - this.range = this.set.map(function (comps) { - return comps.join(' ').trim() - }).join('||').trim() - return this.range -} + if (code === CHAR_BACKWARD_SLASH) { + backslashes = token.backslashes = true; + code = advance(); -Range.prototype.toString = function () { - return this.range -} + if (code === CHAR_LEFT_CURLY_BRACE) { + braceEscaped = true; + } + continue; + } -Range.prototype.parseRange = function (range) { - var loose = this.options.loose - range = range.trim() - // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4` - var hr = loose ? re[t.HYPHENRANGELOOSE] : re[t.HYPHENRANGE] - range = range.replace(hr, hyphenReplace) - debug('hyphen replace', range) - // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5` - range = range.replace(re[t.COMPARATORTRIM], comparatorTrimReplace) - debug('comparator trim', range, re[t.COMPARATORTRIM]) + if (braceEscaped === true || code === CHAR_LEFT_CURLY_BRACE) { + braces++; - // `~ 1.2.3` => `~1.2.3` - range = range.replace(re[t.TILDETRIM], tildeTrimReplace) + while (eos() !== true && (code = advance())) { + if (code === CHAR_BACKWARD_SLASH) { + backslashes = token.backslashes = true; + advance(); + continue; + } - // `^ 1.2.3` => `^1.2.3` - range = range.replace(re[t.CARETTRIM], caretTrimReplace) + if (code === CHAR_LEFT_CURLY_BRACE) { + braces++; + continue; + } - // normalize spaces - range = range.split(/\s+/).join(' ') + if (braceEscaped !== true && code === CHAR_DOT && (code = advance()) === CHAR_DOT) { + isBrace = token.isBrace = true; + isGlob = token.isGlob = true; + finished = true; - // At this point, the range is completely trimmed and - // ready to be split into comparators. + if (scanToEnd === true) { + continue; + } - var compRe = loose ? re[t.COMPARATORLOOSE] : re[t.COMPARATOR] - var set = range.split(' ').map(function (comp) { - return parseComparator(comp, this.options) - }, this).join(' ').split(/\s+/) - if (this.options.loose) { - // in loose mode, throw out any that are not valid comparators - set = set.filter(function (comp) { - return !!comp.match(compRe) - }) - } - set = set.map(function (comp) { - return new Comparator(comp, this.options) - }, this) + break; + } - return set -} + if (braceEscaped !== true && code === CHAR_COMMA) { + isBrace = token.isBrace = true; + isGlob = token.isGlob = true; + finished = true; -Range.prototype.intersects = function (range, options) { - if (!(range instanceof Range)) { - throw new TypeError('a Range is required') - } + if (scanToEnd === true) { + continue; + } - return this.set.some(function (thisComparators) { - return ( - isSatisfiable(thisComparators, options) && - range.set.some(function (rangeComparators) { - return ( - isSatisfiable(rangeComparators, options) && - thisComparators.every(function (thisComparator) { - return rangeComparators.every(function (rangeComparator) { - return thisComparator.intersects(rangeComparator, options) - }) - }) - ) - }) - ) - }) -} + break; + } -// take a set of comparators and determine whether there -// exists a version which can satisfy it -function isSatisfiable (comparators, options) { - var result = true - var remainingComparators = comparators.slice() - var testComparator = remainingComparators.pop() + if (code === CHAR_RIGHT_CURLY_BRACE) { + braces--; - while (result && remainingComparators.length) { - result = remainingComparators.every(function (otherComparator) { - return testComparator.intersects(otherComparator, options) - }) + if (braces === 0) { + braceEscaped = false; + isBrace = token.isBrace = true; + finished = true; + break; + } + } + } - testComparator = remainingComparators.pop() - } + if (scanToEnd === true) { + continue; + } - return result -} + break; + } -// Mostly just for testing and legacy API reasons -exports.toComparators = toComparators -function toComparators (range, options) { - return new Range(range, options).set.map(function (comp) { - return comp.map(function (c) { - return c.value - }).join(' ').trim().split(' ') - }) -} + if (code === CHAR_FORWARD_SLASH) { + slashes.push(index); + tokens.push(token); + token = { value: '', depth: 0, isGlob: false }; -// comprised of xranges, tildes, stars, and gtlt's at this point. -// already replaced the hyphen ranges -// turn into a set of JUST comparators. -function parseComparator (comp, options) { - debug('comp', comp, options) - comp = replaceCarets(comp, options) - debug('caret', comp) - comp = replaceTildes(comp, options) - debug('tildes', comp) - comp = replaceXRanges(comp, options) - debug('xrange', comp) - comp = replaceStars(comp, options) - debug('stars', comp) - return comp -} + if (finished === true) continue; + if (prev === CHAR_DOT && index === (start + 1)) { + start += 2; + continue; + } -function isX (id) { - return !id || id.toLowerCase() === 'x' || id === '*' -} + lastIndex = index + 1; + continue; + } -// ~, ~> --> * (any, kinda silly) -// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0 -// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0 -// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0 -// ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0 -// ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0 -function replaceTildes (comp, options) { - return comp.trim().split(/\s+/).map(function (comp) { - return replaceTilde(comp, options) - }).join(' ') -} + if (opts.noext !== true) { + const isExtglobChar = code === CHAR_PLUS + || code === CHAR_AT + || code === CHAR_ASTERISK + || code === CHAR_QUESTION_MARK + || code === CHAR_EXCLAMATION_MARK; -function replaceTilde (comp, options) { - var r = options.loose ? re[t.TILDELOOSE] : re[t.TILDE] - return comp.replace(r, function (_, M, m, p, pr) { - debug('tilde', comp, _, M, m, p, pr) - var ret + if (isExtglobChar === true && peek() === CHAR_LEFT_PARENTHESES) { + isGlob = token.isGlob = true; + isExtglob = token.isExtglob = true; + finished = true; + if (code === CHAR_EXCLAMATION_MARK && index === start) { + negatedExtglob = true; + } - if (isX(M)) { - ret = '' - } else if (isX(m)) { - ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' - } else if (isX(p)) { - // ~1.2 == >=1.2.0 <1.3.0 - ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' - } else if (pr) { - debug('replaceTilde pr', pr) - ret = '>=' + M + '.' + m + '.' + p + '-' + pr + - ' <' + M + '.' + (+m + 1) + '.0' - } else { - // ~1.2.3 == >=1.2.3 <1.3.0 - ret = '>=' + M + '.' + m + '.' + p + - ' <' + M + '.' + (+m + 1) + '.0' + if (scanToEnd === true) { + while (eos() !== true && (code = advance())) { + if (code === CHAR_BACKWARD_SLASH) { + backslashes = token.backslashes = true; + code = advance(); + continue; + } + + if (code === CHAR_RIGHT_PARENTHESES) { + isGlob = token.isGlob = true; + finished = true; + break; + } + } + continue; + } + break; + } } - debug('tilde return', ret) - return ret - }) -} + if (code === CHAR_ASTERISK) { + if (prev === CHAR_ASTERISK) isGlobstar = token.isGlobstar = true; + isGlob = token.isGlob = true; + finished = true; -// ^ --> * (any, kinda silly) -// ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0 -// ^2.0, ^2.0.x --> >=2.0.0 <3.0.0 -// ^1.2, ^1.2.x --> >=1.2.0 <2.0.0 -// ^1.2.3 --> >=1.2.3 <2.0.0 -// ^1.2.0 --> >=1.2.0 <2.0.0 -function replaceCarets (comp, options) { - return comp.trim().split(/\s+/).map(function (comp) { - return replaceCaret(comp, options) - }).join(' ') -} + if (scanToEnd === true) { + continue; + } + break; + } -function replaceCaret (comp, options) { - debug('caret', comp, options) - var r = options.loose ? re[t.CARETLOOSE] : re[t.CARET] - return comp.replace(r, function (_, M, m, p, pr) { - debug('caret', comp, _, M, m, p, pr) - var ret + if (code === CHAR_QUESTION_MARK) { + isGlob = token.isGlob = true; + finished = true; - if (isX(M)) { - ret = '' - } else if (isX(m)) { - ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' - } else if (isX(p)) { - if (M === '0') { - ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' - } else { - ret = '>=' + M + '.' + m + '.0 <' + (+M + 1) + '.0.0' + if (scanToEnd === true) { + continue; } - } else if (pr) { - debug('replaceCaret pr', pr) - if (M === '0') { - if (m === '0') { - ret = '>=' + M + '.' + m + '.' + p + '-' + pr + - ' <' + M + '.' + m + '.' + (+p + 1) - } else { - ret = '>=' + M + '.' + m + '.' + p + '-' + pr + - ' <' + M + '.' + (+m + 1) + '.0' + break; + } + + if (code === CHAR_LEFT_SQUARE_BRACKET) { + while (eos() !== true && (next = advance())) { + if (next === CHAR_BACKWARD_SLASH) { + backslashes = token.backslashes = true; + advance(); + continue; } - } else { - ret = '>=' + M + '.' + m + '.' + p + '-' + pr + - ' <' + (+M + 1) + '.0.0' - } - } else { - debug('no pr') - if (M === '0') { - if (m === '0') { - ret = '>=' + M + '.' + m + '.' + p + - ' <' + M + '.' + m + '.' + (+p + 1) - } else { - ret = '>=' + M + '.' + m + '.' + p + - ' <' + M + '.' + (+m + 1) + '.0' + + if (next === CHAR_RIGHT_SQUARE_BRACKET) { + isBracket = token.isBracket = true; + isGlob = token.isGlob = true; + finished = true; + break; } - } else { - ret = '>=' + M + '.' + m + '.' + p + - ' <' + (+M + 1) + '.0.0' } - } - - debug('caret return', ret) - return ret - }) -} -function replaceXRanges (comp, options) { - debug('replaceXRanges', comp, options) - return comp.split(/\s+/).map(function (comp) { - return replaceXRange(comp, options) - }).join(' ') -} + if (scanToEnd === true) { + continue; + } -function replaceXRange (comp, options) { - comp = comp.trim() - var r = options.loose ? re[t.XRANGELOOSE] : re[t.XRANGE] - return comp.replace(r, function (ret, gtlt, M, m, p, pr) { - debug('xRange', comp, ret, gtlt, M, m, p, pr) - var xM = isX(M) - var xm = xM || isX(m) - var xp = xm || isX(p) - var anyX = xp + break; + } - if (gtlt === '=' && anyX) { - gtlt = '' + if (opts.nonegate !== true && code === CHAR_EXCLAMATION_MARK && index === start) { + negated = token.negated = true; + start++; + continue; } - // if we're including prereleases in the match, then we need - // to fix this to -0, the lowest possible prerelease value - pr = options.includePrerelease ? '-0' : '' + if (opts.noparen !== true && code === CHAR_LEFT_PARENTHESES) { + isGlob = token.isGlob = true; - if (xM) { - if (gtlt === '>' || gtlt === '<') { - // nothing is allowed - ret = '<0.0.0-0' - } else { - // nothing is forbidden - ret = '*' - } - } else if (gtlt && anyX) { - // we know patch is an x, because we have any x at all. - // replace X with 0 - if (xm) { - m = 0 - } - p = 0 + if (scanToEnd === true) { + while (eos() !== true && (code = advance())) { + if (code === CHAR_LEFT_PARENTHESES) { + backslashes = token.backslashes = true; + code = advance(); + continue; + } - if (gtlt === '>') { - // >1 => >=2.0.0 - // >1.2 => >=1.3.0 - // >1.2.3 => >= 1.2.4 - gtlt = '>=' - if (xm) { - M = +M + 1 - m = 0 - p = 0 - } else { - m = +m + 1 - p = 0 - } - } else if (gtlt === '<=') { - // <=0.7.x is actually <0.8.0, since any 0.7.x should - // pass. Similarly, <=7.x is actually <8.0.0, etc. - gtlt = '<' - if (xm) { - M = +M + 1 - } else { - m = +m + 1 + if (code === CHAR_RIGHT_PARENTHESES) { + finished = true; + break; + } } + continue; } - - ret = gtlt + M + '.' + m + '.' + p + pr - } else if (xm) { - ret = '>=' + M + '.0.0' + pr + ' <' + (+M + 1) + '.0.0' + pr - } else if (xp) { - ret = '>=' + M + '.' + m + '.0' + pr + - ' <' + M + '.' + (+m + 1) + '.0' + pr + break; } - debug('xRange return', ret) - - return ret - }) -} + if (isGlob === true) { + finished = true; -// Because * is AND-ed with everything else in the comparator, -// and '' means "any version", just remove the *s entirely. -function replaceStars (comp, options) { - debug('replaceStars', comp, options) - // Looseness is ignored here. star is always as loose as it gets! - return comp.trim().replace(re[t.STAR], '') -} + if (scanToEnd === true) { + continue; + } -// This function is passed to string.replace(re[t.HYPHENRANGE]) -// M, m, patch, prerelease, build -// 1.2 - 3.4.5 => >=1.2.0 <=3.4.5 -// 1.2.3 - 3.4 => >=1.2.0 <3.5.0 Any 3.4.x will do -// 1.2 - 3.4 => >=1.2.0 <3.5.0 -function hyphenReplace ($0, - from, fM, fm, fp, fpr, fb, - to, tM, tm, tp, tpr, tb) { - if (isX(fM)) { - from = '' - } else if (isX(fm)) { - from = '>=' + fM + '.0.0' - } else if (isX(fp)) { - from = '>=' + fM + '.' + fm + '.0' - } else { - from = '>=' + from + break; + } } - if (isX(tM)) { - to = '' - } else if (isX(tm)) { - to = '<' + (+tM + 1) + '.0.0' - } else if (isX(tp)) { - to = '<' + tM + '.' + (+tm + 1) + '.0' - } else if (tpr) { - to = '<=' + tM + '.' + tm + '.' + tp + '-' + tpr - } else { - to = '<=' + to + if (opts.noext === true) { + isExtglob = false; + isGlob = false; } - return (from + ' ' + to).trim() -} + let base = str; + let prefix = ''; + let glob = ''; -// if ANY of the sets match ALL of its comparators, then pass -Range.prototype.test = function (version) { - if (!version) { - return false + if (start > 0) { + prefix = str.slice(0, start); + str = str.slice(start); + lastIndex -= start; } - if (typeof version === 'string') { - try { - version = new SemVer(version, this.options) - } catch (er) { - return false + if (base && isGlob === true && lastIndex > 0) { + base = str.slice(0, lastIndex); + glob = str.slice(lastIndex); + } else if (isGlob === true) { + base = ''; + glob = str; + } else { + base = str; + } + + if (base && base !== '' && base !== '/' && base !== str) { + if (isPathSeparator(base.charCodeAt(base.length - 1))) { + base = base.slice(0, -1); } } - for (var i = 0; i < this.set.length; i++) { - if (testSet(this.set[i], version, this.options)) { - return true + if (opts.unescape === true) { + if (glob) glob = utils.removeBackslashes(glob); + + if (base && backslashes === true) { + base = utils.removeBackslashes(base); } } - return false -} -function testSet (set, version, options) { - for (var i = 0; i < set.length; i++) { - if (!set[i].test(version)) { - return false + const state = { + prefix, + input, + start, + base, + glob, + isBrace, + isBracket, + isGlob, + isExtglob, + isGlobstar, + negated, + negatedExtglob + }; + + if (opts.tokens === true) { + state.maxDepth = 0; + if (!isPathSeparator(code)) { + tokens.push(token); } + state.tokens = tokens; } - if (version.prerelease.length && !options.includePrerelease) { - // Find the set of versions that are allowed to have prereleases - // For example, ^1.2.3-pr.1 desugars to >=1.2.3-pr.1 <2.0.0 - // That should allow `1.2.3-pr.2` to pass. - // However, `1.2.4-alpha.notready` should NOT be allowed, - // even though it's within the range set by the comparators. - for (i = 0; i < set.length; i++) { - debug(set[i].semver) - if (set[i].semver === ANY) { - continue - } + if (opts.parts === true || opts.tokens === true) { + let prevIndex; - if (set[i].semver.prerelease.length > 0) { - var allowed = set[i].semver - if (allowed.major === version.major && - allowed.minor === version.minor && - allowed.patch === version.patch) { - return true + for (let idx = 0; idx < slashes.length; idx++) { + const n = prevIndex ? prevIndex + 1 : start; + const i = slashes[idx]; + const value = input.slice(n, i); + if (opts.tokens) { + if (idx === 0 && start !== 0) { + tokens[idx].isPrefix = true; + tokens[idx].value = prefix; + } else { + tokens[idx].value = value; } + depth(tokens[idx]); + state.maxDepth += tokens[idx].depth; + } + if (idx !== 0 || value !== '') { + parts.push(value); } + prevIndex = i; } - // Version has a -pre, but it's not one of the ones we like. - return false - } - - return true -} - -exports.satisfies = satisfies -function satisfies (version, range, options) { - try { - range = new Range(range, options) - } catch (er) { - return false - } - return range.test(version) -} + if (prevIndex && prevIndex + 1 < input.length) { + const value = input.slice(prevIndex + 1); + parts.push(value); -exports.maxSatisfying = maxSatisfying -function maxSatisfying (versions, range, options) { - var max = null - var maxSV = null - try { - var rangeObj = new Range(range, options) - } catch (er) { - return null - } - versions.forEach(function (v) { - if (rangeObj.test(v)) { - // satisfies(v, range, options) - if (!max || maxSV.compare(v) === -1) { - // compare(max, v, true) - max = v - maxSV = new SemVer(max, options) + if (opts.tokens) { + tokens[tokens.length - 1].value = value; + depth(tokens[tokens.length - 1]); + state.maxDepth += tokens[tokens.length - 1].depth; } } - }) - return max -} -exports.minSatisfying = minSatisfying -function minSatisfying (versions, range, options) { - var min = null - var minSV = null - try { - var rangeObj = new Range(range, options) - } catch (er) { - return null + state.slashes = slashes; + state.parts = parts; } - versions.forEach(function (v) { - if (rangeObj.test(v)) { - // satisfies(v, range, options) - if (!min || minSV.compare(v) === 1) { - // compare(min, v, true) - min = v - minSV = new SemVer(min, options) - } - } - }) - return min -} -exports.minVersion = minVersion -function minVersion (range, loose) { - range = new Range(range, loose) + return state; +}; - var minver = new SemVer('0.0.0') - if (range.test(minver)) { - return minver +module.exports = scan; + + +/***/ }), +/* 787 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +const path = __webpack_require__(4); +const win32 = process.platform === 'win32'; +const { + REGEX_BACKSLASH, + REGEX_REMOVE_BACKSLASH, + REGEX_SPECIAL_CHARS, + REGEX_SPECIAL_CHARS_GLOBAL +} = __webpack_require__(788); + +exports.isObject = val => val !== null && typeof val === 'object' && !Array.isArray(val); +exports.hasRegexChars = str => REGEX_SPECIAL_CHARS.test(str); +exports.isRegexChar = str => str.length === 1 && exports.hasRegexChars(str); +exports.escapeRegex = str => str.replace(REGEX_SPECIAL_CHARS_GLOBAL, '\\$1'); +exports.toPosixSlashes = str => str.replace(REGEX_BACKSLASH, '/'); + +exports.removeBackslashes = str => { + return str.replace(REGEX_REMOVE_BACKSLASH, match => { + return match === '\\' ? '' : match; + }); +}; + +exports.supportsLookbehinds = () => { + const segs = process.version.slice(1).split('.').map(Number); + if (segs.length === 3 && segs[0] >= 9 || (segs[0] === 8 && segs[1] >= 10)) { + return true; } + return false; +}; - minver = new SemVer('0.0.0-0') - if (range.test(minver)) { - return minver +exports.isWindows = options => { + if (options && typeof options.windows === 'boolean') { + return options.windows; } + return win32 === true || path.sep === '\\'; +}; - minver = null - for (var i = 0; i < range.set.length; ++i) { - var comparators = range.set[i] +exports.escapeLast = (input, char, lastIdx) => { + const idx = input.lastIndexOf(char, lastIdx); + if (idx === -1) return input; + if (input[idx - 1] === '\\') return exports.escapeLast(input, char, idx - 1); + return `${input.slice(0, idx)}\\${input.slice(idx)}`; +}; - comparators.forEach(function (comparator) { - // Clone to avoid manipulating the comparator's semver object. - var compver = new SemVer(comparator.semver.version) - switch (comparator.operator) { - case '>': - if (compver.prerelease.length === 0) { - compver.patch++ - } else { - compver.prerelease.push(0) - } - compver.raw = compver.format() - /* fallthrough */ - case '': - case '>=': - if (!minver || gt(minver, compver)) { - minver = compver - } - break - case '<': - case '<=': - /* Ignore maximum versions */ - break - /* istanbul ignore next */ - default: - throw new Error('Unexpected operation: ' + comparator.operator) - } - }) +exports.removePrefix = (input, state = {}) => { + let output = input; + if (output.startsWith('./')) { + output = output.slice(2); + state.prefix = './'; } + return output; +}; - if (minver && range.test(minver)) { - return minver +exports.wrapOutput = (input, state = {}, options = {}) => { + const prepend = options.contains ? '' : '^'; + const append = options.contains ? '' : '$'; + + let output = `${prepend}(?:${input})${append}`; + if (state.negated === true) { + output = `(?:^(?!${output}).*$)`; } + return output; +}; - return null -} -exports.validRange = validRange -function validRange (range, options) { - try { - // Return '*' instead of '' so that truthiness works. - // This will throw if it's invalid anyway - return new Range(range, options).range || '*' - } catch (er) { - return null - } -} +/***/ }), +/* 788 */ +/***/ (function(module, exports, __webpack_require__) { -// Determine if version is less than all the versions possible in the range -exports.ltr = ltr -function ltr (version, range, options) { - return outside(version, range, '<', options) -} +"use strict"; -// Determine if version is greater than all the versions possible in the range. -exports.gtr = gtr -function gtr (version, range, options) { - return outside(version, range, '>', options) -} -exports.outside = outside -function outside (version, range, hilo, options) { - version = new SemVer(version, options) - range = new Range(range, options) +const path = __webpack_require__(4); +const WIN_SLASH = '\\\\/'; +const WIN_NO_SLASH = `[^${WIN_SLASH}]`; - var gtfn, ltefn, ltfn, comp, ecomp - switch (hilo) { - case '>': - gtfn = gt - ltefn = lte - ltfn = lt - comp = '>' - ecomp = '>=' - break - case '<': - gtfn = lt - ltefn = gte - ltfn = gt - comp = '<' - ecomp = '<=' - break - default: - throw new TypeError('Must provide a hilo val of "<" or ">"') - } +/** + * Posix glob regex + */ + +const DOT_LITERAL = '\\.'; +const PLUS_LITERAL = '\\+'; +const QMARK_LITERAL = '\\?'; +const SLASH_LITERAL = '\\/'; +const ONE_CHAR = '(?=.)'; +const QMARK = '[^/]'; +const END_ANCHOR = `(?:${SLASH_LITERAL}|$)`; +const START_ANCHOR = `(?:^|${SLASH_LITERAL})`; +const DOTS_SLASH = `${DOT_LITERAL}{1,2}${END_ANCHOR}`; +const NO_DOT = `(?!${DOT_LITERAL})`; +const NO_DOTS = `(?!${START_ANCHOR}${DOTS_SLASH})`; +const NO_DOT_SLASH = `(?!${DOT_LITERAL}{0,1}${END_ANCHOR})`; +const NO_DOTS_SLASH = `(?!${DOTS_SLASH})`; +const QMARK_NO_DOT = `[^.${SLASH_LITERAL}]`; +const STAR = `${QMARK}*?`; + +const POSIX_CHARS = { + DOT_LITERAL, + PLUS_LITERAL, + QMARK_LITERAL, + SLASH_LITERAL, + ONE_CHAR, + QMARK, + END_ANCHOR, + DOTS_SLASH, + NO_DOT, + NO_DOTS, + NO_DOT_SLASH, + NO_DOTS_SLASH, + QMARK_NO_DOT, + STAR, + START_ANCHOR +}; + +/** + * Windows glob regex + */ + +const WINDOWS_CHARS = { + ...POSIX_CHARS, + + SLASH_LITERAL: `[${WIN_SLASH}]`, + QMARK: WIN_NO_SLASH, + STAR: `${WIN_NO_SLASH}*?`, + DOTS_SLASH: `${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$)`, + NO_DOT: `(?!${DOT_LITERAL})`, + NO_DOTS: `(?!(?:^|[${WIN_SLASH}])${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`, + NO_DOT_SLASH: `(?!${DOT_LITERAL}{0,1}(?:[${WIN_SLASH}]|$))`, + NO_DOTS_SLASH: `(?!${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`, + QMARK_NO_DOT: `[^.${WIN_SLASH}]`, + START_ANCHOR: `(?:^|[${WIN_SLASH}])`, + END_ANCHOR: `(?:[${WIN_SLASH}]|$)` +}; + +/** + * POSIX Bracket Regex + */ + +const POSIX_REGEX_SOURCE = { + alnum: 'a-zA-Z0-9', + alpha: 'a-zA-Z', + ascii: '\\x00-\\x7F', + blank: ' \\t', + cntrl: '\\x00-\\x1F\\x7F', + digit: '0-9', + graph: '\\x21-\\x7E', + lower: 'a-z', + print: '\\x20-\\x7E ', + punct: '\\-!"#$%&\'()\\*+,./:;<=>?@[\\]^_`{|}~', + space: ' \\t\\r\\n\\v\\f', + upper: 'A-Z', + word: 'A-Za-z0-9_', + xdigit: 'A-Fa-f0-9' +}; + +module.exports = { + MAX_LENGTH: 1024 * 64, + POSIX_REGEX_SOURCE, + + // regular expressions + REGEX_BACKSLASH: /\\(?![*+?^${}(|)[\]])/g, + REGEX_NON_SPECIAL_CHARS: /^[^@![\].,$*+?^{}()|\\/]+/, + REGEX_SPECIAL_CHARS: /[-*+?.^${}(|)[\]]/, + REGEX_SPECIAL_CHARS_BACKREF: /(\\?)((\W)(\3*))/g, + REGEX_SPECIAL_CHARS_GLOBAL: /([-*+?.^${}(|)[\]])/g, + REGEX_REMOVE_BACKSLASH: /(?:\[.*?[^\\]\]|\\(?=.))/g, + + // Replace globs with equivalent patterns to reduce parsing time. + REPLACEMENTS: { + '***': '*', + '**/**': '**', + '**/**/**': '**' + }, + + // Digits + CHAR_0: 48, /* 0 */ + CHAR_9: 57, /* 9 */ + + // Alphabet chars. + CHAR_UPPERCASE_A: 65, /* A */ + CHAR_LOWERCASE_A: 97, /* a */ + CHAR_UPPERCASE_Z: 90, /* Z */ + CHAR_LOWERCASE_Z: 122, /* z */ + + CHAR_LEFT_PARENTHESES: 40, /* ( */ + CHAR_RIGHT_PARENTHESES: 41, /* ) */ + + CHAR_ASTERISK: 42, /* * */ + + // Non-alphabetic chars. + CHAR_AMPERSAND: 38, /* & */ + CHAR_AT: 64, /* @ */ + CHAR_BACKWARD_SLASH: 92, /* \ */ + CHAR_CARRIAGE_RETURN: 13, /* \r */ + CHAR_CIRCUMFLEX_ACCENT: 94, /* ^ */ + CHAR_COLON: 58, /* : */ + CHAR_COMMA: 44, /* , */ + CHAR_DOT: 46, /* . */ + CHAR_DOUBLE_QUOTE: 34, /* " */ + CHAR_EQUAL: 61, /* = */ + CHAR_EXCLAMATION_MARK: 33, /* ! */ + CHAR_FORM_FEED: 12, /* \f */ + CHAR_FORWARD_SLASH: 47, /* / */ + CHAR_GRAVE_ACCENT: 96, /* ` */ + CHAR_HASH: 35, /* # */ + CHAR_HYPHEN_MINUS: 45, /* - */ + CHAR_LEFT_ANGLE_BRACKET: 60, /* < */ + CHAR_LEFT_CURLY_BRACE: 123, /* { */ + CHAR_LEFT_SQUARE_BRACKET: 91, /* [ */ + CHAR_LINE_FEED: 10, /* \n */ + CHAR_NO_BREAK_SPACE: 160, /* \u00A0 */ + CHAR_PERCENT: 37, /* % */ + CHAR_PLUS: 43, /* + */ + CHAR_QUESTION_MARK: 63, /* ? */ + CHAR_RIGHT_ANGLE_BRACKET: 62, /* > */ + CHAR_RIGHT_CURLY_BRACE: 125, /* } */ + CHAR_RIGHT_SQUARE_BRACKET: 93, /* ] */ + CHAR_SEMICOLON: 59, /* ; */ + CHAR_SINGLE_QUOTE: 39, /* ' */ + CHAR_SPACE: 32, /* */ + CHAR_TAB: 9, /* \t */ + CHAR_UNDERSCORE: 95, /* _ */ + CHAR_VERTICAL_LINE: 124, /* | */ + CHAR_ZERO_WIDTH_NOBREAK_SPACE: 65279, /* \uFEFF */ + + SEP: path.sep, + + /** + * Create EXTGLOB_CHARS + */ + + extglobChars(chars) { + return { + '!': { type: 'negate', open: '(?:(?!(?:', close: `))${chars.STAR})` }, + '?': { type: 'qmark', open: '(?:', close: ')?' }, + '+': { type: 'plus', open: '(?:', close: ')+' }, + '*': { type: 'star', open: '(?:', close: ')*' }, + '@': { type: 'at', open: '(?:', close: ')' } + }; + }, - // If it satisifes the range it is not outside - if (satisfies(version, range, options)) { - return false + /** + * Create GLOB_CHARS + */ + + globChars(win32) { + return win32 === true ? WINDOWS_CHARS : POSIX_CHARS; } +}; - // From now on, variable terms are as if we're in "gtr" mode. - // but note that everything is flipped for the "ltr" function. - for (var i = 0; i < range.set.length; ++i) { - var comparators = range.set[i] +/***/ }), +/* 789 */ +/***/ (function(module, exports, __webpack_require__) { - var high = null - var low = null +"use strict"; - comparators.forEach(function (comparator) { - if (comparator.semver === ANY) { - comparator = new Comparator('>=0.0.0') - } - high = high || comparator - low = low || comparator - if (gtfn(comparator.semver, high.semver, options)) { - high = comparator - } else if (ltfn(comparator.semver, low.semver, options)) { - low = comparator - } - }) - // If the edge version comparator has a operator then our version - // isn't outside it - if (high.operator === comp || high.operator === ecomp) { - return false - } +const constants = __webpack_require__(788); +const utils = __webpack_require__(787); - // If the lowest version comparator has an operator and our version - // is less than it then it isn't higher than the range - if ((!low.operator || low.operator === comp) && - ltefn(version, low.semver)) { - return false - } else if (low.operator === ecomp && ltfn(version, low.semver)) { - return false - } - } - return true -} +/** + * Constants + */ -exports.prerelease = prerelease -function prerelease (version, options) { - var parsed = parse(version, options) - return (parsed && parsed.prerelease.length) ? parsed.prerelease : null -} +const { + MAX_LENGTH, + POSIX_REGEX_SOURCE, + REGEX_NON_SPECIAL_CHARS, + REGEX_SPECIAL_CHARS_BACKREF, + REPLACEMENTS +} = constants; -exports.intersects = intersects -function intersects (r1, r2, options) { - r1 = new Range(r1, options) - r2 = new Range(r2, options) - return r1.intersects(r2) -} +/** + * Helpers + */ -exports.coerce = coerce -function coerce (version, options) { - if (version instanceof SemVer) { - return version +const expandRange = (args, options) => { + if (typeof options.expandRange === 'function') { + return options.expandRange(...args, options); } - if (typeof version === 'number') { - version = String(version) - } + args.sort(); + const value = `[${args.join('-')}]`; - if (typeof version !== 'string') { - return null + try { + /* eslint-disable-next-line no-new */ + new RegExp(value); + } catch (ex) { + return args.map(v => utils.escapeRegex(v)).join('..'); } - options = options || {} + return value; +}; - var match = null - if (!options.rtl) { - match = version.match(re[t.COERCE]) - } else { - // Find the right-most coercible string that does not share - // a terminus with a more left-ward coercible string. - // Eg, '1.2.3.4' wants to coerce '2.3.4', not '3.4' or '4' - // - // Walk through the string checking with a /g regexp - // Manually set the index so as to pick up overlapping matches. - // Stop when we get a match that ends at the string end, since no - // coercible string can be more right-ward without the same terminus. - var next - while ((next = re[t.COERCERTL].exec(version)) && - (!match || match.index + match[0].length !== version.length) - ) { - if (!match || - next.index + next[0].length !== match.index + match[0].length) { - match = next - } - re[t.COERCERTL].lastIndex = next.index + next[1].length + next[2].length - } - // leave it in a clean state - re[t.COERCERTL].lastIndex = -1 +/** + * Create the message for a syntax error + */ + +const syntaxError = (type, char) => { + return `Missing ${type}: "${char}" - use "\\\\${char}" to match literal characters`; +}; + +/** + * Parse the given input string. + * @param {String} input + * @param {Object} options + * @return {Object} + */ + +const parse = (input, options) => { + if (typeof input !== 'string') { + throw new TypeError('Expected a string'); } - if (match === null) { - return null + input = REPLACEMENTS[input] || input; + + const opts = { ...options }; + const max = typeof opts.maxLength === 'number' ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH; + + let len = input.length; + if (len > max) { + throw new SyntaxError(`Input length: ${len}, exceeds maximum allowed length: ${max}`); } - return parse(match[2] + - '.' + (match[3] || '0') + - '.' + (match[4] || '0'), options) -} + const bos = { type: 'bos', value: '', output: opts.prepend || '' }; + const tokens = [bos]; + const capture = opts.capture ? '' : '?:'; + const win32 = utils.isWindows(options); -/***/ }), -/* 763 */ -/***/ (function(module, exports, __webpack_require__) { + // create constants based on platform, for windows or posix + const PLATFORM_CHARS = constants.globChars(win32); + const EXTGLOB_CHARS = constants.extglobChars(PLATFORM_CHARS); -"use strict"; + const { + DOT_LITERAL, + PLUS_LITERAL, + SLASH_LITERAL, + ONE_CHAR, + DOTS_SLASH, + NO_DOT, + NO_DOT_SLASH, + NO_DOTS_SLASH, + QMARK, + QMARK_NO_DOT, + STAR, + START_ANCHOR + } = PLATFORM_CHARS; -const EventEmitter = __webpack_require__(164); + const globstar = opts => { + return `(${capture}(?:(?!${START_ANCHOR}${opts.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`; + }; -const written = new WeakMap(); + const nodot = opts.dot ? '' : NO_DOT; + const qmarkNoDot = opts.dot ? QMARK : QMARK_NO_DOT; + let star = opts.bash === true ? globstar(opts) : STAR; -class ProgressEmitter extends EventEmitter { - constructor(source, destination) { - super(); - this._source = source; - this._destination = destination; - } + if (opts.capture) { + star = `(${star})`; + } - set written(value) { - written.set(this, value); - this.emitProgress(); - } + // minimatch options support + if (typeof opts.noext === 'boolean') { + opts.noextglob = opts.noext; + } - get written() { - return written.get(this); - } + const state = { + input, + index: -1, + start: 0, + dot: opts.dot === true, + consumed: '', + output: '', + prefix: '', + backtrack: false, + negated: false, + brackets: 0, + braces: 0, + parens: 0, + quotes: 0, + globstar: false, + tokens + }; - emitProgress() { - const {size, written} = this; - this.emit('progress', { - src: this._source, - dest: this._destination, - size, - written, - percent: written === size ? 1 : written / size - }); - } -} + input = utils.removePrefix(input, state); + len = input.length; -module.exports = ProgressEmitter; + const extglobs = []; + const braces = []; + const stack = []; + let prev = bos; + let value; + /** + * Tokenizing helpers + */ -/***/ }), -/* 764 */ -/***/ (function(module, exports, __webpack_require__) { + const eos = () => state.index === len - 1; + const peek = state.peek = (n = 1) => input[state.index + n]; + const advance = state.advance = () => input[++state.index] || ''; + const remaining = () => input.slice(state.index + 1); + const consume = (value = '', num = 0) => { + state.consumed += value; + state.index += num; + }; -"use strict"; + const append = token => { + state.output += token.output != null ? token.output : token.value; + consume(token.value); + }; + const negate = () => { + let count = 1; -const blacklist = [ - // # All - '^npm-debug\\.log$', // Error log for npm - '^\\..*\\.swp$', // Swap file for vim state + while (peek() === '!' && (peek(2) !== '(' || peek(3) === '?')) { + advance(); + state.start++; + count++; + } - // # macOS - '^\\.DS_Store$', // Stores custom folder attributes - '^\\.AppleDouble$', // Stores additional file resources - '^\\.LSOverride$', // Contains the absolute path to the app to be used - '^Icon\\r$', // Custom Finder icon: http://superuser.com/questions/298785/icon-file-on-os-x-desktop - '^\\._.*', // Thumbnail - '^\\.Spotlight-V100(?:$|\\/)', // Directory that might appear on external disk - '\\.Trashes', // File that might appear on external disk - '^__MACOSX$', // Resource fork + if (count % 2 === 0) { + return false; + } - // # Linux - '~$', // Backup file + state.negated = true; + state.start++; + return true; + }; - // # Windows - '^Thumbs\\.db$', // Image file cache - '^ehthumbs\\.db$', // Folder config file - '^Desktop\\.ini$', // Stores custom folder attributes - '@eaDir$' // Synology Diskstation "hidden" folder where the server stores thumbnails -]; + const increment = type => { + state[type]++; + stack.push(type); + }; -exports.re = () => { - throw new Error('`junk.re` was renamed to `junk.regex`'); -}; + const decrement = type => { + state[type]--; + stack.pop(); + }; -exports.regex = new RegExp(blacklist.join('|')); + /** + * Push tokens onto the tokens array. This helper speeds up + * tokenizing by 1) helping us avoid backtracking as much as possible, + * and 2) helping us avoid creating extra tokens when consecutive + * characters are plain text. This improves performance and simplifies + * lookbehinds. + */ -exports.is = filename => exports.regex.test(filename); + const push = tok => { + if (prev.type === 'globstar') { + const isBrace = state.braces > 0 && (tok.type === 'comma' || tok.type === 'brace'); + const isExtglob = tok.extglob === true || (extglobs.length && (tok.type === 'pipe' || tok.type === 'paren')); -exports.not = filename => !exports.is(filename); + if (tok.type !== 'slash' && tok.type !== 'paren' && !isBrace && !isExtglob) { + state.output = state.output.slice(0, -prev.output.length); + prev.type = 'star'; + prev.value = '*'; + prev.output = star; + state.output += prev.output; + } + } -// TODO: Remove this for the next major release -exports.default = module.exports; + if (extglobs.length && tok.type !== 'paren') { + extglobs[extglobs.length - 1].inner += tok.value; + } + if (tok.value || tok.output) append(tok); + if (prev && prev.type === 'text' && tok.type === 'text') { + prev.value += tok.value; + prev.output = (prev.output || '') + tok.value; + return; + } -/***/ }), -/* 765 */ -/***/ (function(module, exports, __webpack_require__) { + tok.prev = prev; + tokens.push(tok); + prev = tok; + }; -"use strict"; + const extglobOpen = (type, value) => { + const token = { ...EXTGLOB_CHARS[value], conditions: 1, inner: '' }; -const pMap = __webpack_require__(766); + token.prev = prev; + token.parens = state.parens; + token.output = state.output; + const output = (opts.capture ? '(' : '') + token.open; -const pFilter = async (iterable, filterer, options) => { - const values = await pMap( - iterable, - (element, index) => Promise.all([filterer(element, index), element]), - options - ); - return values.filter(value => Boolean(value[0])).map(value => value[1]); -}; + increment('parens'); + push({ type, value, output: state.output ? '' : ONE_CHAR }); + push({ type: 'paren', extglob: true, value: advance(), output }); + extglobs.push(token); + }; -module.exports = pFilter; -// TODO: Remove this for the next major release -module.exports.default = pFilter; + const extglobClose = token => { + let output = token.close + (opts.capture ? ')' : ''); + let rest; + if (token.type === 'negate') { + let extglobStar = star; -/***/ }), -/* 766 */ -/***/ (function(module, exports, __webpack_require__) { + if (token.inner && token.inner.length > 1 && token.inner.includes('/')) { + extglobStar = globstar(opts); + } -"use strict"; + if (extglobStar !== star || eos() || /^\)+$/.test(remaining())) { + output = token.close = `)$))${extglobStar}`; + } + if (token.inner.includes('*') && (rest = remaining()) && /^\.[^\\/.]+$/.test(rest)) { + output = token.close = `)${rest})${extglobStar})`; + } -const pMap = (iterable, mapper, options) => new Promise((resolve, reject) => { - options = Object.assign({ - concurrency: Infinity - }, options); + if (token.prev.type === 'bos') { + state.negatedExtglob = true; + } + } - if (typeof mapper !== 'function') { - throw new TypeError('Mapper function is required'); - } + push({ type: 'paren', extglob: true, value, output }); + decrement('parens'); + }; - const {concurrency} = options; + /** + * Fast paths + */ - if (!(typeof concurrency === 'number' && concurrency >= 1)) { - throw new TypeError(`Expected \`concurrency\` to be a number from 1 and up, got \`${concurrency}\` (${typeof concurrency})`); - } + if (opts.fastpaths !== false && !/(^[*!]|[/()[\]{}"])/.test(input)) { + let backslashes = false; - const ret = []; - const iterator = iterable[Symbol.iterator](); - let isRejected = false; - let isIterableDone = false; - let resolvingCount = 0; - let currentIndex = 0; + let output = input.replace(REGEX_SPECIAL_CHARS_BACKREF, (m, esc, chars, first, rest, index) => { + if (first === '\\') { + backslashes = true; + return m; + } - const next = () => { - if (isRejected) { - return; - } + if (first === '?') { + if (esc) { + return esc + first + (rest ? QMARK.repeat(rest.length) : ''); + } + if (index === 0) { + return qmarkNoDot + (rest ? QMARK.repeat(rest.length) : ''); + } + return QMARK.repeat(chars.length); + } - const nextItem = iterator.next(); - const i = currentIndex; - currentIndex++; + if (first === '.') { + return DOT_LITERAL.repeat(chars.length); + } - if (nextItem.done) { - isIterableDone = true; + if (first === '*') { + if (esc) { + return esc + first + (rest ? star : ''); + } + return star; + } + return esc ? m : `\\${m}`; + }); - if (resolvingCount === 0) { - resolve(ret); - } + if (backslashes === true) { + if (opts.unescape === true) { + output = output.replace(/\\/g, ''); + } else { + output = output.replace(/\\+/g, m => { + return m.length % 2 === 0 ? '\\\\' : (m ? '\\' : ''); + }); + } + } - return; - } + if (output === input && opts.contains === true) { + state.output = input; + return state; + } - resolvingCount++; + state.output = utils.wrapOutput(output, state, options); + return state; + } - Promise.resolve(nextItem.value) - .then(element => mapper(element, i)) - .then( - value => { - ret[i] = value; - resolvingCount--; - next(); - }, - error => { - isRejected = true; - reject(error); - } - ); - }; + /** + * Tokenize input until we reach end-of-string + */ - for (let i = 0; i < concurrency; i++) { - next(); + while (!eos()) { + value = advance(); - if (isIterableDone) { - break; - } - } -}); + if (value === '\u0000') { + continue; + } -module.exports = pMap; -// TODO: Remove this for the next major release -module.exports.default = pMap; + /** + * Escaped characters + */ + if (value === '\\') { + const next = peek(); -/***/ }), -/* 767 */ -/***/ (function(module, exports, __webpack_require__) { + if (next === '/' && opts.bash !== true) { + continue; + } -"use strict"; + if (next === '.' || next === ';') { + continue; + } -const NestedError = __webpack_require__(759); + if (!next) { + value += '\\'; + push({ type: 'text', value }); + continue; + } -class CpyError extends NestedError { - constructor(message, nested) { - super(message, nested); - Object.assign(this, nested); - this.name = 'CpyError'; - } -} + // collapse slashes to reduce potential for exploits + const match = /^\\+/.exec(remaining()); + let slashes = 0; -module.exports = CpyError; + if (match && match[0].length > 2) { + slashes = match[0].length; + state.index += slashes; + if (slashes % 2 !== 0) { + value += '\\'; + } + } + if (opts.unescape === true) { + value = advance(); + } else { + value += advance(); + } -/***/ }), -/* 768 */ -/***/ (function(module, exports, __webpack_require__) { + if (state.brackets === 0) { + push({ type: 'text', value }); + continue; + } + } -"use strict"; + /** + * If we're inside a regex character class, continue + * until we reach the closing bracket. + */ -const fs = __webpack_require__(132); -const arrayUnion = __webpack_require__(242); -const merge2 = __webpack_require__(243); -const fastGlob = __webpack_require__(769); -const dirGlob = __webpack_require__(326); -const gitignore = __webpack_require__(794); -const {FilterStream, UniqueStream} = __webpack_require__(795); + if (state.brackets > 0 && (value !== ']' || prev.value === '[' || prev.value === '[^')) { + if (opts.posix !== false && value === ':') { + const inner = prev.value.slice(1); + if (inner.includes('[')) { + prev.posix = true; -const DEFAULT_FILTER = () => false; + if (inner.includes(':')) { + const idx = prev.value.lastIndexOf('['); + const pre = prev.value.slice(0, idx); + const rest = prev.value.slice(idx + 2); + const posix = POSIX_REGEX_SOURCE[rest]; + if (posix) { + prev.value = pre + posix; + state.backtrack = true; + advance(); -const isNegative = pattern => pattern[0] === '!'; + if (!bos.output && tokens.indexOf(prev) === 1) { + bos.output = ONE_CHAR; + } + continue; + } + } + } + } -const assertPatternsInput = patterns => { - if (!patterns.every(pattern => typeof pattern === 'string')) { - throw new TypeError('Patterns must be a string or an array of strings'); - } -}; + if ((value === '[' && peek() !== ':') || (value === '-' && peek() === ']')) { + value = `\\${value}`; + } -const checkCwdOption = (options = {}) => { - if (!options.cwd) { - return; - } + if (value === ']' && (prev.value === '[' || prev.value === '[^')) { + value = `\\${value}`; + } - let stat; - try { - stat = fs.statSync(options.cwd); - } catch { - return; - } + if (opts.posix === true && value === '!' && prev.value === '[') { + value = '^'; + } - if (!stat.isDirectory()) { - throw new Error('The `cwd` option must be a path to a directory'); - } -}; + prev.value += value; + append({ value }); + continue; + } -const getPathString = p => p.stats instanceof fs.Stats ? p.path : p; + /** + * If we're inside a quoted string, continue + * until we reach the closing double quote. + */ -const generateGlobTasks = (patterns, taskOptions) => { - patterns = arrayUnion([].concat(patterns)); - assertPatternsInput(patterns); - checkCwdOption(taskOptions); + if (state.quotes === 1 && value !== '"') { + value = utils.escapeRegex(value); + prev.value += value; + append({ value }); + continue; + } - const globTasks = []; + /** + * Double quotes + */ - taskOptions = { - ignore: [], - expandDirectories: true, - ...taskOptions - }; + if (value === '"') { + state.quotes = state.quotes === 1 ? 0 : 1; + if (opts.keepQuotes === true) { + push({ type: 'text', value }); + } + continue; + } - for (const [index, pattern] of patterns.entries()) { - if (isNegative(pattern)) { - continue; - } + /** + * Parentheses + */ - const ignore = patterns - .slice(index) - .filter(pattern => isNegative(pattern)) - .map(pattern => pattern.slice(1)); + if (value === '(') { + increment('parens'); + push({ type: 'paren', value }); + continue; + } - const options = { - ...taskOptions, - ignore: taskOptions.ignore.concat(ignore) - }; + if (value === ')') { + if (state.parens === 0 && opts.strictBrackets === true) { + throw new SyntaxError(syntaxError('opening', '(')); + } - globTasks.push({pattern, options}); - } + const extglob = extglobs[extglobs.length - 1]; + if (extglob && state.parens === extglob.parens + 1) { + extglobClose(extglobs.pop()); + continue; + } - return globTasks; -}; + push({ type: 'paren', value, output: state.parens ? ')' : '\\)' }); + decrement('parens'); + continue; + } -const globDirs = (task, fn) => { - let options = {}; - if (task.options.cwd) { - options.cwd = task.options.cwd; - } + /** + * Square brackets + */ - if (Array.isArray(task.options.expandDirectories)) { - options = { - ...options, - files: task.options.expandDirectories - }; - } else if (typeof task.options.expandDirectories === 'object') { - options = { - ...options, - ...task.options.expandDirectories - }; - } + if (value === '[') { + if (opts.nobracket === true || !remaining().includes(']')) { + if (opts.nobracket !== true && opts.strictBrackets === true) { + throw new SyntaxError(syntaxError('closing', ']')); + } - return fn(task.pattern, options); -}; + value = `\\${value}`; + } else { + increment('brackets'); + } -const getPattern = (task, fn) => task.options.expandDirectories ? globDirs(task, fn) : [task.pattern]; + push({ type: 'bracket', value }); + continue; + } -const getFilterSync = options => { - return options && options.gitignore ? - gitignore.sync({cwd: options.cwd, ignore: options.ignore}) : - DEFAULT_FILTER; -}; + if (value === ']') { + if (opts.nobracket === true || (prev && prev.type === 'bracket' && prev.value.length === 1)) { + push({ type: 'text', value, output: `\\${value}` }); + continue; + } -const globToTask = task => glob => { - const {options} = task; - if (options.ignore && Array.isArray(options.ignore) && options.expandDirectories) { - options.ignore = dirGlob.sync(options.ignore); - } + if (state.brackets === 0) { + if (opts.strictBrackets === true) { + throw new SyntaxError(syntaxError('opening', '[')); + } - return { - pattern: glob, - options - }; -}; + push({ type: 'text', value, output: `\\${value}` }); + continue; + } -module.exports = async (patterns, options) => { - const globTasks = generateGlobTasks(patterns, options); + decrement('brackets'); - const getFilter = async () => { - return options && options.gitignore ? - gitignore({cwd: options.cwd, ignore: options.ignore}) : - DEFAULT_FILTER; - }; + const prevValue = prev.value.slice(1); + if (prev.posix !== true && prevValue[0] === '^' && !prevValue.includes('/')) { + value = `/${value}`; + } - const getTasks = async () => { - const tasks = await Promise.all(globTasks.map(async task => { - const globs = await getPattern(task, dirGlob); - return Promise.all(globs.map(globToTask(task))); - })); + prev.value += value; + append({ value }); - return arrayUnion(...tasks); - }; + // when literal brackets are explicitly disabled + // assume we should match with a regex character class + if (opts.literalBrackets === false || utils.hasRegexChars(prevValue)) { + continue; + } - const [filter, tasks] = await Promise.all([getFilter(), getTasks()]); - const paths = await Promise.all(tasks.map(task => fastGlob(task.pattern, task.options))); + const escaped = utils.escapeRegex(prev.value); + state.output = state.output.slice(0, -prev.value.length); - return arrayUnion(...paths).filter(path_ => !filter(getPathString(path_))); -}; + // when literal brackets are explicitly enabled + // assume we should escape the brackets to match literal characters + if (opts.literalBrackets === true) { + state.output += escaped; + prev.value = escaped; + continue; + } -module.exports.sync = (patterns, options) => { - const globTasks = generateGlobTasks(patterns, options); + // when the user specifies nothing, try to match both + prev.value = `(${capture}${escaped}|${prev.value})`; + state.output += prev.value; + continue; + } - const tasks = []; - for (const task of globTasks) { - const newTask = getPattern(task, dirGlob.sync).map(globToTask(task)); - tasks.push(...newTask); - } + /** + * Braces + */ - const filter = getFilterSync(options); + if (value === '{' && opts.nobrace !== true) { + increment('braces'); - let matches = []; - for (const task of tasks) { - matches = arrayUnion(matches, fastGlob.sync(task.pattern, task.options)); - } + const open = { + type: 'brace', + value, + output: '(', + outputIndex: state.output.length, + tokensIndex: state.tokens.length + }; - return matches.filter(path_ => !filter(path_)); -}; + braces.push(open); + push(open); + continue; + } -module.exports.stream = (patterns, options) => { - const globTasks = generateGlobTasks(patterns, options); + if (value === '}') { + const brace = braces[braces.length - 1]; - const tasks = []; - for (const task of globTasks) { - const newTask = getPattern(task, dirGlob.sync).map(globToTask(task)); - tasks.push(...newTask); - } + if (opts.nobrace === true || !brace) { + push({ type: 'text', value, output: value }); + continue; + } - const filter = getFilterSync(options); - const filterStream = new FilterStream(p => !filter(p)); - const uniqueStream = new UniqueStream(); + let output = ')'; - return merge2(tasks.map(task => fastGlob.stream(task.pattern, task.options))) - .pipe(filterStream) - .pipe(uniqueStream); -}; + if (brace.dots === true) { + const arr = tokens.slice(); + const range = []; -module.exports.generateGlobTasks = generateGlobTasks; + for (let i = arr.length - 1; i >= 0; i--) { + tokens.pop(); + if (arr[i].type === 'brace') { + break; + } + if (arr[i].type !== 'dots') { + range.unshift(arr[i].value); + } + } -module.exports.hasMagic = (patterns, options) => [] - .concat(patterns) - .some(pattern => fastGlob.isDynamicPattern(pattern, options)); + output = expandRange(range, opts); + state.backtrack = true; + } -module.exports.gitignore = gitignore; + if (brace.comma !== true && brace.dots !== true) { + const out = state.output.slice(0, brace.outputIndex); + const toks = state.tokens.slice(brace.tokensIndex); + brace.value = brace.output = '\\{'; + value = output = '\\}'; + state.output = out; + for (const t of toks) { + state.output += (t.output || t.value); + } + } + push({ type: 'brace', value, output }); + decrement('braces'); + braces.pop(); + continue; + } -/***/ }), -/* 769 */ -/***/ (function(module, exports, __webpack_require__) { + /** + * Pipes + */ -"use strict"; - -const taskManager = __webpack_require__(770); -const async_1 = __webpack_require__(780); -const stream_1 = __webpack_require__(790); -const sync_1 = __webpack_require__(791); -const settings_1 = __webpack_require__(793); -const utils = __webpack_require__(771); -async function FastGlob(source, options) { - assertPatternsInput(source); - const works = getWorks(source, async_1.default, options); - const result = await Promise.all(works); - return utils.array.flatten(result); -} -// https://github.com/typescript-eslint/typescript-eslint/issues/60 -// eslint-disable-next-line no-redeclare -(function (FastGlob) { - function sync(source, options) { - assertPatternsInput(source); - const works = getWorks(source, sync_1.default, options); - return utils.array.flatten(works); - } - FastGlob.sync = sync; - function stream(source, options) { - assertPatternsInput(source); - const works = getWorks(source, stream_1.default, options); - /** - * The stream returned by the provider cannot work with an asynchronous iterator. - * To support asynchronous iterators, regardless of the number of tasks, we always multiplex streams. - * This affects performance (+25%). I don't see best solution right now. - */ - return utils.stream.merge(works); - } - FastGlob.stream = stream; - function generateTasks(source, options) { - assertPatternsInput(source); - const patterns = [].concat(source); - const settings = new settings_1.default(options); - return taskManager.generate(patterns, settings); - } - FastGlob.generateTasks = generateTasks; - function isDynamicPattern(source, options) { - assertPatternsInput(source); - const settings = new settings_1.default(options); - return utils.pattern.isDynamicPattern(source, settings); - } - FastGlob.isDynamicPattern = isDynamicPattern; - function escapePath(source) { - assertPatternsInput(source); - return utils.path.escape(source); - } - FastGlob.escapePath = escapePath; -})(FastGlob || (FastGlob = {})); -function getWorks(source, _Provider, options) { - const patterns = [].concat(source); - const settings = new settings_1.default(options); - const tasks = taskManager.generate(patterns, settings); - const provider = new _Provider(settings); - return tasks.map(provider.read, provider); -} -function assertPatternsInput(input) { - const source = [].concat(input); - const isValidSource = source.every((item) => utils.string.isString(item) && !utils.string.isEmpty(item)); - if (!isValidSource) { - throw new TypeError('Patterns must be a string (non empty) or an array of strings'); - } -} -module.exports = FastGlob; + if (value === '|') { + if (extglobs.length > 0) { + extglobs[extglobs.length - 1].conditions++; + } + push({ type: 'text', value }); + continue; + } + /** + * Commas + */ -/***/ }), -/* 770 */ -/***/ (function(module, exports, __webpack_require__) { + if (value === ',') { + let output = value; -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.convertPatternGroupToTask = exports.convertPatternGroupsToTasks = exports.groupPatternsByBaseDirectory = exports.getNegativePatternsAsPositive = exports.getPositivePatterns = exports.convertPatternsToTasks = exports.generate = void 0; -const utils = __webpack_require__(771); -function generate(patterns, settings) { - const positivePatterns = getPositivePatterns(patterns); - const negativePatterns = getNegativePatternsAsPositive(patterns, settings.ignore); - const staticPatterns = positivePatterns.filter((pattern) => utils.pattern.isStaticPattern(pattern, settings)); - const dynamicPatterns = positivePatterns.filter((pattern) => utils.pattern.isDynamicPattern(pattern, settings)); - const staticTasks = convertPatternsToTasks(staticPatterns, negativePatterns, /* dynamic */ false); - const dynamicTasks = convertPatternsToTasks(dynamicPatterns, negativePatterns, /* dynamic */ true); - return staticTasks.concat(dynamicTasks); -} -exports.generate = generate; -function convertPatternsToTasks(positive, negative, dynamic) { - const positivePatternsGroup = groupPatternsByBaseDirectory(positive); - // When we have a global group – there is no reason to divide the patterns into independent tasks. - // In this case, the global task covers the rest. - if ('.' in positivePatternsGroup) { - const task = convertPatternGroupToTask('.', positive, negative, dynamic); - return [task]; - } - return convertPatternGroupsToTasks(positivePatternsGroup, negative, dynamic); -} -exports.convertPatternsToTasks = convertPatternsToTasks; -function getPositivePatterns(patterns) { - return utils.pattern.getPositivePatterns(patterns); -} -exports.getPositivePatterns = getPositivePatterns; -function getNegativePatternsAsPositive(patterns, ignore) { - const negative = utils.pattern.getNegativePatterns(patterns).concat(ignore); - const positive = negative.map(utils.pattern.convertToPositivePattern); - return positive; -} -exports.getNegativePatternsAsPositive = getNegativePatternsAsPositive; -function groupPatternsByBaseDirectory(patterns) { - const group = {}; - return patterns.reduce((collection, pattern) => { - const base = utils.pattern.getBaseDirectory(pattern); - if (base in collection) { - collection[base].push(pattern); - } - else { - collection[base] = [pattern]; - } - return collection; - }, group); -} -exports.groupPatternsByBaseDirectory = groupPatternsByBaseDirectory; -function convertPatternGroupsToTasks(positive, negative, dynamic) { - return Object.keys(positive).map((base) => { - return convertPatternGroupToTask(base, positive[base], negative, dynamic); - }); -} -exports.convertPatternGroupsToTasks = convertPatternGroupsToTasks; -function convertPatternGroupToTask(base, positive, negative, dynamic) { - return { - dynamic, - positive, - negative, - base, - patterns: [].concat(positive, negative.map(utils.pattern.convertToNegativePattern)) - }; -} -exports.convertPatternGroupToTask = convertPatternGroupToTask; + const brace = braces[braces.length - 1]; + if (brace && stack[stack.length - 1] === 'braces') { + brace.comma = true; + output = '|'; + } + push({ type: 'comma', value, output }); + continue; + } -/***/ }), -/* 771 */ -/***/ (function(module, exports, __webpack_require__) { + /** + * Slashes + */ -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.string = exports.stream = exports.pattern = exports.path = exports.fs = exports.errno = exports.array = void 0; -const array = __webpack_require__(772); -exports.array = array; -const errno = __webpack_require__(773); -exports.errno = errno; -const fs = __webpack_require__(774); -exports.fs = fs; -const path = __webpack_require__(775); -exports.path = path; -const pattern = __webpack_require__(776); -exports.pattern = pattern; -const stream = __webpack_require__(778); -exports.stream = stream; -const string = __webpack_require__(779); -exports.string = string; + if (value === '/') { + // if the beginning of the glob is "./", advance the start + // to the current index, and don't add the "./" characters + // to the state. This greatly simplifies lookbehinds when + // checking for BOS characters like "!" and "." (not "./") + if (prev.type === 'dot' && state.index === state.start + 1) { + state.start = state.index + 1; + state.consumed = ''; + state.output = ''; + tokens.pop(); + prev = bos; // reset "prev" to the first token + continue; + } + push({ type: 'slash', value, output: SLASH_LITERAL }); + continue; + } -/***/ }), -/* 772 */ -/***/ (function(module, exports, __webpack_require__) { + /** + * Dots + */ -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.splitWhen = exports.flatten = void 0; -function flatten(items) { - return items.reduce((collection, item) => [].concat(collection, item), []); -} -exports.flatten = flatten; -function splitWhen(items, predicate) { - const result = [[]]; - let groupIndex = 0; - for (const item of items) { - if (predicate(item)) { - groupIndex++; - result[groupIndex] = []; - } - else { - result[groupIndex].push(item); - } - } - return result; -} -exports.splitWhen = splitWhen; + if (value === '.') { + if (state.braces > 0 && prev.type === 'dot') { + if (prev.value === '.') prev.output = DOT_LITERAL; + const brace = braces[braces.length - 1]; + prev.type = 'dots'; + prev.output += value; + prev.value += value; + brace.dots = true; + continue; + } + if ((state.braces + state.parens) === 0 && prev.type !== 'bos' && prev.type !== 'slash') { + push({ type: 'text', value, output: DOT_LITERAL }); + continue; + } -/***/ }), -/* 773 */ -/***/ (function(module, exports, __webpack_require__) { + push({ type: 'dot', value, output: DOT_LITERAL }); + continue; + } -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.isEnoentCodeError = void 0; -function isEnoentCodeError(error) { - return error.code === 'ENOENT'; -} -exports.isEnoentCodeError = isEnoentCodeError; + /** + * Question marks + */ + if (value === '?') { + const isGroup = prev && prev.value === '('; + if (!isGroup && opts.noextglob !== true && peek() === '(' && peek(2) !== '?') { + extglobOpen('qmark', value); + continue; + } -/***/ }), -/* 774 */ -/***/ (function(module, exports, __webpack_require__) { + if (prev && prev.type === 'paren') { + const next = peek(); + let output = value; -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.createDirentFromStats = void 0; -class DirentFromStats { - constructor(name, stats) { - this.name = name; - this.isBlockDevice = stats.isBlockDevice.bind(stats); - this.isCharacterDevice = stats.isCharacterDevice.bind(stats); - this.isDirectory = stats.isDirectory.bind(stats); - this.isFIFO = stats.isFIFO.bind(stats); - this.isFile = stats.isFile.bind(stats); - this.isSocket = stats.isSocket.bind(stats); - this.isSymbolicLink = stats.isSymbolicLink.bind(stats); - } -} -function createDirentFromStats(name, stats) { - return new DirentFromStats(name, stats); -} -exports.createDirentFromStats = createDirentFromStats; + if (next === '<' && !utils.supportsLookbehinds()) { + throw new Error('Node.js v10 or higher is required for regex lookbehinds'); + } + if ((prev.value === '(' && !/[!=<:]/.test(next)) || (next === '<' && !/<([!=]|\w+>)/.test(remaining()))) { + output = `\\${value}`; + } -/***/ }), -/* 775 */ -/***/ (function(module, exports, __webpack_require__) { + push({ type: 'text', value, output }); + continue; + } -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.removeLeadingDotSegment = exports.escape = exports.makeAbsolute = exports.unixify = void 0; -const path = __webpack_require__(4); -const LEADING_DOT_SEGMENT_CHARACTERS_COUNT = 2; // ./ or .\\ -const UNESCAPED_GLOB_SYMBOLS_RE = /(\\?)([()*?[\]{|}]|^!|[!+@](?=\())/g; -/** - * Designed to work only with simple paths: `dir\\file`. - */ -function unixify(filepath) { - return filepath.replace(/\\/g, '/'); -} -exports.unixify = unixify; -function makeAbsolute(cwd, filepath) { - return path.resolve(cwd, filepath); -} -exports.makeAbsolute = makeAbsolute; -function escape(pattern) { - return pattern.replace(UNESCAPED_GLOB_SYMBOLS_RE, '\\$2'); -} -exports.escape = escape; -function removeLeadingDotSegment(entry) { - // We do not use `startsWith` because this is 10x slower than current implementation for some cases. - // eslint-disable-next-line @typescript-eslint/prefer-string-starts-ends-with - if (entry.charAt(0) === '.') { - const secondCharactery = entry.charAt(1); - if (secondCharactery === '/' || secondCharactery === '\\') { - return entry.slice(LEADING_DOT_SEGMENT_CHARACTERS_COUNT); - } - } - return entry; -} -exports.removeLeadingDotSegment = removeLeadingDotSegment; + if (opts.dot !== true && (prev.type === 'slash' || prev.type === 'bos')) { + push({ type: 'qmark', value, output: QMARK_NO_DOT }); + continue; + } + push({ type: 'qmark', value, output: QMARK }); + continue; + } -/***/ }), -/* 776 */ -/***/ (function(module, exports, __webpack_require__) { + /** + * Exclamation + */ -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.matchAny = exports.convertPatternsToRe = exports.makeRe = exports.getPatternParts = exports.expandBraceExpansion = exports.expandPatternsWithBraceExpansion = exports.isAffectDepthOfReadingPattern = exports.endsWithSlashGlobStar = exports.hasGlobStar = exports.getBaseDirectory = exports.getPositivePatterns = exports.getNegativePatterns = exports.isPositivePattern = exports.isNegativePattern = exports.convertToNegativePattern = exports.convertToPositivePattern = exports.isDynamicPattern = exports.isStaticPattern = void 0; -const path = __webpack_require__(4); -const globParent = __webpack_require__(265); -const micromatch = __webpack_require__(777); -const picomatch = __webpack_require__(279); -const GLOBSTAR = '**'; -const ESCAPE_SYMBOL = '\\'; -const COMMON_GLOB_SYMBOLS_RE = /[*?]|^!/; -const REGEX_CHARACTER_CLASS_SYMBOLS_RE = /\[.*]/; -const REGEX_GROUP_SYMBOLS_RE = /(?:^|[^!*+?@])\(.*\|.*\)/; -const GLOB_EXTENSION_SYMBOLS_RE = /[!*+?@]\(.*\)/; -const BRACE_EXPANSIONS_SYMBOLS_RE = /{.*(?:,|\.\.).*}/; -function isStaticPattern(pattern, options = {}) { - return !isDynamicPattern(pattern, options); -} -exports.isStaticPattern = isStaticPattern; -function isDynamicPattern(pattern, options = {}) { - /** - * A special case with an empty string is necessary for matching patterns that start with a forward slash. - * An empty string cannot be a dynamic pattern. - * For example, the pattern `/lib/*` will be spread into parts: '', 'lib', '*'. - */ - if (pattern === '') { - return false; - } - /** - * When the `caseSensitiveMatch` option is disabled, all patterns must be marked as dynamic, because we cannot check - * filepath directly (without read directory). - */ - if (options.caseSensitiveMatch === false || pattern.includes(ESCAPE_SYMBOL)) { - return true; - } - if (COMMON_GLOB_SYMBOLS_RE.test(pattern) || REGEX_CHARACTER_CLASS_SYMBOLS_RE.test(pattern) || REGEX_GROUP_SYMBOLS_RE.test(pattern)) { - return true; - } - if (options.extglob !== false && GLOB_EXTENSION_SYMBOLS_RE.test(pattern)) { - return true; - } - if (options.braceExpansion !== false && BRACE_EXPANSIONS_SYMBOLS_RE.test(pattern)) { - return true; - } - return false; -} -exports.isDynamicPattern = isDynamicPattern; -function convertToPositivePattern(pattern) { - return isNegativePattern(pattern) ? pattern.slice(1) : pattern; -} -exports.convertToPositivePattern = convertToPositivePattern; -function convertToNegativePattern(pattern) { - return '!' + pattern; -} -exports.convertToNegativePattern = convertToNegativePattern; -function isNegativePattern(pattern) { - return pattern.startsWith('!') && pattern[1] !== '('; -} -exports.isNegativePattern = isNegativePattern; -function isPositivePattern(pattern) { - return !isNegativePattern(pattern); -} -exports.isPositivePattern = isPositivePattern; -function getNegativePatterns(patterns) { - return patterns.filter(isNegativePattern); -} -exports.getNegativePatterns = getNegativePatterns; -function getPositivePatterns(patterns) { - return patterns.filter(isPositivePattern); -} -exports.getPositivePatterns = getPositivePatterns; -function getBaseDirectory(pattern) { - return globParent(pattern, { flipBackslashes: false }); -} -exports.getBaseDirectory = getBaseDirectory; -function hasGlobStar(pattern) { - return pattern.includes(GLOBSTAR); -} -exports.hasGlobStar = hasGlobStar; -function endsWithSlashGlobStar(pattern) { - return pattern.endsWith('/' + GLOBSTAR); -} -exports.endsWithSlashGlobStar = endsWithSlashGlobStar; -function isAffectDepthOfReadingPattern(pattern) { - const basename = path.basename(pattern); - return endsWithSlashGlobStar(pattern) || isStaticPattern(basename); -} -exports.isAffectDepthOfReadingPattern = isAffectDepthOfReadingPattern; -function expandPatternsWithBraceExpansion(patterns) { - return patterns.reduce((collection, pattern) => { - return collection.concat(expandBraceExpansion(pattern)); - }, []); -} -exports.expandPatternsWithBraceExpansion = expandPatternsWithBraceExpansion; -function expandBraceExpansion(pattern) { - return micromatch.braces(pattern, { - expand: true, - nodupes: true - }); -} -exports.expandBraceExpansion = expandBraceExpansion; -function getPatternParts(pattern, options) { - let { parts } = picomatch.scan(pattern, Object.assign(Object.assign({}, options), { parts: true })); - /** - * The scan method returns an empty array in some cases. - * See micromatch/picomatch#58 for more details. - */ - if (parts.length === 0) { - parts = [pattern]; - } - /** - * The scan method does not return an empty part for the pattern with a forward slash. - * This is another part of micromatch/picomatch#58. - */ - if (parts[0].startsWith('/')) { - parts[0] = parts[0].slice(1); - parts.unshift(''); - } - return parts; -} -exports.getPatternParts = getPatternParts; -function makeRe(pattern, options) { - return micromatch.makeRe(pattern, options); -} -exports.makeRe = makeRe; -function convertPatternsToRe(patterns, options) { - return patterns.map((pattern) => makeRe(pattern, options)); -} -exports.convertPatternsToRe = convertPatternsToRe; -function matchAny(entry, patternsRe) { - return patternsRe.some((patternRe) => patternRe.test(entry)); -} -exports.matchAny = matchAny; + if (value === '!') { + if (opts.noextglob !== true && peek() === '(') { + if (peek(2) !== '?' || !/[!=<:]/.test(peek(3))) { + extglobOpen('negate', value); + continue; + } + } + if (opts.nonegate !== true && state.index === 0) { + negate(); + continue; + } + } -/***/ }), -/* 777 */ -/***/ (function(module, exports, __webpack_require__) { + /** + * Plus + */ -"use strict"; + if (value === '+') { + if (opts.noextglob !== true && peek() === '(' && peek(2) !== '?') { + extglobOpen('plus', value); + continue; + } + if ((prev && prev.value === '(') || opts.regex === false) { + push({ type: 'plus', value, output: PLUS_LITERAL }); + continue; + } -const util = __webpack_require__(113); -const braces = __webpack_require__(269); -const picomatch = __webpack_require__(279); -const utils = __webpack_require__(282); -const isEmptyString = val => typeof val === 'string' && (val === '' || val === './'); + if ((prev && (prev.type === 'bracket' || prev.type === 'paren' || prev.type === 'brace')) || state.parens > 0) { + push({ type: 'plus', value }); + continue; + } -/** - * Returns an array of strings that match one or more glob patterns. - * - * ```js - * const mm = require('micromatch'); - * // mm(list, patterns[, options]); - * - * console.log(mm(['a.js', 'a.txt'], ['*.js'])); - * //=> [ 'a.js' ] - * ``` - * @param {String|Array} list List of strings to match. - * @param {String|Array} patterns One or more glob patterns to use for matching. - * @param {Object} options See available [options](#options) - * @return {Array} Returns an array of matches - * @summary false - * @api public - */ + push({ type: 'plus', value: PLUS_LITERAL }); + continue; + } -const micromatch = (list, patterns, options) => { - patterns = [].concat(patterns); - list = [].concat(list); + /** + * Plain text + */ - let omit = new Set(); - let keep = new Set(); - let items = new Set(); - let negatives = 0; + if (value === '@') { + if (opts.noextglob !== true && peek() === '(' && peek(2) !== '?') { + push({ type: 'at', extglob: true, value, output: '' }); + continue; + } - let onResult = state => { - items.add(state.output); - if (options && options.onResult) { - options.onResult(state); + push({ type: 'text', value }); + continue; } - }; - - for (let i = 0; i < patterns.length; i++) { - let isMatch = picomatch(String(patterns[i]), { ...options, onResult }, true); - let negated = isMatch.state.negated || isMatch.state.negatedExtglob; - if (negated) negatives++; - for (let item of list) { - let matched = isMatch(item, true); + /** + * Plain text + */ - let match = negated ? !matched.isMatch : matched.isMatch; - if (!match) continue; + if (value !== '*') { + if (value === '$' || value === '^') { + value = `\\${value}`; + } - if (negated) { - omit.add(matched.output); - } else { - omit.delete(matched.output); - keep.add(matched.output); + const match = REGEX_NON_SPECIAL_CHARS.exec(remaining()); + if (match) { + value += match[0]; + state.index += match[0].length; } + + push({ type: 'text', value }); + continue; } - } - let result = negatives === patterns.length ? [...items] : [...keep]; - let matches = result.filter(item => !omit.has(item)); + /** + * Stars + */ - if (options && matches.length === 0) { - if (options.failglob === true) { - throw new Error(`No matches found for "${patterns.join(', ')}"`); + if (prev && (prev.type === 'globstar' || prev.star === true)) { + prev.type = 'star'; + prev.star = true; + prev.value += value; + prev.output = star; + state.backtrack = true; + state.globstar = true; + consume(value); + continue; } - if (options.nonull === true || options.nullglob === true) { - return options.unescape ? patterns.map(p => p.replace(/\\/g, '')) : patterns; + let rest = remaining(); + if (opts.noextglob !== true && /^\([^?]/.test(rest)) { + extglobOpen('star', value); + continue; } - } - return matches; -}; + if (prev.type === 'star') { + if (opts.noglobstar === true) { + consume(value); + continue; + } + + const prior = prev.prev; + const before = prior.prev; + const isStart = prior.type === 'slash' || prior.type === 'bos'; + const afterStar = before && (before.type === 'star' || before.type === 'globstar'); + + if (opts.bash === true && (!isStart || (rest[0] && rest[0] !== '/'))) { + push({ type: 'star', value, output: '' }); + continue; + } + + const isBrace = state.braces > 0 && (prior.type === 'comma' || prior.type === 'brace'); + const isExtglob = extglobs.length && (prior.type === 'pipe' || prior.type === 'paren'); + if (!isStart && prior.type !== 'paren' && !isBrace && !isExtglob) { + push({ type: 'star', value, output: '' }); + continue; + } + + // strip consecutive `/**/` + while (rest.slice(0, 3) === '/**') { + const after = input[state.index + 4]; + if (after && after !== '/') { + break; + } + rest = rest.slice(3); + consume('/**', 3); + } + + if (prior.type === 'bos' && eos()) { + prev.type = 'globstar'; + prev.value += value; + prev.output = globstar(opts); + state.output = prev.output; + state.globstar = true; + consume(value); + continue; + } + + if (prior.type === 'slash' && prior.prev.type !== 'bos' && !afterStar && eos()) { + state.output = state.output.slice(0, -(prior.output + prev.output).length); + prior.output = `(?:${prior.output}`; + + prev.type = 'globstar'; + prev.output = globstar(opts) + (opts.strictSlashes ? ')' : '|$)'); + prev.value += value; + state.globstar = true; + state.output += prior.output + prev.output; + consume(value); + continue; + } + + if (prior.type === 'slash' && prior.prev.type !== 'bos' && rest[0] === '/') { + const end = rest[1] !== void 0 ? '|$' : ''; + + state.output = state.output.slice(0, -(prior.output + prev.output).length); + prior.output = `(?:${prior.output}`; + + prev.type = 'globstar'; + prev.output = `${globstar(opts)}${SLASH_LITERAL}|${SLASH_LITERAL}${end})`; + prev.value += value; + + state.output += prior.output + prev.output; + state.globstar = true; -/** - * Backwards compatibility - */ + consume(value + advance()); -micromatch.match = micromatch; + push({ type: 'slash', value: '/', output: '' }); + continue; + } -/** - * Returns a matcher function from the given glob `pattern` and `options`. - * The returned function takes a string to match as its only argument and returns - * true if the string is a match. - * - * ```js - * const mm = require('micromatch'); - * // mm.matcher(pattern[, options]); - * - * const isMatch = mm.matcher('*.!(*a)'); - * console.log(isMatch('a.a')); //=> false - * console.log(isMatch('a.b')); //=> true - * ``` - * @param {String} `pattern` Glob pattern - * @param {Object} `options` - * @return {Function} Returns a matcher function. - * @api public - */ + if (prior.type === 'bos' && rest[0] === '/') { + prev.type = 'globstar'; + prev.value += value; + prev.output = `(?:^|${SLASH_LITERAL}|${globstar(opts)}${SLASH_LITERAL})`; + state.output = prev.output; + state.globstar = true; + consume(value + advance()); + push({ type: 'slash', value: '/', output: '' }); + continue; + } -micromatch.matcher = (pattern, options) => picomatch(pattern, options); + // remove single star from output + state.output = state.output.slice(0, -prev.output.length); -/** - * Returns true if **any** of the given glob `patterns` match the specified `string`. - * - * ```js - * const mm = require('micromatch'); - * // mm.isMatch(string, patterns[, options]); - * - * console.log(mm.isMatch('a.a', ['b.*', '*.a'])); //=> true - * console.log(mm.isMatch('a.a', 'b.*')); //=> false - * ``` - * @param {String} str The string to test. - * @param {String|Array} patterns One or more glob patterns to use for matching. - * @param {Object} [options] See available [options](#options). - * @return {Boolean} Returns true if any patterns match `str` - * @api public - */ + // reset previous token to globstar + prev.type = 'globstar'; + prev.output = globstar(opts); + prev.value += value; -micromatch.isMatch = (str, patterns, options) => picomatch(patterns, options)(str); + // reset output with globstar + state.output += prev.output; + state.globstar = true; + consume(value); + continue; + } -/** - * Backwards compatibility - */ + const token = { type: 'star', value, output: star }; -micromatch.any = micromatch.isMatch; + if (opts.bash === true) { + token.output = '.*?'; + if (prev.type === 'bos' || prev.type === 'slash') { + token.output = nodot + token.output; + } + push(token); + continue; + } -/** - * Returns a list of strings that _**do not match any**_ of the given `patterns`. - * - * ```js - * const mm = require('micromatch'); - * // mm.not(list, patterns[, options]); - * - * console.log(mm.not(['a.a', 'b.b', 'c.c'], '*.a')); - * //=> ['b.b', 'c.c'] - * ``` - * @param {Array} `list` Array of strings to match. - * @param {String|Array} `patterns` One or more glob pattern to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Array} Returns an array of strings that **do not match** the given patterns. - * @api public - */ + if (prev && (prev.type === 'bracket' || prev.type === 'paren') && opts.regex === true) { + token.output = value; + push(token); + continue; + } -micromatch.not = (list, patterns, options = {}) => { - patterns = [].concat(patterns).map(String); - let result = new Set(); - let items = []; + if (state.index === state.start || prev.type === 'slash' || prev.type === 'dot') { + if (prev.type === 'dot') { + state.output += NO_DOT_SLASH; + prev.output += NO_DOT_SLASH; - let onResult = state => { - if (options.onResult) options.onResult(state); - items.push(state.output); - }; + } else if (opts.dot === true) { + state.output += NO_DOTS_SLASH; + prev.output += NO_DOTS_SLASH; - let matches = micromatch(list, patterns, { ...options, onResult }); + } else { + state.output += nodot; + prev.output += nodot; + } - for (let item of items) { - if (!matches.includes(item)) { - result.add(item); + if (peek() !== '*') { + state.output += ONE_CHAR; + prev.output += ONE_CHAR; + } } - } - return [...result]; -}; -/** - * Returns true if the given `string` contains the given pattern. Similar - * to [.isMatch](#isMatch) but the pattern can match any part of the string. - * - * ```js - * var mm = require('micromatch'); - * // mm.contains(string, pattern[, options]); - * - * console.log(mm.contains('aa/bb/cc', '*b')); - * //=> true - * console.log(mm.contains('aa/bb/cc', '*d')); - * //=> false - * ``` - * @param {String} `str` The string to match. - * @param {String|Array} `patterns` Glob pattern to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Boolean} Returns true if the patter matches any part of `str`. - * @api public - */ + push(token); + } -micromatch.contains = (str, pattern, options) => { - if (typeof str !== 'string') { - throw new TypeError(`Expected a string: "${util.inspect(str)}"`); + while (state.brackets > 0) { + if (opts.strictBrackets === true) throw new SyntaxError(syntaxError('closing', ']')); + state.output = utils.escapeLast(state.output, '['); + decrement('brackets'); } - if (Array.isArray(pattern)) { - return pattern.some(p => micromatch.contains(str, p, options)); + while (state.parens > 0) { + if (opts.strictBrackets === true) throw new SyntaxError(syntaxError('closing', ')')); + state.output = utils.escapeLast(state.output, '('); + decrement('parens'); } - if (typeof pattern === 'string') { - if (isEmptyString(str) || isEmptyString(pattern)) { - return false; - } + while (state.braces > 0) { + if (opts.strictBrackets === true) throw new SyntaxError(syntaxError('closing', '}')); + state.output = utils.escapeLast(state.output, '{'); + decrement('braces'); + } - if (str.includes(pattern) || (str.startsWith('./') && str.slice(2).includes(pattern))) { - return true; - } + if (opts.strictSlashes !== true && (prev.type === 'star' || prev.type === 'bracket')) { + push({ type: 'maybe_slash', value: '', output: `${SLASH_LITERAL}?` }); } - return micromatch.isMatch(str, pattern, { ...options, contains: true }); -}; + // rebuild the output if we had to backtrack at any point + if (state.backtrack === true) { + state.output = ''; -/** - * Filter the keys of the given object with the given `glob` pattern - * and `options`. Does not attempt to match nested keys. If you need this feature, - * use [glob-object][] instead. - * - * ```js - * const mm = require('micromatch'); - * // mm.matchKeys(object, patterns[, options]); - * - * const obj = { aa: 'a', ab: 'b', ac: 'c' }; - * console.log(mm.matchKeys(obj, '*b')); - * //=> { ab: 'b' } - * ``` - * @param {Object} `object` The object with keys to filter. - * @param {String|Array} `patterns` One or more glob patterns to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Object} Returns an object with only keys that match the given patterns. - * @api public - */ + for (const token of state.tokens) { + state.output += token.output != null ? token.output : token.value; -micromatch.matchKeys = (obj, patterns, options) => { - if (!utils.isObject(obj)) { - throw new TypeError('Expected the first argument to be an object'); + if (token.suffix) { + state.output += token.suffix; + } + } } - let keys = micromatch(Object.keys(obj), patterns, options); - let res = {}; - for (let key of keys) res[key] = obj[key]; - return res; + + return state; }; /** - * Returns true if some of the strings in the given `list` match any of the given glob `patterns`. - * - * ```js - * const mm = require('micromatch'); - * // mm.some(list, patterns[, options]); - * - * console.log(mm.some(['foo.js', 'bar.js'], ['*.js', '!foo.js'])); - * // true - * console.log(mm.some(['foo.js'], ['*.js', '!foo.js'])); - * // false - * ``` - * @param {String|Array} `list` The string or array of strings to test. Returns as soon as the first match is found. - * @param {String|Array} `patterns` One or more glob patterns to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Boolean} Returns true if any patterns match `str` - * @api public + * Fast paths for creating regular expressions for common glob patterns. + * This can significantly speed up processing and has very little downside + * impact when none of the fast paths match. */ -micromatch.some = (list, patterns, options) => { - let items = [].concat(list); - - for (let pattern of [].concat(patterns)) { - let isMatch = picomatch(String(pattern), options); - if (items.some(item => isMatch(item))) { - return true; - } +parse.fastpaths = (input, options) => { + const opts = { ...options }; + const max = typeof opts.maxLength === 'number' ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH; + const len = input.length; + if (len > max) { + throw new SyntaxError(`Input length: ${len}, exceeds maximum allowed length: ${max}`); } - return false; -}; -/** - * Returns true if every string in the given `list` matches - * any of the given glob `patterns`. - * - * ```js - * const mm = require('micromatch'); - * // mm.every(list, patterns[, options]); - * - * console.log(mm.every('foo.js', ['foo.js'])); - * // true - * console.log(mm.every(['foo.js', 'bar.js'], ['*.js'])); - * // true - * console.log(mm.every(['foo.js', 'bar.js'], ['*.js', '!foo.js'])); - * // false - * console.log(mm.every(['foo.js'], ['*.js', '!foo.js'])); - * // false - * ``` - * @param {String|Array} `list` The string or array of strings to test. - * @param {String|Array} `patterns` One or more glob patterns to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Boolean} Returns true if any patterns match `str` - * @api public - */ + input = REPLACEMENTS[input] || input; + const win32 = utils.isWindows(options); -micromatch.every = (list, patterns, options) => { - let items = [].concat(list); + // create constants based on platform, for windows or posix + const { + DOT_LITERAL, + SLASH_LITERAL, + ONE_CHAR, + DOTS_SLASH, + NO_DOT, + NO_DOTS, + NO_DOTS_SLASH, + STAR, + START_ANCHOR + } = constants.globChars(win32); - for (let pattern of [].concat(patterns)) { - let isMatch = picomatch(String(pattern), options); - if (!items.every(item => isMatch(item))) { - return false; - } + const nodot = opts.dot ? NO_DOTS : NO_DOT; + const slashDot = opts.dot ? NO_DOTS_SLASH : NO_DOT; + const capture = opts.capture ? '' : '?:'; + const state = { negated: false, prefix: '' }; + let star = opts.bash === true ? '.*?' : STAR; + + if (opts.capture) { + star = `(${star})`; } - return true; -}; -/** - * Returns true if **all** of the given `patterns` match - * the specified string. - * - * ```js - * const mm = require('micromatch'); - * // mm.all(string, patterns[, options]); - * - * console.log(mm.all('foo.js', ['foo.js'])); - * // true - * - * console.log(mm.all('foo.js', ['*.js', '!foo.js'])); - * // false - * - * console.log(mm.all('foo.js', ['*.js', 'foo.js'])); - * // true - * - * console.log(mm.all('foo.js', ['*.js', 'f*', '*o*', '*o.js'])); - * // true - * ``` - * @param {String|Array} `str` The string to test. - * @param {String|Array} `patterns` One or more glob patterns to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Boolean} Returns true if any patterns match `str` - * @api public - */ + const globstar = opts => { + if (opts.noglobstar === true) return star; + return `(${capture}(?:(?!${START_ANCHOR}${opts.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`; + }; -micromatch.all = (str, patterns, options) => { - if (typeof str !== 'string') { - throw new TypeError(`Expected a string: "${util.inspect(str)}"`); - } + const create = str => { + switch (str) { + case '*': + return `${nodot}${ONE_CHAR}${star}`; - return [].concat(patterns).every(p => picomatch(p, options)(str)); -}; + case '.*': + return `${DOT_LITERAL}${ONE_CHAR}${star}`; -/** - * Returns an array of matches captured by `pattern` in `string, or `null` if the pattern did not match. - * - * ```js - * const mm = require('micromatch'); - * // mm.capture(pattern, string[, options]); - * - * console.log(mm.capture('test/*.js', 'test/foo.js')); - * //=> ['foo'] - * console.log(mm.capture('test/*.js', 'foo/bar.css')); - * //=> null - * ``` - * @param {String} `glob` Glob pattern to use for matching. - * @param {String} `input` String to match - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Boolean} Returns an array of captures if the input matches the glob pattern, otherwise `null`. - * @api public - */ + case '*.*': + return `${nodot}${star}${DOT_LITERAL}${ONE_CHAR}${star}`; -micromatch.capture = (glob, input, options) => { - let posix = utils.isWindows(options); - let regex = picomatch.makeRe(String(glob), { ...options, capture: true }); - let match = regex.exec(posix ? utils.toPosixSlashes(input) : input); + case '*/*': + return `${nodot}${star}${SLASH_LITERAL}${ONE_CHAR}${slashDot}${star}`; - if (match) { - return match.slice(1).map(v => v === void 0 ? '' : v); - } -}; + case '**': + return nodot + globstar(opts); -/** - * Create a regular expression from the given glob `pattern`. - * - * ```js - * const mm = require('micromatch'); - * // mm.makeRe(pattern[, options]); - * - * console.log(mm.makeRe('*.js')); - * //=> /^(?:(\.[\\\/])?(?!\.)(?=.)[^\/]*?\.js)$/ - * ``` - * @param {String} `pattern` A glob pattern to convert to regex. - * @param {Object} `options` - * @return {RegExp} Returns a regex created from the given pattern. - * @api public - */ + case '**/*': + return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${slashDot}${ONE_CHAR}${star}`; -micromatch.makeRe = (...args) => picomatch.makeRe(...args); + case '**/*.*': + return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${slashDot}${star}${DOT_LITERAL}${ONE_CHAR}${star}`; -/** - * Scan a glob pattern to separate the pattern into segments. Used - * by the [split](#split) method. - * - * ```js - * const mm = require('micromatch'); - * const state = mm.scan(pattern[, options]); - * ``` - * @param {String} `pattern` - * @param {Object} `options` - * @return {Object} Returns an object with - * @api public - */ + case '**/.*': + return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${DOT_LITERAL}${ONE_CHAR}${star}`; -micromatch.scan = (...args) => picomatch.scan(...args); + default: { + const match = /^(.*?)\.(\w+)$/.exec(str); + if (!match) return; -/** - * Parse a glob pattern to create the source string for a regular - * expression. - * - * ```js - * const mm = require('micromatch'); - * const state = mm(pattern[, options]); - * ``` - * @param {String} `glob` - * @param {Object} `options` - * @return {Object} Returns an object with useful properties and output to be used as regex source string. - * @api public - */ + const source = create(match[1]); + if (!source) return; -micromatch.parse = (patterns, options) => { - let res = []; - for (let pattern of [].concat(patterns || [])) { - for (let str of braces(String(pattern), options)) { - res.push(picomatch.parse(str, options)); + return source + DOT_LITERAL + match[2]; + } } - } - return res; -}; + }; -/** - * Process the given brace `pattern`. - * - * ```js - * const { braces } = require('micromatch'); - * console.log(braces('foo/{a,b,c}/bar')); - * //=> [ 'foo/(a|b|c)/bar' ] - * - * console.log(braces('foo/{a,b,c}/bar', { expand: true })); - * //=> [ 'foo/a/bar', 'foo/b/bar', 'foo/c/bar' ] - * ``` - * @param {String} `pattern` String with brace pattern to process. - * @param {Object} `options` Any [options](#options) to change how expansion is performed. See the [braces][] library for all available options. - * @return {Array} - * @api public - */ + const output = utils.removePrefix(input, state); + let source = create(output); -micromatch.braces = (pattern, options) => { - if (typeof pattern !== 'string') throw new TypeError('Expected a string'); - if ((options && options.nobrace === true) || !/\{.*\}/.test(pattern)) { - return [pattern]; + if (source && opts.strictSlashes !== true) { + source += `${SLASH_LITERAL}?`; } - return braces(pattern, options); -}; -/** - * Expand braces - */ - -micromatch.braceExpand = (pattern, options) => { - if (typeof pattern !== 'string') throw new TypeError('Expected a string'); - return micromatch.braces(pattern, { ...options, expand: true }); + return source; }; -/** - * Expose micromatch - */ - -module.exports = micromatch; +module.exports = parse; /***/ }), -/* 778 */ +/* 790 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -88967,7 +93177,7 @@ function propagateCloseEventToSources(streams) { /***/ }), -/* 779 */ +/* 791 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -88985,14 +93195,14 @@ exports.isEmpty = isEmpty; /***/ }), -/* 780 */ +/* 792 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const stream_1 = __webpack_require__(781); -const provider_1 = __webpack_require__(783); +const stream_1 = __webpack_require__(793); +const provider_1 = __webpack_require__(795); class ProviderAsync extends provider_1.default { constructor() { super(...arguments); @@ -89020,16 +93230,16 @@ exports.default = ProviderAsync; /***/ }), -/* 781 */ +/* 793 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const stream_1 = __webpack_require__(173); -const fsStat = __webpack_require__(289); -const fsWalk = __webpack_require__(294); -const reader_1 = __webpack_require__(782); +const fsStat = __webpack_require__(295); +const fsWalk = __webpack_require__(300); +const reader_1 = __webpack_require__(794); class ReaderStream extends reader_1.default { constructor() { super(...arguments); @@ -89082,15 +93292,15 @@ exports.default = ReaderStream; /***/ }), -/* 782 */ +/* 794 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = __webpack_require__(4); -const fsStat = __webpack_require__(289); -const utils = __webpack_require__(771); +const fsStat = __webpack_require__(295); +const utils = __webpack_require__(777); class Reader { constructor(_settings) { this._settings = _settings; @@ -89122,17 +93332,17 @@ exports.default = Reader; /***/ }), -/* 783 */ +/* 795 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = __webpack_require__(4); -const deep_1 = __webpack_require__(784); -const entry_1 = __webpack_require__(787); -const error_1 = __webpack_require__(788); -const entry_2 = __webpack_require__(789); +const deep_1 = __webpack_require__(796); +const entry_1 = __webpack_require__(799); +const error_1 = __webpack_require__(800); +const entry_2 = __webpack_require__(801); class Provider { constructor(_settings) { this._settings = _settings; @@ -89177,14 +93387,14 @@ exports.default = Provider; /***/ }), -/* 784 */ +/* 796 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(771); -const partial_1 = __webpack_require__(785); +const utils = __webpack_require__(777); +const partial_1 = __webpack_require__(797); class DeepFilter { constructor(_settings, _micromatchOptions) { this._settings = _settings; @@ -89246,13 +93456,13 @@ exports.default = DeepFilter; /***/ }), -/* 785 */ +/* 797 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const matcher_1 = __webpack_require__(786); +const matcher_1 = __webpack_require__(798); class PartialMatcher extends matcher_1.default { match(filepath) { const parts = filepath.split('/'); @@ -89291,13 +93501,13 @@ exports.default = PartialMatcher; /***/ }), -/* 786 */ +/* 798 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(771); +const utils = __webpack_require__(777); class Matcher { constructor(_patterns, _settings, _micromatchOptions) { this._patterns = _patterns; @@ -89348,13 +93558,13 @@ exports.default = Matcher; /***/ }), -/* 787 */ +/* 799 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(771); +const utils = __webpack_require__(777); class EntryFilter { constructor(_settings, _micromatchOptions) { this._settings = _settings; @@ -89411,13 +93621,13 @@ exports.default = EntryFilter; /***/ }), -/* 788 */ +/* 800 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(771); +const utils = __webpack_require__(777); class ErrorFilter { constructor(_settings) { this._settings = _settings; @@ -89433,13 +93643,13 @@ exports.default = ErrorFilter; /***/ }), -/* 789 */ +/* 801 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(771); +const utils = __webpack_require__(777); class EntryTransformer { constructor(_settings) { this._settings = _settings; @@ -89466,15 +93676,15 @@ exports.default = EntryTransformer; /***/ }), -/* 790 */ +/* 802 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const stream_1 = __webpack_require__(173); -const stream_2 = __webpack_require__(781); -const provider_1 = __webpack_require__(783); +const stream_2 = __webpack_require__(793); +const provider_1 = __webpack_require__(795); class ProviderStream extends provider_1.default { constructor() { super(...arguments); @@ -89504,14 +93714,14 @@ exports.default = ProviderStream; /***/ }), -/* 791 */ +/* 803 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const sync_1 = __webpack_require__(792); -const provider_1 = __webpack_require__(783); +const sync_1 = __webpack_require__(804); +const provider_1 = __webpack_require__(795); class ProviderSync extends provider_1.default { constructor() { super(...arguments); @@ -89534,15 +93744,15 @@ exports.default = ProviderSync; /***/ }), -/* 792 */ +/* 804 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsStat = __webpack_require__(289); -const fsWalk = __webpack_require__(294); -const reader_1 = __webpack_require__(782); +const fsStat = __webpack_require__(295); +const fsWalk = __webpack_require__(300); +const reader_1 = __webpack_require__(794); class ReaderSync extends reader_1.default { constructor() { super(...arguments); @@ -89584,7 +93794,7 @@ exports.default = ReaderSync; /***/ }), -/* 793 */ +/* 805 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -89648,7 +93858,7 @@ exports.default = Settings; /***/ }), -/* 794 */ +/* 806 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -89656,9 +93866,9 @@ exports.default = Settings; const {promisify} = __webpack_require__(113); const fs = __webpack_require__(132); const path = __webpack_require__(4); -const fastGlob = __webpack_require__(769); -const gitIgnore = __webpack_require__(329); -const slash = __webpack_require__(330); +const fastGlob = __webpack_require__(775); +const gitIgnore = __webpack_require__(335); +const slash = __webpack_require__(336); const DEFAULT_IGNORE = [ '**/node_modules/**', @@ -89775,7 +93985,7 @@ module.exports.sync = options => { /***/ }), -/* 795 */ +/* 807 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -89828,7 +94038,7 @@ module.exports = { /***/ }), -/* 796 */ +/* 808 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -89836,17 +94046,17 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "buildNonBazelProductionProjects", function() { return buildNonBazelProductionProjects; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getProductionProjects", function() { return getProductionProjects; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "buildProject", function() { return buildProject; }); -/* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(556); +/* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(562); /* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(cpy__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(240); /* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(del__WEBPACK_IMPORTED_MODULE_1__); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(553); +/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(559); /* harmony import */ var _utils_fs__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(231); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(220); -/* harmony import */ var _utils_package_json__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(343); -/* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(340); +/* harmony import */ var _utils_package_json__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(349); +/* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(346); /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License diff --git a/yarn.lock b/yarn.lock index 44bc48206b2b4..a9ee92713bb57 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20186,15 +20186,7 @@ micromatch@3.1.10, micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4: snapdragon "^0.8.1" to-regex "^3.0.2" -micromatch@^4.0.0, micromatch@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" - integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== - dependencies: - braces "^3.0.1" - picomatch "^2.0.5" - -micromatch@^4.0.4: +micromatch@^4.0.0, micromatch@^4.0.2, micromatch@^4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== @@ -22361,7 +22353,7 @@ phin@^2.9.1: resolved "https://registry.yarnpkg.com/phin/-/phin-2.9.3.tgz#f9b6ac10a035636fb65dfc576aaaa17b8743125c" integrity sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA== -picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1: +picomatch@^2.0.4, picomatch@^2.2.1: version "2.2.2" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== From ee3c0c447ebc5a18277ee51d62dc8ccb7ff1f93f Mon Sep 17 00:00:00 2001 From: Corey Robertson Date: Wed, 20 Oct 2021 19:11:43 -0400 Subject: [PATCH 169/204] Handle view/edit mode in control group (#115867) --- .../__stories__/input_controls.stories.tsx | 61 +++++++++++++++-- .../component/control_group_component.tsx | 67 +++++++++++-------- .../component/control_group_sortable_item.tsx | 10 +-- .../controls/control_group/types.ts | 4 +- 4 files changed, 102 insertions(+), 40 deletions(-) diff --git a/src/plugins/presentation_util/public/components/controls/__stories__/input_controls.stories.tsx b/src/plugins/presentation_util/public/components/controls/__stories__/input_controls.stories.tsx index f984b7c996a03..ec1678c5faa96 100644 --- a/src/plugins/presentation_util/public/components/controls/__stories__/input_controls.stories.tsx +++ b/src/plugins/presentation_util/public/components/controls/__stories__/input_controls.stories.tsx @@ -6,8 +6,10 @@ * Side Public License, v 1. */ -import React, { useEffect, useMemo } from 'react'; +import React, { useEffect, useMemo, useState, useCallback, FC } from 'react'; import uuid from 'uuid'; +import { EuiFlexGroup, EuiFlexItem, EuiSwitch, EuiTextAlign } from '@elastic/eui'; +import useEffectOnce from 'react-use/lib/useEffectOnce'; import { decorators } from './decorators'; import { pluginServices, registry } from '../../../services/storybook'; @@ -18,6 +20,7 @@ import { OptionsListEmbeddableInput, OPTIONS_LIST_CONTROL, } from '../control_types/options_list/options_list_embeddable'; +import { ViewMode } from '../control_group/types'; export default { title: 'Controls', @@ -25,13 +28,31 @@ export default { decorators, }; -const EmptyControlGroupStoryComponent = ({ panels }: { panels?: ControlsPanels }) => { +type UnwrapPromise = T extends Promise ? P : T; +type EmbeddableType = UnwrapPromise>; + +const EmptyControlGroupStoryComponent: FC<{ + panels?: ControlsPanels; + edit?: boolean; +}> = ({ panels, edit }) => { const embeddableRoot: React.RefObject = useMemo(() => React.createRef(), []); + const [embeddable, setEmbeddable] = useState(); + const [viewMode, setViewMode] = useState( + edit === undefined || edit ? ViewMode.EDIT : ViewMode.VIEW + ); + + const handleToggleViewMode = useCallback(() => { + if (embeddable) { + const newViewMode = + embeddable.getInput().viewMode === ViewMode.EDIT ? ViewMode.VIEW : ViewMode.EDIT; + embeddable.updateInput({ viewMode: newViewMode }); + } + }, [embeddable]); pluginServices.setRegistry(registry.start({})); populateStorybookControlFactories(pluginServices.getServices().controls); - useEffect(() => { + useEffectOnce(() => { (async () => { const factory = new ControlGroupContainerFactory(); const controlGroupContainerEmbeddable = await factory.create({ @@ -43,17 +64,45 @@ const EmptyControlGroupStoryComponent = ({ panels }: { panels?: ControlsPanels } controlStyle: 'oneLine', panels: panels ?? {}, id: uuid.v4(), + viewMode, }); + if (controlGroupContainerEmbeddable && embeddableRoot.current) { controlGroupContainerEmbeddable.render(embeddableRoot.current); } + setEmbeddable(controlGroupContainerEmbeddable); })(); - }, [embeddableRoot, panels]); + }); + + useEffect(() => { + if (embeddable) { + const subscription = embeddable.getInput$().subscribe((updatedInput) => { + if (updatedInput.viewMode) { + setViewMode(updatedInput.viewMode); + } + }); + + return () => subscription.unsubscribe(); + } + }, [embeddable, setViewMode]); + + return ( + <> + + + + + + + +
- return
; +
+ + ); }; -export const EmptyControlGroupStory = () => ; +export const EmptyControlGroupStory = () => ; export const ConfiguredControlGroupStory = () => ( { const dispatch = useEmbeddableDispatch(); // current state - const { panels, controlStyle } = useEmbeddableSelector((state) => state); + const { panels, viewMode, controlStyle } = useEmbeddableSelector((state) => state); + + const isEditable = viewMode === ViewMode.EDIT; const idsInOrder = useMemo( () => @@ -100,6 +102,10 @@ export const ControlGroup = () => { }; const emptyState = !(idsInOrder && idsInOrder.length > 0); + // Empty, non-editable view is null + if (!isEditable && emptyState) { + return null; + } return ( { (controlId, index) => panels[controlId] && ( { - - - - - { - const flyoutInstance = openFlyout( - forwardAllContext( - flyoutInstance.close()} />, - reduxContainerContext - ) - ); - }} - /> - - - - - - - - - + {isEditable && ( + + + + + { + const flyoutInstance = openFlyout( + forwardAllContext( + flyoutInstance.close()} />, + reduxContainerContext + ) + ); + }} + /> + + + + + + + + + + )} ) : ( <> diff --git a/src/plugins/presentation_util/public/components/controls/control_group/component/control_group_sortable_item.tsx b/src/plugins/presentation_util/public/components/controls/control_group/component/control_group_sortable_item.tsx index d72f7980fab8b..3ec553e5aa7d1 100644 --- a/src/plugins/presentation_util/public/components/controls/control_group/component/control_group_sortable_item.tsx +++ b/src/plugins/presentation_util/public/components/controls/control_group/component/control_group_sortable_item.tsx @@ -25,17 +25,19 @@ interface DragInfo { export type SortableControlProps = ControlFrameProps & { dragInfo: DragInfo; + isEditable: boolean; }; /** * A sortable wrapper around the generic control frame. */ export const SortableControl = (frameProps: SortableControlProps) => { - const { embeddableId } = frameProps; + const { embeddableId, isEditable } = frameProps; const { over, listeners, isSorting, transform, transition, attributes, isDragging, setNodeRef } = useSortable({ id: embeddableId, animateLayoutChanges: () => true, + disabled: !isEditable, }); frameProps.dragInfo = { ...frameProps.dragInfo, isOver: over?.id === embeddableId, isDragging }; @@ -58,7 +60,7 @@ export const SortableControl = (frameProps: SortableControlProps) => { const SortableControlInner = forwardRef< HTMLButtonElement, SortableControlProps & { style: HTMLAttributes['style'] } ->(({ embeddableId, dragInfo, style, ...dragHandleProps }, dragHandleRef) => { +>(({ embeddableId, dragInfo, style, isEditable, ...dragHandleProps }, dragHandleRef) => { const { isOver, isDragging, draggingIndex, index } = dragInfo; const { useEmbeddableSelector } = useReduxContainerContext(); const { panels } = useEmbeddableSelector((state) => state); @@ -85,9 +87,9 @@ const SortableControlInner = forwardRef< style={style} > ); diff --git a/src/plugins/presentation_util/public/components/controls/control_group/types.ts b/src/plugins/presentation_util/public/components/controls/control_group/types.ts index 438eee1c461dd..f6639b6a55bca 100644 --- a/src/plugins/presentation_util/public/components/controls/control_group/types.ts +++ b/src/plugins/presentation_util/public/components/controls/control_group/types.ts @@ -6,10 +6,12 @@ * Side Public License, v 1. */ -import { PanelState, EmbeddableInput } from '../../../../../embeddable/public'; +import { PanelState, EmbeddableInput, ViewMode } from '../../../../../embeddable/public'; import { InputControlInput } from '../../../services/controls'; import { ControlStyle, ControlWidth } from '../types'; +export { ViewMode }; + export interface ControlGroupInput extends EmbeddableInput, Omit { From 64f1dddcb05a4330145174c06df81b96b7cba8ca Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Wed, 20 Oct 2021 18:53:39 -0500 Subject: [PATCH 170/204] [feature branch] index pattern => data view for user facing content (#109821) * [index pattern management] index pattern => data view for user facing content (#109577) --- .../common/search/aggs/param_types/field.ts | 4 +- .../search/expressions/esaggs/esaggs_fn.ts | 2 +- .../data/common/search/expressions/esdsl.ts | 6 +- .../search_source/inspect/inspector_stats.ts | 12 +- .../common/data_views/data_views.ts | 8 +- .../common/expressions/load_index_pattern.ts | 8 +- src/plugins/data_views/common/lib/errors.ts | 2 +- .../server/deprecations/scripted_fields.ts | 8 +- .../server/saved_objects/data_views.ts | 1 + .../application/apps/doc/components/doc.tsx | 4 +- ...ver_index_pattern_management.test.tsx.snap | 6 +- .../sidebar/change_indexpattern.tsx | 4 +- .../components/sidebar/discover_field.tsx | 6 +- .../discover_index_pattern_management.tsx | 6 +- .../apps/main/utils/resolve_index_pattern.ts | 12 +- src/plugins/discover/server/ui_settings.ts | 8 +- .../advanced_params_content.tsx | 2 +- .../empty_index_list_prompt.test.tsx.snap | 4 +- .../empty_index_list_prompt.tsx | 4 +- .../empty_index_pattern_prompt.test.tsx.snap | 12 +- .../empty_index_pattern_prompt.tsx | 14 +- .../public/components/footer/footer.tsx | 2 +- .../components/form_fields/title_field.tsx | 14 +- .../components/form_fields/type_field.tsx | 4 +- .../public/components/form_schema.ts | 6 +- .../index_pattern_editor_flyout_content.tsx | 2 +- ...index_pattern_flyout_content_container.tsx | 4 +- .../rollup_beta_warning.tsx | 12 +- .../field_editor_flyout_content.tsx | 2 +- .../preview/field_list/field_list.tsx | 2 +- .../public/components/breadcrumbs.ts | 8 +- .../create_edit_field/create_edit_field.tsx | 13 +- .../edit_index_pattern/edit_index_pattern.tsx | 8 +- .../index_header/index_header.tsx | 26 +-- .../table/__snapshots__/table.test.tsx.snap | 2 +- .../components/table/table.tsx | 4 +- .../index_pattern_table.tsx | 16 +- .../mount_management_section.tsx | 4 +- .../index_pattern_management/public/plugin.ts | 4 +- .../index_pattern_select.tsx | 37 ++-- .../switch_mode_popover.tsx | 9 +- .../application/components/series_config.js | 4 +- .../use_index_patter_mode_callout.tsx | 12 +- .../components/vis_types/timeseries/config.js | 4 +- .../search_selection/search_selection.tsx | 4 +- .../_index_pattern_create_delete.js | 2 +- x-pack/plugins/apm/common/processor_event.ts | 2 +- .../public/application/application.test.tsx | 4 +- .../plugins/apm/public/application/index.tsx | 8 +- .../plugins/apm/public/application/uxApp.tsx | 8 +- .../app/RumDashboard/LocalUIFilters/index.tsx | 8 +- ...{use_index_pattern.ts => use_data_view.ts} | 26 +-- .../app/Settings/ApmIndices/index.tsx | 2 +- .../service_inventory.test.tsx | 6 +- .../service_overview.test.tsx | 6 +- .../components/shared/kuery_bar/index.tsx | 21 +- ...ex_pattern.ts => use_dynamic_data_view.ts} | 6 +- .../rest/{index_pattern.ts => data_view.ts} | 4 +- .../plugins/apm/public/utils/testHelpers.tsx | 1 - .../aggregate-latency-metrics/index.ts | 2 +- .../create_anomaly_detection_jobs.ts | 10 +- .../create_static_data_view.test.ts} | 32 ++-- .../create_static_data_view.ts} | 42 ++-- .../get_apm_data_view_title.test.ts} | 10 +- .../get_apm_data_view_title.ts} | 2 +- .../get_dynamic_data_view.ts} | 26 +-- .../server/lib/helpers/setup_request.test.ts | 4 +- .../lib/transactions/breakdown/index.test.ts | 1 - .../routes/{index_pattern.ts => data_view.ts} | 26 +-- .../get_global_apm_server_route_repository.ts | 4 +- x-pack/plugins/apm/server/tutorial/index.ts | 10 +- .../plugins/apm/server/utils/test_helpers.tsx | 1 - .../field_data_row/action_menu/actions.ts | 16 +- .../field_type_icon/field_type_icon.tsx | 2 +- .../results_links/results_links.tsx | 4 +- .../components/utils/kibana_field_format.ts | 2 +- .../components/import_errors/errors.tsx | 4 +- .../import_progress/import_progress.tsx | 24 +-- .../components/import_settings/advanced.tsx | 8 +- .../components/import_settings/simple.tsx | 4 +- .../import_summary/import_summary.tsx | 4 +- .../components/import_view/import_view.js | 10 +- .../full_time_range_selector.tsx | 2 +- .../index_data_visualizer_view.tsx | 16 +- .../index_pattern_management.tsx | 14 +- .../index_data_visualizer.tsx | 4 +- .../locator/locator.test.ts | 2 +- .../utils/saved_search_utils.test.ts | 2 +- .../data_visualizer/check_fields_exist.ts | 2 +- .../models/data_visualizer/data_visualizer.ts | 2 +- .../data_visualizer/server/routes/routes.ts | 44 ++--- .../schemas/index_data_visualizer_schemas.ts | 6 +- x-pack/plugins/event_log/README.md | 2 +- .../plugins/features/server/oss_features.ts | 4 +- .../components/import_complete_view.tsx | 2 +- .../components/json_upload_and_parse.tsx | 8 +- .../file_upload/public/validate_index_name.ts | 2 +- .../guidance_panel/guidance_panel.tsx | 6 +- .../graph/public/components/source_picker.tsx | 4 +- .../state_management/datasource.sagas.ts | 4 +- .../public/state_management/persistence.ts | 4 +- .../plugins/graph/server/sample_data/logs.ts | 2 +- .../editor_frame/config_panel/add_layer.tsx | 2 +- .../workspace_panel/workspace_panel.tsx | 12 +- .../editor_frame_service/error_helper.ts | 5 +- .../change_indexpattern.tsx | 4 +- .../indexpattern_datasource/datapanel.tsx | 18 +- .../dimensions_editor_helpers.tsx | 2 +- .../indexpattern_datasource/field_item.tsx | 8 +- .../indexpattern_datasource/indexpattern.tsx | 4 +- .../indexpattern_datasource/layerpanel.tsx | 4 +- .../no_fields_callout.test.tsx | 2 +- .../no_fields_callout.tsx | 2 +- .../definitions/last_value.test.tsx | 2 +- .../operations/definitions/last_value.tsx | 8 +- .../operations/layer_helpers.ts | 4 +- .../plugins/lens/server/routes/field_stats.ts | 2 +- x-pack/plugins/maps/common/i18n_getters.ts | 19 ++ .../layer_template.tsx | 11 +- .../layers/file_upload_wizard/config.tsx | 2 +- .../observability_layer_wizard.tsx | 2 +- .../security/index_pattern_select.tsx | 8 +- .../security/security_layer_wizard.tsx | 2 +- .../es_geo_grid_source/es_geo_grid_source.tsx | 6 +- .../update_source_editor.js | 9 +- .../es_geo_line_source/es_geo_line_source.tsx | 6 +- .../es_pew_pew_source/create_source_editor.js | 13 +- .../es_pew_pew_source/es_pew_pew_source.js | 6 +- .../es_pew_pew_source/update_source_editor.js | 9 +- .../es_search_source/es_search_source.tsx | 10 +- .../top_hits/update_source_editor.tsx | 8 +- .../es_search_source/update_source_editor.js | 8 +- .../util/load_index_settings.ts | 2 +- .../classes/sources/es_source/es_source.ts | 10 +- .../geo_index_pattern_select.test.tsx.snap | 16 +- .../components/geo_index_pattern_select.tsx | 19 +- .../join_editor/resources/join.tsx | 6 +- .../join_editor/resources/join_expression.tsx | 8 +- x-pack/plugins/maps/server/routes.js | 2 +- x-pack/plugins/ml/common/constants/locator.ts | 2 +- .../components/anomalies_table/links_menu.js | 6 +- .../components/data_grid/data_grid.tsx | 2 +- .../full_time_range_selector.test.tsx.snap | 4 +- .../full_time_range_selector.test.tsx | 2 +- .../full_time_range_selector.tsx | 4 +- .../data_frame_analytics/common/fields.ts | 4 +- .../common/use_results_view_config.ts | 15 +- .../configuration_step_form.tsx | 2 +- .../supported_fields_message.tsx | 8 +- .../create_analytics_advanced_editor.tsx | 13 +- .../details_step/details_step_form.tsx | 12 +- .../pages/analytics_creation/page.tsx | 4 +- .../index_pattern_prompt.tsx | 10 +- .../action_clone/clone_action_name.tsx | 18 +- .../action_delete/delete_action_modal.tsx | 11 +- .../action_delete/use_delete_action.tsx | 13 +- .../source_selection.test.tsx | 32 ++-- .../source_selection/source_selection.tsx | 22 +-- .../use_create_analytics_form/reducer.test.ts | 10 +- .../use_create_analytics_form/reducer.ts | 4 +- .../hooks/use_create_analytics_form/state.ts | 2 +- .../use_create_analytics_form.ts | 35 ++-- .../analytics_service/delete_analytics.ts | 15 +- .../pages/job_map/components/controls.tsx | 4 +- .../datavisualizer_selector.tsx | 10 +- .../index_based/data_loader/data_loader.ts | 2 +- .../explorer_chart_config_builder.js | 2 +- .../formatters/kibana_field_format.ts | 2 +- .../__snapshots__/editor.test.tsx.snap | 16 +- .../custom_url_editor/editor.test.tsx | 4 +- .../components/custom_url_editor/editor.tsx | 4 +- .../components/custom_url_editor/utils.js | 2 +- .../components/edit_job_flyout/edit_utils.js | 4 +- .../edit_job_flyout/tabs/custom_urls.tsx | 4 +- .../components/job_actions/management.js | 4 +- .../job_creator/categorization_job_creator.ts | 2 +- .../json_editor_flyout/json_editor_flyout.tsx | 2 +- .../components/time_field/description.tsx | 2 +- .../invalid_ccs_version_valid_callout.tsx | 2 +- .../new_job/pages/index_or_search/page.tsx | 10 +- .../jobs/new_job/pages/job_type/page.tsx | 20 +- .../jobs/new_job/pages/new_job/page.tsx | 6 +- .../new_job/pages/new_job/wizard_steps.tsx | 11 +- .../jobs/new_job/recognize/page.tsx | 6 +- .../jobs/new_job/utils/new_job_utils.test.ts | 2 +- .../services/field_format_service.ts | 14 +- .../services/ml_api_service/index.ts | 6 +- .../load_new_job_capabilities.ts | 6 +- .../ml/public/application/util/index_utils.ts | 16 +- .../anomaly_charts_embeddable.tsx | 7 +- .../ml/public/locator/ml_locator.test.ts | 2 +- .../models/data_recognizer/data_recognizer.ts | 12 +- .../models/data_visualizer/data_visualizer.ts | 14 +- .../ml/server/routes/data_frame_analytics.ts | 2 +- .../ml/server/routes/data_visualizer.ts | 24 +-- .../routes/schemas/data_visualizer_schema.ts | 6 +- .../common/utils/get_inspect_response.ts | 8 +- ...inment_alert_type_expression.test.tsx.snap | 6 +- .../expressions/boundary_index_expression.tsx | 2 +- .../expressions/entity_index_expression.tsx | 2 +- .../geo_index_pattern_select.test.tsx.snap | 12 +- .../geo_index_pattern_select.test.tsx | 2 +- .../geo_index_pattern_select.tsx | 10 +- .../util_components/single_field_select.tsx | 2 +- .../alert_types/geo_containment/readme.md | 14 +- .../geo_containment/validation.test.ts | 4 +- .../alert_types/geo_containment/validation.ts | 4 +- .../transform/common/api_schemas/common.ts | 2 +- x-pack/plugins/transform/common/constants.ts | 2 +- .../common/types/index_pattern.test.ts | 2 +- .../public/app/hooks/use_delete_transform.tsx | 20 +- .../app/hooks/use_search_items/common.ts | 2 +- .../use_search_items/use_search_items.ts | 2 +- .../clone_transform_section.tsx | 2 +- .../step_create/step_create_form.tsx | 27 ++- .../hooks/use_latest_function_config.ts | 2 +- .../step_define/step_define_form.test.tsx | 2 +- .../step_define/step_define_form.tsx | 4 +- .../step_define/step_define_summary.tsx | 4 +- .../step_details/step_details_form.tsx | 17 +- .../step_details/step_details_summary.tsx | 8 +- .../step_details/step_details_time_field.tsx | 6 +- .../action_clone/use_clone_action.tsx | 12 +- .../action_delete/delete_action_modal.tsx | 8 +- .../discover_action_name.test.tsx | 8 +- .../action_discover/discover_action_name.tsx | 4 +- .../action_edit/use_edit_action.tsx | 12 +- .../edit_transform_flyout_form.tsx | 2 +- .../search_selection/search_selection.tsx | 4 +- .../transform/server/routes/api/transforms.ts | 2 +- .../translations/translations/ja-JP.json | 176 +---------------- .../translations/translations/zh-CN.json | 179 +----------------- .../public/common/index_controls/index.ts | 2 +- .../apis/ml/modules/setup_module.ts | 6 +- .../classification_creation.ts | 2 +- .../outlier_detection_creation.ts | 2 +- .../regression_creation.ts | 2 +- .../data_visualizer/file_data_visualizer.ts | 2 +- .../data_visualizer/index_data_visualizer.ts | 8 +- ...ata_visualizer_index_pattern_management.ts | 4 +- .../apps/ml/permissions/full_ml_access.ts | 2 +- .../apps/ml/permissions/read_ml_access.ts | 2 +- .../test/functional/apps/transform/cloning.ts | 4 +- .../apps/transform/creation_index_pattern.ts | 2 +- .../transform/creation_runtime_mappings.ts | 2 +- .../apps/transform/creation_saved_search.ts | 2 +- .../ml/data_frame_analytics_creation.ts | 2 +- .../functional/services/transform/wizard.ts | 2 +- .../event_log/public_api_integration.ts | 2 +- 249 files changed, 879 insertions(+), 1305 deletions(-) rename x-pack/plugins/apm/public/components/app/RumDashboard/LocalUIFilters/{use_index_pattern.ts => use_data_view.ts} (50%) rename x-pack/plugins/apm/public/hooks/{use_dynamic_index_pattern.ts => use_dynamic_data_view.ts} (74%) rename x-pack/plugins/apm/public/services/rest/{index_pattern.ts => data_view.ts} (76%) rename x-pack/plugins/apm/server/lib/{index_pattern/create_static_index_pattern.test.ts => data_view/create_static_data_view.test.ts} (77%) rename x-pack/plugins/apm/server/lib/{index_pattern/create_static_index_pattern.ts => data_view/create_static_data_view.ts} (62%) rename x-pack/plugins/apm/server/lib/{index_pattern/get_apm_index_pattern_title.test.ts => data_view/get_apm_data_view_title.test.ts} (73%) rename x-pack/plugins/apm/server/lib/{index_pattern/get_apm_index_pattern_title.ts => data_view/get_apm_data_view_title.ts} (86%) rename x-pack/plugins/apm/server/lib/{index_pattern/get_dynamic_index_pattern.ts => data_view/get_dynamic_data_view.ts} (65%) rename x-pack/plugins/apm/server/routes/{index_pattern.ts => data_view.ts} (61%) diff --git a/src/plugins/data/common/search/aggs/param_types/field.ts b/src/plugins/data/common/search/aggs/param_types/field.ts index 05e1302475d04..dd9f91ceea223 100644 --- a/src/plugins/data/common/search/aggs/param_types/field.ts +++ b/src/plugins/data/common/search/aggs/param_types/field.ts @@ -56,7 +56,7 @@ export class FieldParamType extends BaseParamType { 'data.search.aggs.paramTypes.field.notFoundSavedFieldParameterErrorMessage', { defaultMessage: - 'The field "{fieldParameter}" associated with this object no longer exists in the index pattern. Please use another field.', + 'The field "{fieldParameter}" associated with this object no longer exists in the data view. Please use another field.', values: { fieldParameter: field.name, }, @@ -75,7 +75,7 @@ export class FieldParamType extends BaseParamType { 'data.search.aggs.paramTypes.field.invalidSavedFieldParameterErrorMessage', { defaultMessage: - 'Saved field "{fieldParameter}" of index pattern "{indexPatternTitle}" is invalid for use with the "{aggType}" aggregation. Please select a new field.', + 'Saved field "{fieldParameter}" of data view "{indexPatternTitle}" is invalid for use with the "{aggType}" aggregation. Please select a new field.', values: { fieldParameter: field.name, aggType: aggConfig?.type?.title, diff --git a/src/plugins/data/common/search/expressions/esaggs/esaggs_fn.ts b/src/plugins/data/common/search/expressions/esaggs/esaggs_fn.ts index 09d68177c3ec3..792b28408c5ff 100644 --- a/src/plugins/data/common/search/expressions/esaggs/esaggs_fn.ts +++ b/src/plugins/data/common/search/expressions/esaggs/esaggs_fn.ts @@ -62,7 +62,7 @@ export const getEsaggsMeta: () => Omit types: ['index_pattern'], required: true, help: i18n.translate('data.search.functions.esaggs.index.help', { - defaultMessage: 'Index pattern retrieved with indexPatternLoad', + defaultMessage: 'Data view retrieved with indexPatternLoad', }), }, aggs: { diff --git a/src/plugins/data/common/search/expressions/esdsl.ts b/src/plugins/data/common/search/expressions/esdsl.ts index c5b116073c06b..faa43dab65657 100644 --- a/src/plugins/data/common/search/expressions/esdsl.ts +++ b/src/plugins/data/common/search/expressions/esdsl.ts @@ -115,12 +115,12 @@ export const getEsdslFn = ({ request.stats({ indexPattern: { - label: i18n.translate('data.search.es_search.indexPatternLabel', { - defaultMessage: 'Index pattern', + label: i18n.translate('data.search.es_search.dataViewLabel', { + defaultMessage: 'Data view', }), value: args.index, description: i18n.translate('data.search.es_search.indexPatternDescription', { - defaultMessage: 'The index pattern that connected to the Elasticsearch indices.', + defaultMessage: 'The data view that connected to the Elasticsearch indices.', }), }, }); diff --git a/src/plugins/data/common/search/search_source/inspect/inspector_stats.ts b/src/plugins/data/common/search/search_source/inspect/inspector_stats.ts index e5a3acc23eee8..67c23fb16b8de 100644 --- a/src/plugins/data/common/search/search_source/inspect/inspector_stats.ts +++ b/src/plugins/data/common/search/search_source/inspect/inspector_stats.ts @@ -25,17 +25,17 @@ export function getRequestInspectorStats(searchSource: ISearchSource) { if (index) { stats.indexPattern = { - label: i18n.translate('data.search.searchSource.indexPatternLabel', { - defaultMessage: 'Index pattern', + label: i18n.translate('data.search.searchSource.dataViewLabel', { + defaultMessage: 'Data view', }), value: index.title, - description: i18n.translate('data.search.searchSource.indexPatternDescription', { - defaultMessage: 'The index pattern that connected to the Elasticsearch indices.', + description: i18n.translate('data.search.searchSource.dataViewDescription', { + defaultMessage: 'The data view that was queried.', }), }; stats.indexPatternId = { - label: i18n.translate('data.search.searchSource.indexPatternIdLabel', { - defaultMessage: 'Index pattern ID', + label: i18n.translate('data.search.searchSource.dataViewIdLabel', { + defaultMessage: 'Data view ID', }), value: index.id!, description: i18n.translate('data.search.searchSource.indexPatternIdDescription', { diff --git a/src/plugins/data_views/common/data_views/data_views.ts b/src/plugins/data_views/common/data_views/data_views.ts index a76b531668162..e5c83b28dee96 100644 --- a/src/plugins/data_views/common/data_views/data_views.ts +++ b/src/plugins/data_views/common/data_views/data_views.ts @@ -295,7 +295,7 @@ export class DataViewsService { this.onError(err, { title: i18n.translate('dataViews.fetchFieldErrorTitle', { - defaultMessage: 'Error fetching fields for index pattern {title} (ID: {id})', + defaultMessage: 'Error fetching fields for data view {title} (ID: {id})', values: { id: indexPattern.id, title: indexPattern.title }, }), }); @@ -341,7 +341,7 @@ export class DataViewsService { this.onError(err, { title: i18n.translate('dataViews.fetchFieldErrorTitle', { - defaultMessage: 'Error fetching fields for index pattern {title} (ID: {id})', + defaultMessage: 'Error fetching fields for data view {title} (ID: {id})', values: { id, title }, }), }); @@ -483,7 +483,7 @@ export class DataViewsService { } else { this.onError(err, { title: i18n.translate('dataViews.fetchFieldErrorTitle', { - defaultMessage: 'Error fetching fields for index pattern {title} (ID: {id})', + defaultMessage: 'Error fetching fields for data view {title} (ID: {id})', values: { id: savedObject.id, title }, }), }); @@ -654,7 +654,7 @@ export class DataViewsService { } const title = i18n.translate('dataViews.unableWriteLabel', { defaultMessage: - 'Unable to write index pattern! Refresh the page to get the most up to date changes for this index pattern.', + 'Unable to write data view! Refresh the page to get the most up to date changes for this data view.', }); this.onNotification({ title, color: 'danger' }); diff --git a/src/plugins/data_views/common/expressions/load_index_pattern.ts b/src/plugins/data_views/common/expressions/load_index_pattern.ts index ca0c1090ceea8..3c923597a5063 100644 --- a/src/plugins/data_views/common/expressions/load_index_pattern.ts +++ b/src/plugins/data_views/common/expressions/load_index_pattern.ts @@ -46,15 +46,15 @@ export const getIndexPatternLoadMeta = (): Omit< name, type, inputTypes: ['null'], - help: i18n.translate('dataViews.indexPatternLoad.help', { - defaultMessage: 'Loads an index pattern', + help: i18n.translate('dataViews.functions.dataViewLoad.help', { + defaultMessage: 'Loads a data view', }), args: { id: { types: ['string'], required: true, - help: i18n.translate('dataViews.functions.indexPatternLoad.id.help', { - defaultMessage: 'index pattern id to load', + help: i18n.translate('dataViews.functions.dataViewLoad.id.help', { + defaultMessage: 'data view id to load', }), }, }, diff --git a/src/plugins/data_views/common/lib/errors.ts b/src/plugins/data_views/common/lib/errors.ts index f8422a6e5dd0d..c75ad7fe29930 100644 --- a/src/plugins/data_views/common/lib/errors.ts +++ b/src/plugins/data_views/common/lib/errors.ts @@ -15,7 +15,7 @@ import { KbnError } from '../../../kibana_utils/common'; */ export class DataViewMissingIndices extends KbnError { constructor(message: string) { - const defaultMessage = "IndexPattern's configured pattern does not match any indices"; + const defaultMessage = "Data view's title does not match any indices"; super( message && message.length ? `No matching indices found: ${message}` : defaultMessage diff --git a/src/plugins/data_views/server/deprecations/scripted_fields.ts b/src/plugins/data_views/server/deprecations/scripted_fields.ts index 9ee2d64e25cb5..684115a19299a 100644 --- a/src/plugins/data_views/server/deprecations/scripted_fields.ts +++ b/src/plugins/data_views/server/deprecations/scripted_fields.ts @@ -42,10 +42,10 @@ export const createScriptedFieldsDeprecationsConfig: ( return [ { title: i18n.translate('dataViews.deprecations.scriptedFieldsTitle', { - defaultMessage: 'Found index patterns using scripted fields', + defaultMessage: 'Found data views using scripted fields', }), message: i18n.translate('dataViews.deprecations.scriptedFieldsMessage', { - defaultMessage: `You have {numberOfIndexPatternsWithScriptedFields} index patterns ({titlesPreview}...) that use scripted fields. Scripted fields are deprecated and will be removed in future. Use runtime fields instead.`, + defaultMessage: `You have {numberOfIndexPatternsWithScriptedFields} data views ({titlesPreview}...) that use scripted fields. Scripted fields are deprecated and will be removed in future. Use runtime fields instead.`, values: { titlesPreview: indexPatternTitles.slice(0, PREVIEW_LIMIT).join('; '), numberOfIndexPatternsWithScriptedFields: indexPatternsWithScriptedFields.length, @@ -57,11 +57,11 @@ export const createScriptedFieldsDeprecationsConfig: ( correctiveActions: { manualSteps: [ i18n.translate('dataViews.deprecations.scriptedFields.manualStepOneMessage', { - defaultMessage: 'Navigate to Stack Management > Kibana > Index Patterns.', + defaultMessage: 'Navigate to Stack Management > Kibana > Data Views.', }), i18n.translate('dataViews.deprecations.scriptedFields.manualStepTwoMessage', { defaultMessage: - 'Update {numberOfIndexPatternsWithScriptedFields} index patterns that have scripted fields to use runtime fields instead. In most cases, to migrate existing scripts, you will need to change "return ;" to "emit();". Index patterns with at least one scripted field: {allTitles}', + 'Update {numberOfIndexPatternsWithScriptedFields} data views that have scripted fields to use runtime fields instead. In most cases, to migrate existing scripts, you will need to change "return ;" to "emit();". Data views with at least one scripted field: {allTitles}', values: { allTitles: indexPatternTitles.join('; '), numberOfIndexPatternsWithScriptedFields: indexPatternsWithScriptedFields.length, diff --git a/src/plugins/data_views/server/saved_objects/data_views.ts b/src/plugins/data_views/server/saved_objects/data_views.ts index d340732873235..68443609f3fc6 100644 --- a/src/plugins/data_views/server/saved_objects/data_views.ts +++ b/src/plugins/data_views/server/saved_objects/data_views.ts @@ -15,6 +15,7 @@ export const dataViewSavedObjectType: SavedObjectsType = { hidden: false, namespaceType: 'single', management: { + displayName: 'Data view', icon: 'indexPatternApp', defaultSearchField: 'title', importableAndExportable: true, diff --git a/src/plugins/discover/public/application/apps/doc/components/doc.tsx b/src/plugins/discover/public/application/apps/doc/components/doc.tsx index c6cfad3953e95..7ccf77d2a29d4 100644 --- a/src/plugins/discover/public/application/apps/doc/components/doc.tsx +++ b/src/plugins/discover/public/application/apps/doc/components/doc.tsx @@ -48,8 +48,8 @@ export function Doc(props: DocProps) { iconType="alert" title={ } diff --git a/src/plugins/discover/public/application/apps/main/components/sidebar/__snapshots__/discover_index_pattern_management.test.tsx.snap b/src/plugins/discover/public/application/apps/main/components/sidebar/__snapshots__/discover_index_pattern_management.test.tsx.snap index 02e2879476a5e..6cb6a15aa0f66 100644 --- a/src/plugins/discover/public/application/apps/main/components/sidebar/__snapshots__/discover_index_pattern_management.test.tsx.snap +++ b/src/plugins/discover/public/application/apps/main/components/sidebar/__snapshots__/discover_index_pattern_management.test.tsx.snap @@ -671,7 +671,7 @@ exports[`Discover IndexPattern Management renders correctly 1`] = ` anchorPosition="downCenter" button={